gRPC 框架笔记1
gRPC是什么
gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。
proto文件
用于定义gRPC服务和消息的协定;服务端和客户端共享proto文件。
服务端:
使用Core 3.0的gRPC模板生成服务端,关键点是proto文件
使用模板生成会自动添加依赖项
Grpc.AspNetCore
proto文件:
syntax = "proto3"; option csharp_namespace = "GrpcGreeter"; package greet; // The greeting service definition. service Greeter { //自定义 rpc GetName (HelloRequest) returns (HelloReply); // 通过一元方式传输 rpc SayHello (HelloRequest) returns (HelloReply); // 通过客户端流的方式传输 rpc SayHelloClientStream (stream HelloRequest) returns (HelloReply); //通过服务端流的方式传输 rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply); //通过双向流的方式传输 rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply); } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; }
注意:完成proto文件后,需要先进行项目生成,系统会自动生成所有gRPC存根
然后创建相关服务 GreeterService
public class GreeterService : Greeter.GreeterBase { private readonly ILogger<GreeterService> _logger; public GreeterService(ILogger<GreeterService> logger) { _logger = logger; } public override Task<HelloReply> GetName(HelloRequest request, ServerCallContext context) { return Task.FromResult(new HelloReply { Message = "name:" + request.Name }); } //一元方式 public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { //Thread.Sleep(5000); return Task.FromResult(new HelloReply { Message = "回复: " + new Random().Next().ToString() + request.Name }); } //客户端流 public override async Task<HelloReply> SayHelloClientStream(IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context) { string msg = string.Empty; //var current = new HelloRequest(); while (await requestStream.MoveNext()) { msg += requestStream.Current.Name; } var task = new Task<HelloReply>(() => { var reply = new HelloReply() { Message = "回复:" + msg//current.Name }; return reply; }); task.Start(); var result = await task; return result; } //服务端流 public override async Task SayHelloServerStream(HelloRequest request, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context) { for (int i = 0; i < 3; i++) { await responseStream.WriteAsync(new HelloReply() { Message = "回复:" + request.Name + i.ToString() }); } } //双向流 public override async Task SayHelloStream(IAsyncStreamReader<HelloRequest> requestStream, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context) { while (await requestStream.MoveNext()) { await responseStream.WriteAsync(new HelloReply() { Message = "回复:" + requestStream.Current.Name }); } } }
配置上,如果是模板生成,会自动添加
services.AddGrpc();
app.UseEndpoints(endpoints => { endpoints.MapGrpcService<GreeterService>(); ...... });
客户端:
依赖项:
Google.Protobuf
Grpc.Net.ClientFactory
Grpc.Tools
先引用服务端的proto文件
客户端代码:
class Program { static async Task Main(string[] args) { //http1方式要加下面这个段,不然有异常 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);//使用HTTP方式 //服务端地址 using var channel = GrpcChannel.ForAddress("http://localhost:50051"); var client = new Greeter.GreeterClient(channel); var reply = await client.SayHelloAsync( new HelloRequest { Name = "GreeterClient123" }); var reply1 = await client.GetNameAsync(new HelloRequest { Name = "我是谁?" }); Console.WriteLine(); Console.WriteLine("Greeting: " + reply.Message); Console.WriteLine(); Console.WriteLine("回复:" + reply1.Message); Console.WriteLine(); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); await run1(); } static async Task run1() { ////http1方式要加下面这个段,不然有异常 //AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress("http://localhost:50051"); var helloClient = new Greeter.GreeterClient(channel); Console.WriteLine("一元调用开始-------"); //一元调用(同步方法) Console.WriteLine("发送:一元同步调用"); var reply = helloClient.SayHello(new HelloRequest { Name = "一元同步调用" }); Console.WriteLine($"{reply.Message}"); //一元调用(异步方法) Console.WriteLine("发送:一元异步调用"); var reply2 = helloClient.SayHelloAsync(new HelloRequest { Name = "一元异步调用" }).GetAwaiter().GetResult(); Console.WriteLine($"{reply2.Message}"); Console.WriteLine("一元调用结束-------"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("服务端流开始-------"); //服务端流 Console.WriteLine("发送:服务端流"); var reply3 = helloClient.SayHelloServerStream(new HelloRequest { Name = "服务端流" }); while (await reply3.ResponseStream.MoveNext()) { Console.WriteLine(reply3.ResponseStream.Current.Message); } Console.WriteLine("服务端流结束-------"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("客户端流开始-------"); //客户端流 using (var call = helloClient.SayHelloClientStream()) { for (var i = 0; i < 3; i++) { Console.WriteLine("发送:" + "客户端流" + i.ToString()); await call.RequestStream.WriteAsync(new HelloRequest { Name = "客户端流" + i.ToString() }); } await call.RequestStream.CompleteAsync(); var reply4 = await call; Console.WriteLine($"{reply4.Message}"); } Console.WriteLine("客户端流结束-------"); Console.WriteLine(); Console.WriteLine(); Console.WriteLine("双向流开始-------"); //双向流 using (var call = helloClient.SayHelloStream()) { //Console.WriteLine("启动后台任务以接收消息:输入quit退出"); //接收 var readTask = Task.Run(async () => { await foreach (var response in call.ResponseStream.ReadAllAsync()) { Console.WriteLine(response.Message); } }); ////连续发送 //Console.WriteLine("启动后台任务以接收消息"); //for (var i = 0; i < 3; i++) //{ // Thread.Sleep(1000);//等待1秒,等服务器回复 // Console.WriteLine("发送:" + "双向流" + i.ToString()); // await call.RequestStream.WriteAsync(new HelloRequest { Name = "双向流" + i.ToString() }); //} //手动输入发送 Console.WriteLine("启动后台任务以接收消息:输入quit退出"); while (true) { string input = Console.ReadLine(); if (input == "quit") { break; } await call.RequestStream.WriteAsync(new HelloRequest { Name = input }); } await call.RequestStream.CompleteAsync(); await readTask; } Console.WriteLine("双向流结束-------"); Console.ReadKey(); } }