.NET?Core中使用gRPC的方法
1.什么是gRPC
1.基本介紹
gRPC 一開始由 google 開發(fā),是一款語言中立、平臺(tái)中立、開源的遠(yuǎn)程過程調(diào)用(RPC)系統(tǒng),所以叫g(shù)(google)RPC。支持主流開發(fā)語言(C, C++, Python, PHP, Ruby, NodeJS, C#, Objective-C、Golang
2.proto文件
用于定義協(xié)議接口和數(shù)據(jù)格式,不同的語言,相同的文件,可以理解為一項(xiàng)約定,序列化支持 PB(Protocol buffer)和 JSON,PB 是一種語言無關(guān)的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 調(diào)用的高性能。
說這么多感覺還是很模糊,上面只是介紹了gRPC是什么,在我看來其實(shí)它大致的作用跟WebServices、WCF差不多,在某個(gè)維度上可以說都是作為遠(yuǎn)程調(diào)用,只不過所處的時(shí)代和本身的特性,以及生態(tài)的發(fā)展下,導(dǎo)致它成為目前比較火熱的原因之一,具體的內(nèi)容后面再討論,先用起來,再深入了解,接下來我們使用.Net Core 先搭建一個(gè)簡(jiǎn)單的Demo,來親自上手實(shí)踐一下。
其實(shí)背景就是最近在做一個(gè)項(xiàng)目,需要做一個(gè)公司內(nèi)部的Nuget包,大概的業(yè)務(wù)就是Nuget包請(qǐng)求微服務(wù)數(shù)據(jù),開始想直接使用http的方式,基于整體項(xiàng)目結(jié)構(gòu)后面定了使用gRPC,既學(xué)即用,剛好也可以在實(shí)際項(xiàng)目應(yīng)用中,查漏補(bǔ)缺。
3.上手實(shí)踐
1.使用vs首先創(chuàng)建一個(gè)NetCore gRPC項(xiàng)目,得到一個(gè)項(xiàng)目結(jié)構(gòu)如下,框架默認(rèn)包含一個(gè)已經(jīng)預(yù)先定義的協(xié)議文件和服務(wù)接口,如果使用其他的方式也很簡(jiǎn)單直接引用相關(guān)的包,然后添加以下服務(wù)就可以了

2.我們自己創(chuàng)建一個(gè)自己的接口,定義一個(gè)協(xié)議文件mytestdemo.proto,然后定義一些方法,主要包含如下幾類,其他的一些用法可以在網(wǎng)上搜到,或者去看文檔,只是簡(jiǎn)單列一下
1.有參數(shù)有返回值
2.無參數(shù)有返回值 ,無參使用google.protobuf.Empty
3.集合作為返回值,必須使用repeated 標(biāo)記
如果你真的不熟悉protobuf的定義方式和寫法,這個(gè)無傷大雅,可以使用工具生成
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);
}
//多參數(shù)請(qǐng)求參數(shù)
message MultipleRequestPara {
int32 Id = 1;
string Name = 2;//參數(shù)個(gè)數(shù)
bool IsExists =3;
}
message SingeRespone {
bool Success =1;
TestEntity a1 = 2;
message TestEntity{
int32 Id =1;
}
}
//多參數(shù)返回
message MultipleRespone {
bool Success =1;
}
//返回集合參數(shù)
message CollectionResponePara {
repeated CollectionChildrenRespone1 param1 =1;
repeated CollectionChildrenRespone2 param2 =2;
repeated int32 param3 =3;
}
//集合屬性1
message CollectionChildrenRespone1 {
int32 Id =1;
}
//集合屬性2
message CollectionChildrenRespone2 {
string Name =1;
}3.右鍵類,選擇添加,選擇連接的服務(wù),添加gRPC,或者直接修改項(xiàng)目文件,將新建的proto添加到類中
3.1 重新生成,然后創(chuàng)建服務(wù)代碼MyTestService,如下代碼
3.2 在啟動(dòng)類中映射gRPC app.MapGrpcService<MyTestService>(); 否則會(huì)報(bào)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.創(chuàng)建客戶端,將proto文件拷貝過去調(diào)用,添加服務(wù)為客戶端模式,然后添加如下代碼
using (var channel = GrpcChannel.ForAddress("https://localhost:7245"))
{
var client = new MyTestDemo.MyTestDemoClient(channel);
//多參數(shù)調(diào)用
var reply = client.MultipleParam(new MultipleRequestPara { Id = 123, Name = "sa", IsExists = true });
//無參調(diào)用
var singeRespone = client.NoParam(new Google.Protobuf.WellKnownTypes.Empty());
//調(diào)用集合
var collectionResponePara = client.CollectionParam(new Google.Protobuf.WellKnownTypes.Empty());
}
2.gRPC流
gRPC中支持4種流,分別是:
1.簡(jiǎn)單 RPC(Unary RPC)它的特點(diǎn)是傳入一個(gè)請(qǐng)求對(duì)象,返回一個(gè)請(qǐng)求對(duì)象

