<三>GRPC双向流
直接使用晓晨Master大佬的给猫洗澡的例子。原文链接:ASP.NET Core 3.0 gRPC 双向流
1、定义 SayHi.ptoto文件添加两个方法,一个计算猫数量,一个给猫洗澡。client端和服务端都要
syntax = "proto3"; option csharp_namespace = "GrpcClient"; import "google/protobuf/empty.proto"; package SayHi; //定义服务 service SayHier{ //定义 rpc SayHillo(SayHiRequest) returns(SayHiResult); rpc BathTheCat(stream BathTheCatReq) returns ( stream BathTheCatResp); rpc Count(google.protobuf.Empty) returns (CountCatResult); } message SayHiRequest { string name=1; } message SayHiResult{ string message=1; } message BathTheCatReq{ int32 id=1; } message BathTheCatResp{ string message=1; } message CountCatResult{ int32 Count=1; }
2、实现服务
public class SayHierService : SayHier.SayHierBase { private static readonly List<string> Cats = new List<string>() { "英短银渐层", "英短金渐层", "美短", "蓝猫", "狸花猫", "橘猫" }; private static readonly Random Rand = new Random(DateTime.Now.Millisecond); public override Task<SayHiResult> SayHillo(SayHiRequest request, ServerCallContext context) { return Task.FromResult(new SayHiResult { Message = "Hillo " + request.Name }); } public override async Task BathTheCat(IAsyncStreamReader<BathTheCatReq> requestStream, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context) { var bathQueue = new Queue<int>(); while (await requestStream.MoveNext()) { //将要洗澡的猫加入队列 bathQueue.Enqueue(requestStream.Current.Id); // _logger.LogInformation($"Cat {requestStream.Current.Id} Enqueue."); } //遍历队列开始洗澡 while (bathQueue.TryDequeue(out var catId)) { await responseStream.WriteAsync(new BathTheCatResp() { Message = $"铲屎的成功给一只{Cats[catId]}洗了澡!" }); await Task.Delay(500);//此处主要是为了方便客户端能看出流调用的效果 } } public override Task<CountCatResult> Count(Empty request, ServerCallContext context) { return Task.FromResult(new CountCatResult() { Count = Cats.Count }); } }
3、实现客户端调用服务
var channel = GrpcChannel.ForAddress("https://localhost:5001"); var catClient = new SayHier.SayHierClient(channel); //获取猫总数 var catCount = await catClient.CountAsync(new Empty()); Console.WriteLine($"一共{catCount.Count}只猫。"); var rand = new Random(DateTime.Now.Millisecond); var bathCat = catClient.BathTheCat(); //定义接收吸猫响应逻辑 var bathCatRespTask = Task.Run(async () => { while (await bathCat.ResponseStream.MoveNext(CancellationToken.None)) { Console.WriteLine(bathCat.ResponseStream.Current.Message); } }); //随机给10个猫洗澡 for (int i = 0; i < 10; i++) { await bathCat.RequestStream.WriteAsync(new BathTheCatReq() { Id = rand.Next(0, catCount.Count) }); } //发送完毕 await bathCat.RequestStream.CompleteAsync(); Console.WriteLine("客户端已发送完10个需要洗澡的猫id"); Console.WriteLine("接收洗澡结果:"); //开始接收响应 await bathCatRespTask; Console.WriteLine("洗澡完毕");
4、运行
5、限流,在客户端接收MoveNext函数里传一个CancellationToken,具体代码如下:
var channel = GrpcChannel.ForAddress("https://localhost:5001"); var catClient = new SayHier.SayHierClient(channel); //获取猫总数 var catCount = await catClient.CountAsync(new Empty()); Console.WriteLine($"一共{catCount.Count}只猫。"); var rand = new Random(DateTime.Now.Millisecond); var cts = new CancellationTokenSource(); //指定在2.5s后进行取消操作 cts.CancelAfter(TimeSpan.FromSeconds(2.5)); var bathCat = catClient.BathTheCat(); //定义接收吸猫响应逻辑 var bathCatRespTask = Task.Run(async () => { try { while (await bathCat.ResponseStream.MoveNext(cts.Token)) { Console.WriteLine(bathCat.ResponseStream.Current.Message); } } catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled) { Console.WriteLine("Stream cancelled."); } }); //随机给10个猫洗澡 for (int i = 0; i < 10; i++) { await bathCat.RequestStream.WriteAsync(new BathTheCatReq() { Id = rand.Next(0, catCount.Count) }); } //发送完毕 await bathCat.RequestStream.CompleteAsync(); Console.WriteLine("客户端已发送完10个需要洗澡的猫id"); Console.WriteLine("接收洗澡结果:"); //开始接收响应 await bathCatRespTask; Console.WriteLine("洗澡完毕");
6、运行结果