为什么要用Grpc
跨语言进行,调用服务,获取跨服务器调用等 目前我的需要使用 我的抓取端是go 写的 查询端用 Net6 写的 导致很多时候 我需要把一些临时数据写入到 Redis 在两个服务器进行处理
参考地址:
哔哩哔哩杨旭大佬:https://www.bilibili.com/video/BV1eE411T7GC/?spm_id_from=333.999.0.0强烈建议去看一遍杨旭大佬讲的这篇Grpc 没有那么繁琐 清晰明了
1、依赖安装:
Grpc.AspNetCore Server 端包
2、编写proto 文件
syntax = "proto3"; option csharp_namespace= "GrpcServer.Web.Protos"; message Employee{ int32 id = 1; int32 no = 2; string firstName = 3; string lastName =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); }
3、编写Server端代码
在编写服务端代码之前,我们需要对proto 文件进行生成代码
如上图:1)我们要先进行勾选Build Action 选择,如图所示, 因为我们这里需要的只是 Server 端,选择生成Server Only 2) 选择这两个之后 我们需要重新生成一下项目
EmployeeService.EmployeeServiceBase 是我们Proto 生成的代码,我们要去重写父类的这些方法
public class MyEmployeeService : EmployeeService.EmployeeServiceBase { private readonly ILogger<MyEmployeeService> _logger; public MyEmployeeService(ILogger<MyEmployeeService> logger) { _logger = logger; } public override async Task<EmployeeResponse> GetByNo(GetByNoRequest request, ServerCallContext context) { 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 Exception($"employee is not found :{request.No}"); } public override async Task GetAll(GetAllRequest request, IServerStreamWriter<EmployeeResponse> responseStream, ServerCallContext context) { foreach (var item in InMemoryData.Employees) { await responseStream.WriteAsync(new EmployeeResponse { Employee = item }); } //return base.GetAll(request, responseStream, context); } public override async Task<AddPhotoResponse> AddPhoto( IAsyncStreamReader<AddPhotoRequest> requestStream , ServerCallContext context) { Metadata md = context.RequestHeaders; foreach (var item in md) { Console.WriteLine($"{item.Key}:{item.Value}"); } var data = new List<byte>(); while (await requestStream.MoveNext()) { Console.WriteLine($"Received: {requestStream.Current.Data.Length}"); data.AddRange(requestStream.Current.Data); } Console.WriteLine($"data file with {data.Count} bytes"); return new AddPhotoResponse { IsOK = true }; } public override async Task SaveAll( IAsyncStreamReader<EmployeeRequest> requestStream , IServerStreamWriter<EmployeeResponse> responseStream , ServerCallContext context) { while (await requestStream.MoveNext()) { var employee = requestStream.Current.Employee; lock (this) { InMemoryData.Employees.Add(employee); } await responseStream.WriteAsync(new EmployeeResponse { Employee = employee }); } Console.WriteLine("Employees"); foreach (var item in InMemoryData.Employees) { Console.WriteLine(item); } //return base.SaveAll(requestStream, responseStream, context); } }
4、编写Client客户端代码
我们把服务端的Proto 文件复制一份到Client 端代码 生成一份Client 代码如下图
需要注意
在编写客户端代码的时候,我遇到了一个错误,提示证书验证失败!,(没有配置证书) 所以我们要加上不对证书进行验证 使用 GrpcChannelOptions() 对证书验证
using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions() { HttpClient = null, HttpHandler = new HttpClientHandler { //方法一 ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator //方法二 //ServerCertificateCustomValidationCallback = (a, b, c, d) => true } });
Demo 地址:https://gitee.com/Lovely_Rabbit/basic-principle-exercise
标签:
留言评论