2.服務(wù)端流式 RPC (Server streaming RPC)客戶端傳入一個(gè)請(qǐng)求對(duì)象,服務(wù)端可以返回多個(gè)結(jié)果對(duì)象,形象的表示就是客戶端傳入一個(gè)股票的id,服務(wù)端就將股票的信息遠(yuǎn)遠(yuǎn)不斷地返回

3.客戶端流式 RPC (Client streaming RPC) 客戶端源源不斷的傳入多個(gè)請(qǐng)求對(duì)象,服務(wù)端返回一個(gè)結(jié)果對(duì)象,形象的表示例如上位機(jī)采集實(shí)時(shí)將采集數(shù)據(jù),源源不斷的傳入服務(wù)器

4.雙向流式 RPC (Bi-directional streaming RPC) 結(jié)合服務(wù)端和客戶端流,傳入多請(qǐng)求,返回多個(gè)結(jié)果,相當(dāng)于建立長(zhǎng)連接,可以進(jìn)行相互的操作

下面我們就主要介紹幾類主要的流的使用以及步驟
1.服務(wù)端流、客戶端流、雙向流
服務(wù)端流主要的特征就是服務(wù)端會(huì)源源不斷的響應(yīng)數(shù)據(jù)到客戶端
1.首先還是創(chuàng)建protobuf文件,聲明一個(gè)服務(wù)端流的rpc接口ExcuteServerStream 和一個(gè)客戶端流接口ExcuteClientStream
syntax = "proto3";
option csharp_namespace = "GrpcDemo";
package streamtest;
service StreamTest {
//服務(wù)端流定義
rpc ExcuteServerStream(StreamForClientRequest) returns (stream StreamForClientRespones);
//客戶端流定義
rpc ExcuteServerStream(StreamForClientRequest) returns (stream StreamForClientRespones);
//雙向流
rpc ExcuteMutualStream(stream StreamForClientRequest) returns ( stream StreamForClientRespones);
}
//調(diào)用流的請(qǐng)求對(duì)象
message StreamForClientRequest{
int32 Id=1;
}
//調(diào)用端流的返回對(duì)象
message StreamForClientRespones{
repeated int32 Number=1;//集合
}2.重新生成服務(wù)引用,然后創(chuàng)建對(duì)應(yīng)的實(shí)現(xiàn)接口StreamTestService并重寫生成的服務(wù),然后在啟動(dòng)程序映射服務(wù)接口
//服務(wù)端流接口
public override async Task ExcuteServerStream(StreamForClientRequest req,IServerStreamWriter<StreamForClientRespones> resStream,ServerCallContext context)
{
//list集合作為模擬數(shù)據(jù)源
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);
//模擬源源不斷的數(shù)據(jù)響應(yīng)
await Task.Delay(1000);
}
}
//客戶端流接口
public override async Task<StreamForClientRespones> ExcuteClientStream( IAsyncStreamReader<StreamForClientRequest> requestStream, ServerCallContext context)
{
StreamForClientRespones intArrayModel = new StreamForClientRespones();
//獲取請(qǐng)求流中的數(shù)據(jù)
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;
//從流中獲取請(qǐng)求
while (await reqStream.MoveNext())
{
i++;
var ele = new StreamForClientRespones();
ele.Number.Add(i);
//寫入響應(yīng)流
await resStream.WriteAsync(ele);
await Task.Delay(500);
}
}3.創(chuàng)建客戶端調(diào)用,把服務(wù)端的protobuf文件拷貝到客戶端,然后生成,啟動(dòng)調(diào)用
//調(diào)用服務(wù)端流
using (var channel = GrpcChannel.ForAddress("https://localhost:7245"))
{
var client = new StreamTest.StreamTestClient(channel);
//調(diào)用服務(wù)端流
var reply = client.ExcuteServerStream(new StreamForClientRequest { Id =1});
//利用線程取消
//CancellationTokenSource cts = new CancellationTokenSource();
//指定在2s后進(jìn)行取消操作
//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]);
}
}
//調(diào)用客戶端流
using (var channel = GrpcChannel.ForAddress("https://localhost:7245"))
{
var client = new StreamTest.StreamTestClient(channel);
//調(diào)用客戶端流接口
var reply = client.ExcuteClientStream();
//模擬源源不斷的數(shù)據(jù)發(fā)送
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("*************發(fā)送完畢*******************");
await reply.RequestStream.CompleteAsync();
//接受結(jié)果
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);
//調(diào)用雙向流接口
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);
}
//發(fā)送完畢
await reply.RequestStream.CompleteAsync();
//開始接收響應(yīng)
await bathCatRespTask;
}
2.NetCore Web項(xiàng)目作為客戶端
1.首先還是先引入proto文件,然后生成客戶端
2.在web項(xiàng)目中的控制器中,我們就不能直接簡(jiǎn)陋的使用 using的方式來連接gRPC服務(wù)端了,可以利用內(nèi)置的依賴注入的模式來完成
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.調(diào)用出現(xiàn)如下問題 ,使用dotnet dev-certs https --trust

