
目录
本篇文章主要讲解如下内容
一。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

