目录
本篇文章主要讲解如下内容
一。grpc在 .Net Core 3.x 中的应用
二。grpc如何分段上传图片
代码与实现过程比较简单,大家可以无限延伸对 grpc 的使用
环境部署
Server
(一) 创建服务器端 GrpcServer.Web
(二) 项目结构如下
(三) Startup.cs 内容如下
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddGrpc(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseStaticFiles(); app.UseHttpsRedirection(); app.UseEndpoints(endpoints => { endpoints.MapGrpcService<MyEmployeeService>(); }); } }
(四) 安装 Grpc.AspNetCore
launchSettings.json
{ "profiles": { "GrpcServer.Web": { "commandName": "Project", "launchBrowser": true, "applicationUrl": "https://localhost:5001", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
Client
(一) 创建 GrpcClient 控制台应用程序
(二) 安装相关 Grpc 客户端的包
Grpc 建立普通通信方式
Server
在 Proto 文件夹下添加 Message.proto 文件
添加内容
syntax = "proto3"; option csharp_namespace = "GrpcServer.Web.Protos"; message Employee { int32 id = 1; int32 no = 2; string firstName = 3; string lasterName = 4; float salary = 5; } message GetByNoRequest{ int32 no = 1; } message EmployeeResponse{ Employee employee =1; } message GetAllRequest {} message AddPhotoRequest{ bytes data = 1; } message AddPhotoResponse{ bool isOk = 1; } message EmployeeRequest{ Employee employee = 1; } service EmployeeService{ rpc GetByNo(GetByNoRequest) returns (EmployeeResponse); rpc GetAll(GetAllRequest) returns (stream EmployeeResponse); rpc AddPhoto(stream AddPhotoRequest) returns (AddPhotoResponse); rpc Save(EmployeeRequest) returns (EmployeeResponse); rpc SaveAll(stream EmployeeRequest) returns (stream EmployeeResponse); }
并选中 Message.proto 修改为如下属性
在 Data 文件夹下添加 InMemoryData.cs
public class InMemoryData { public static List<Employee> Employees = new List<Employee>() { new Employee{ Id = 1, No = 1994, FirstName = "Chandler", LasterName = "Bing", Salary = 2200 }, new Employee{ Id = 2, No = 1999, FirstName = "Rachl", LasterName = "Green", Salary = 2400 }, new Employee{ Id = 3, No = 2452, FirstName = "MI", LasterName = "UIMI", Salary = 2600 } }; }
在 Servcies 文件夹下添加 MyEmployeeService.cs
public class MyEmployeeService: EmployeeService.EmployeeServiceBase { private readonly ILogger<MyEmployeeService> _logger; private readonly IHostingEnvironment _hostingEnvironment; public MyEmployeeService(ILogger<MyEmployeeService> logger, IHostingEnvironment hostingEnvironment) { _logger = logger; _hostingEnvironment = hostingEnvironment; } public async override Task<EmployeeResponse> GetByNo(GetByNoRequest request, ServerCallContext context) { //获取 Metadata = context.RequestHeaders var md = context.RequestHeaders; foreach (var item in md) { _logger.LogInformation($"{ item.Key } : { item.Value }"); } var employee = InMemoryData.Employees .SingleOrDefault(x => x.No == request.No); if (employee!=null) { var response = new EmployeeResponse { Employee = employee }; return await Task.FromResult(response); } throw new System.Exception($"Employee not found with no: {request.No}"); } public override async Task GetAll(GetAllRequest request, IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context) { foreach (var employee in InMemoryData.Employees) { await responseStream.WriteAsync(new EmployeeResponse { Employee = employee }); } } }
Client
将服务器上的 Message.proto 文件复制到 Protos 文件夹下面
并修改其属性如下
修改 Program.cs
static async Task Main(string[] args) { // using var channel = GrpcChannel.ForAddress("https://localhost:5001"); //var client = new var client = new EmployeeService.EmployeeServiceClient(channel); //await GetPhotoAsync(client); var option = int.Parse(args[0]); switch (option) { case 1: await GetByNoAsync(client); break; case 2: await GetAllAsync(client); break; default: break; } Console.ReadKey(); } private static async Task GetAllAsync(EmployeeService.EmployeeServiceClient client) { using var call = client.GetAll(new GetAllRequest()); var responseStream = call.ResponseStream; while (await responseStream.MoveNext()) { Console.WriteLine(responseStream.Current.Employee); } } private static async Task GetByNoAsync( EmployeeService.EmployeeServiceClient client) { //定义元数据 var md = new Metadata { { "username","administrator" }, { "role","administrator" } }; var response = await client.GetByNoAsync(new GetByNoRequest { No = 1994 }, md); Console.WriteLine($"Response messages:{response}"); Console.WriteLine("Press any key to exit."); }
《 Test 》
先启动服务器端,然后再在控制台中启动客户端
通过客户端分段上传图片到服务器端
Server
实现 AddPhoto 方法
public async override Task<AddPhotoResponse> AddPhoto(IAsyncStreamReader<AddPhotoRequest> requestStream, ServerCallContext context) { //获取 Metadata Metadata md = context.RequestHeaders; string FileName = ""; foreach (var item in md) { Console.WriteLine($"{item.Key}: {item.Value}"); if (item.Key== "filename") { FileName = item.Value; } } //获取 var data = new List<byte>(); //获取流 while (await requestStream.MoveNext()) { Console.WriteLine($"Received:{requestStream.Current.Data.Length} bytes "); data.AddRange(requestStream.Current.Data); } string fullnamepath = _hostingEnvironment.WebRootPath +"\\"+ DateTime.Now.ToString("yyyyMMddhhmmss") + FileName; using var file = new MemoryStream(data.ToArray()); using (FileStream filestream = new FileStream(fullnamepath, FileMode.Create)) { await file.CopyToAsync(filestream); filestream.Flush(); } Console.WriteLine($"Received file with {data.Count} bytes"); //返回流结果 return new AddPhotoResponse { IsOk = true }; }
Client
修改 Program.cs
。。。 case 3: await GetPhotoAsync(client); break; 。。。 private static async Task GetPhotoAsync(EmployeeService.EmployeeServiceClient client) { FileStream fs = File.OpenRead("logo.png"); //定义元数据 var md = new Metadata { { "username","administrator" }, { "role","administrator" }, { "filename", fs.Name.Substring(fs.Name.LastIndexOf('\\')+1) } }; using var call = client.AddPhoto(md); //向 server 端发起请求 var stream = call.RequestStream; //然后进行分段上传图片 while (true) { byte[] buffer = new byte[1024]; int numRead = await fs.ReadAsync(buffer, offset: 0, count: buffer.Length); //读取读满了退出 if (numRead == 0) { break; } //读取长度不一致将变为一致 if (numRead < buffer.Length) { Array.Resize(ref buffer, numRead); } //向服务端写数据 await stream.WriteAsync(new AddPhotoRequest() { Data = ByteString.CopyFrom(buffer) }); } //告诉 server 我的数据都传完了 await stream.CompleteAsync(); //获取上传结果 var res = await call.ResponseAsync; Console.WriteLine(res.IsOk); }
《 Test 》
扩展资料
如果你想进一步了解 gRPC,请查阅 ASP.NET Core gRPC 文档。请向我们提出你对 gRPC-Web 的意见和经验反馈,因为这将帮助我们选择如何以及是否在未来的 ASP.NET Core 版本中使 gRPC-Web 成 一个标准特性。你可以在这里发布评论,或者在 GitHub 上发布标题中带有“反馈”的 issue。
翻译自原文:https://blog.stevensanderson.com/2020/01/15/2020-01-15-grpc-web-in-blazor-webassembly
相关文章:
近期,观测分析平台 SkyWalking 的 .NET 自动探针 (SkyAPM-dotnet) 也已经支持了 grpc-dotnet 远程调用的链路跟踪采集,欢迎大家使用!如果喜欢,也请大家给点个星星!
项目地址:https://github.com/SkyAPM/SkyAPM-dotnet
欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739