3.gRPC AOP攔截
有時(shí)候我們想在gRPC服務(wù)執(zhí)行前后做一些操作,這時(shí)候可以使用其Aop攔截,如果你要問攔截器可以做什么,我不太想解釋,繼續(xù)往下看,攔截器方法定義在Interceptor類中,服務(wù)端和客戶端攔截是一樣的原理,下面列舉一些攔截器:
| 名稱 | 特點(diǎn) |
|---|---|
| BlockingUnaryCall | 攔截阻塞調(diào)用 |
| AsyncUnaryCall | 攔截異步調(diào)用 |
| AsyncServerStreamingCall | 攔截異步服務(wù)端流調(diào)用 |
| AsyncClientStreamingCall | 攔截異步客戶端流調(diào)用 |
| AsyncDuplexStreamingCall | 攔截異步雙向流調(diào)用 |
| UnaryServerHandler | 用于攔截和傳入普通調(diào)用的服務(wù)器端處理程序 |
| ClientStreamingSerHandler | 用于攔截客戶端流調(diào)用的服務(wù)器端處理程序 |
| ServerStreamingSerHandler | 用于攔截服務(wù)端流調(diào)用的服務(wù)器端處理程序 |
| DuplexStreamingSerHandler | 用于攔截雙向流調(diào)用的服務(wù)器端處理程序 |
1.聲明一個(gè)UnaryServerHandlerInterceptor類型的自定義攔截器,用于攔截和傳入普通調(diào)用的服務(wù)器端處理程序,然后繼承自Grpc.Core.Interceptors.Interceptor類, 重寫已經(jīng)定義的方法UnaryServerHandler
public class UnaryServerHandlerInterceptor : Interceptor
{
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request,
ServerCallContext context,
UnaryServerMethod<TRequest, TResponse> continuation)
{
Console.WriteLine("執(zhí)行調(diào)用前");
var result = await continuation(request, context);
Console.WriteLine("執(zhí)行調(diào)用后");
// 或向 客戶端附加 一些信息
// 也可以 用try catch 做異常日志
// 可以從 context中取出 調(diào)用方ip,做ip限制
// 可以 監(jiān)控continuation 的 執(zhí)行時(shí)間
return result;
}
}2.然后在注入容器時(shí)加入選項(xiàng)
builder.Services.AddGrpc(option => {
option.EnableDetailedErrors = true;
//加入服務(wù)端攔截器選項(xiàng)
option.Interceptors.Add<UnaryServerHandlerInterceptor>();
});到此這篇關(guān)于.NET Core中使用gRPC的文章就介紹到這了,更多相關(guān).NET Core使用gRPC內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Asp.net 頁面調(diào)用javascript變量的值
開發(fā)過程中碰到了這種情況,我想將javascript中定義的變量賦值給頁面中的TextBox控件.2009-12-12
Asp.net通過SignalR2進(jìn)行實(shí)時(shí)聊天
這篇文章介紹了Asp.net通過SignalR2進(jìn)行實(shí)時(shí)聊天的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
gridview和checkboxlist的嵌套相關(guān)應(yīng)用
gridview和checkboxlist的嵌套使用,會(huì)有效的提高開發(fā)的效率,不過很多的童鞋們對(duì)此還是很陌生的,接下來將幫助童鞋們實(shí)現(xiàn)gridview和checkboxlist的嵌套使用,感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02
ASP.NET數(shù)組刪除重復(fù)值實(shí)現(xiàn)代碼
在ASP.NET編程中,要想刪除數(shù)組的重復(fù)值可以使用多種方法代碼實(shí)現(xiàn)相同的效果。今天,在某個(gè)博客中看到某功能代碼中的一小段代碼很不錯(cuò),它就是用來移動(dòng)數(shù)組中相同值的方法,分享給大家2015-10-10
詳解ASP.NET與ASP.NET Core用戶驗(yàn)證Cookie并存解決方案
本篇文章主要介紹了詳解ASP.NET與ASP.NET Core用戶驗(yàn)證Cookie并存解決方案 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-02-02
asp.net(c#)下讀取word文檔的方法小結(jié)
asp.net(c#)下讀取word文檔的方法小結(jié),需要的朋友可以參考下。2011-07-07

