.NET Core中使用gRPC的方法
目錄
- 1.什么是gRPC
- 1.基本介紹
- 2.proto文件
- 3.上手實踐
- 2.gRPC流
- 1.服務端流、客戶端流、雙向流
- 2.NetCore Web項目作為客戶端
- 3.gRPC AOP攔截
1.什么是gRPC
1.基本介紹
gRPC 一開始由 google 開發,是一款語言中立、平臺中立、開源的遠程過程調用(RPC)系統,所以叫g(google)RPC。支持主流開發語言(C, C++, Python, PHP, Ruby, NodeJS, C#, Objective-C、Golang
2.proto文件
用于定義協議接口和數據格式,不同的語言,相同的文件,可以理解為一項約定,序列化支持 PB(Protocol buffer)和 JSON,PB 是一種語言無關的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 調用的高性能。
說這么多感覺還是很模糊,上面只是介紹了gRPC是什么,在我看來其實它大致的作用跟WebServices
、WCF
差不多,在某個維度上可以說都是作為遠程調用,只不過所處的時代和本身的特性,以及生態的發展下,導致它成為目前比較火熱的原因之一,具體的內容后面再討論,先用起來,再深入了解,接下來我們使用.Net Core 先搭建一個簡單的Demo,來親自上手實踐一下。
其實背景就是最近在做一個項目,需要做一個公司內部的Nuget包,大概的業務就是Nuget包請求微服務數據
,開始想直接使用http的方式,基于整體項目結構后面定了使用gRPC,既學即用,剛好也可以在實際項目應用中,查漏補缺。
3.上手實踐
1.使用vs首先創建一個NetCore gRPC
項目,得到一個項目結構如下,框架默認包含一個已經預先定義
的協議文件
和服務接口
,如果使用其他的方式也很簡單直接引用相關的包,然后添加以下服務就可以了
2.我們自己創建一個自己的接口,定義一個協議文件mytestdemo.proto,然后定義一些方法,主要包含如下幾類,其他的一些用法可以在網上搜到,或者去看文檔,只是簡單列一下
1.有參數有返回值
2.無參數有返回值 ,無參使用google.protobuf.Empty
3.集合作為返回值,必須使用repeated
標記
如果你真的不熟悉protobuf的定義方式和寫法,這個無傷大雅,可以使用
工具生成
syntax = "proto3";//引入集合包import "google/protobuf/empty.proto";//命名空間option csharp_namespace = "GrpcDemo";//包名package MyTest;//接口定義service MyTestDemo { rpc MultipleParam(MultipleRequestPara) returns (MultipleRespone); rpc NoParam(google.protobuf.Empty) returns (SingeRespone); rpc CollectionParam(google.protobuf.Empty) returns (CollectionResponePara);}//多參數請求參數message MultipleRequestPara { int32 Id = 1; string Name = 2;//參數個數 bool IsExists =3;}message SingeRespone { bool Success =1; TestEntity a1 = 2; message TestEntity{ int32 Id =1; }}//多參數返回message MultipleRespone { bool Success =1;}//返回集合參數message CollectionResponePara { repeated CollectionChildrenRespone1 param1 =1; repeated CollectionChildrenRespone2 param2 =2; repeated int32 param3 =3;}//集合屬性1message CollectionChildrenRespone1 { int32 Id =1;}//集合屬性2message CollectionChildrenRespone2 { string Name =1; }
3.右鍵類,選擇添加,選擇連接的服務,添加gRPC,或者直接修改項目文件,將新建的proto添加到類中
3.1 重新生成,然后創建服務代碼MyTestService
,如下代碼
3.2 在啟動類中映射gRPC app.MapGrpcService<MyTestService>();
否則會報service is unimplemented.
/// <summary>/// 繼承自MyTestDemo.MyTestDemoBase/// </summary>public class MyTestService : MyTestDemo.MyTestDemoBase{ public override async Task<MultipleRespone> MultipleParam(MultipleRequestPara request, ServerCallContext context) {return await Task.FromResult(new MultipleRespone{ Success = true,}); } public override async Task<SingeRespone> NoParam(Empty request, ServerCallContext context) { TestEntity t = new TestEntity(); t.Id = 1; return await Task.FromResult(new SingeRespone { Success = true, entity = t }); ; } public override async Task<CollectionResponePara> CollectionParam(Empty request, ServerCallContext context) {CollectionResponePara collectionResponePara = new CollectionResponePara();CollectionChildrenRespone1 a = new CollectionChildrenRespone1 { Id = 1 };CollectionChildrenRespone2 b = new CollectionChildrenRespone2 { Name = "jeck" };collectionResponePara.Param1.Add(a);collectionResponePara.Param2.Add(b);return await Task.FromResult(collectionResponePara); }}
4.創建客戶端,將proto文件拷貝過去調用,添加服務為客戶端模式,然后添加如下代碼
using (var channel = GrpcChannel.ForAddress("https://localhost:7245")) { var client = new MyTestDemo.MyTestDemoClient(channel); //多參數調用 var reply = client.MultipleParam(new MultipleRequestPara { Id = 123, Name = "sa", IsExists = true }); //無參調用 var singeRespone = client.NoParam(new Google.Protobuf.WellKnownTypes.Empty()); //調用集合 var collectionResponePara = client.CollectionParam(new Google.Protobuf.WellKnownTypes.Empty()); }
2.gRPC流
gRPC中支持4種流,分別是:
1.簡單 RPC(Unary RPC)
它的特點是傳入一個請求對象,返回一個請求對象
2.服務端流式 RPC (Server streaming RPC)
客戶端傳入一個請求對象,服務端可以返回多個結果對象,形象的表示就是客戶端傳入一個股票的id,服務端就將股票的信息遠遠不斷地返回
3.客戶端流式 RPC (Client streaming RPC)
客戶端源源不斷的傳入多個請求對象,服務端返回一個結果對象,形象的表示例如上位機采集實時將采集數據,源源不斷的傳入服務器
4.雙向流式 RPC (Bi-directional streaming RPC)
結合服務端和客戶端流,傳入多請求,返回多個結果,相當于建立長連接,可以進行相互的操作
下面我們就主要介紹幾類主要的流的使用以及步驟
1.服務端流、客戶端流、雙向流
服務端流主要的特征就是服務端會源源不斷的響應數據到客戶端
1.首先還是創建protobuf
文件,聲明
一個服務端流
的rpc接口ExcuteServerStream
和一個客戶端流接口ExcuteClientStream
syntax = "proto3";option csharp_namespace = "GrpcDemo";package streamtest;service StreamTest { //服務端流定義 rpc ExcuteServerStream(StreamForClientRequest) returns (stream StreamForClientRespones); //客戶端流定義 rpc ExcuteServerStream(StreamForClientRequest) returns (stream StreamForClientRespones); //雙向流 rpc ExcuteMutualStream(stream StreamForClientRequest) returns ( stream StreamForClientRespones);}//調用流的請求對象message StreamForClientRequest{ int32 Id=1;}//調用端流的返回對象message StreamForClientRespones{ repeated int32 Number=1;//集合}
2.重新生成服務引用,然后創建對應的實現接口StreamTestService
并重寫生成的服務,然后在啟動程序映射服務接口
//服務端流接口public override async Task ExcuteServerStream(StreamForClientRequest req,IServerStreamWriter<StreamForClientRespones> resStream,ServerCallContext context){ //list集合作為模擬數據源 var list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; foreach (var item in list) {Console.WriteLine($"********{item}*******");var ele = new StreamForClientRespones();ele.Number.Add(item);//寫入流中await resStream.WriteAsync(ele);//模擬源源不斷的數據響應await Task.Delay(1000); }}//客戶端流接口public override async Task<StreamForClientRespones> ExcuteClientStream( IAsyncStreamReader<StreamForClientRequest> requestStream, ServerCallContext context){ StreamForClientRespones intArrayModel = new StreamForClientRespones(); //獲取請求流中的數據 while (await requestStream.MoveNext()) {intArrayModel.Number.Add(requestStream.Current.Id + 1);Console.WriteLine($"ExcuteClientStream Number {requestStream.Current.Id} 獲取到并處理.");Thread.Sleep(100); } return intArrayModel;}//雙向流public override async Task ExcuteMutualStream(IAsyncStreamReader<StreamForClientRequest> reqStream,IServerStreamWriter<StreamForClientRespones> resStream,ServerCallContext context){ int i = 0; //從流中獲取請求 while (await reqStream.MoveNext()) {i++;var ele = new StreamForClientRespones();ele.Number.Add(i);//寫入響應流await resStream.WriteAsync(ele);await Task.Delay(500); }}
3.創建客戶端調用,把服務端的protobuf
文件拷貝到客戶端,然后生成,啟動調用
//調用服務端流 using (var channel = GrpcChannel.ForAddress("https://localhost:7245")) { var client = new StreamTest.StreamTestClient(channel); //調用服務端流 var reply = client.ExcuteServerStream(new StreamForClientRequest { Id =1}); //利用線程取消 //CancellationTokenSource cts = new CancellationTokenSource(); //指定在2s后進行取消操作 //cts.CancelAfter(TimeSpan.FromSeconds(2.5)); //var reply = client.ExcuteServerStream(new StreamForClientRequest { Id = 1 }, cancellationToken: cts.Token); await foreach (var resp in reply.ResponseStream.ReadAllAsync()) { Console.WriteLine(resp.Number[0]); } } //調用客戶端流 using (var channel = GrpcChannel.ForAddress("https://localhost:7245")) { var client = new StreamTest.StreamTestClient(channel); //調用客戶端流接口 var reply = client.ExcuteClientStream(); //模擬源源不斷的數據發送 for (int i = 0; i < 10; i++) { await reply.RequestStream.WriteAsync(new StreamForClientRequest() { Id = new Random().Next(0, 20) }); await Task.Delay(100); } Console.WriteLine("*************發送完畢*******************"); await reply.RequestStream.CompleteAsync(); //接受結果 foreach (var item in reply.ResponseAsync.Result.Number) { Console.WriteLine($"This is {item} Result"); } }//雙向流using (var channel = GrpcChannel.ForAddress("https://localhost:7245")){ var client = new StreamTest.StreamTestClient(channel); //調用雙向流接口 var reply = client.ExcuteMutualStream(); //獲取流放入線程 var bathCatRespTask = Task.Run(async () => {await foreach (var resp in reply.ResponseStream.ReadAllAsync()){ Console.WriteLine(resp.Number[0]);} }); //寫入流 for (int i = 0; i < 10; i++) {await reply.RequestStream.WriteAsync(new StreamForClientRequest() { Id = new Random().Next(0, 20) });await Task.Delay(100); } //發送完畢 await reply.RequestStream.CompleteAsync(); //開始接收響應 await bathCatRespTask;}
2.NetCore Web
項目作為客戶端
1.首先還是先引入proto文件,然后生成客戶端
2.在web項目中的控制器中,我們就不能直接簡陋的使用 using的方式來連接gRPC服務端了,可以利用內置的依賴注入的模式來完成
3.下載Grpc.Net.ClientFactory
包,然后在`Program將客戶端添加到依賴注入容器
builder.Services.AddGrpcClient<MyTestDemo.MyTestDemoClient>(option => { option.Address = new Uri("https://localhost:7245");});
4.然后在控制器中直接注入,就可以使用
public class gRPCTestController : ControllerBase { private readonly MyTestDemoClient _client; public gRPCTestController(MyTestDemoClient client) { _client = client; } [HttpGet(Name = "Excute")] public async Task<string> Get() { var a = await _client.NoParamAsync(new Google.Protobuf.WellKnownTypes.Empty()); var str = a.Success.ToString(); return str; } }
5.調用出現如下問題 ,使用dotnet dev-certs https --trust
3.gRPC AOP攔截
有時候我們想在gRPC服務執行前后做一些操作,這時候可以使用其Aop攔截
,如果你要問攔截器可以做什么,我不太想解釋,繼續往下看,攔截器方法定義在Interceptor
類中,服務端和客戶端攔截是一樣的原理,下面列舉一些攔截器:
1.聲明一個UnaryServerHandlerInterceptor
類型的自定義攔截器,用于攔截和傳入普通調用的服務器端處理程序
,然后繼承自Grpc.Core.Interceptors.Interceptor類, 重寫已經定義的方法UnaryServerHandler
public class UnaryServerHandlerInterceptor : Interceptor{ public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>( TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation) {Console.WriteLine("執行調用前");var result = await continuation(request, context);Console.WriteLine("執行調用后");// 或向 客戶端附加 一些信息// 也可以 用try catch 做異常日志// 可以從 context中取出 調用方ip,做ip限制// 可以 監控continuation 的 執行時間return result; } }
2.然后在注入容器時加入選項
builder.Services.AddGrpc(option => { option.EnableDetailedErrors = true; //加入服務端攔截器選項 option.Interceptors.Add<UnaryServerHandlerInterceptor>();});
到此這篇關于.NET Core中使用gRPC的文章就介紹到這了,更多相關.NET Core使用gRPC內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!
