今天小編給大家分享一下ASP.NET Core中Grpc通信怎么使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
Grpc中使用協議緩沖區 (protobuf) 用作接口設計語言 (IDL),它的主要內容包含:
GRPC 服務的定義。
客戶端和服務器之間發送的消息。
Grpc.Tools 這個工具,在每次編譯的時候,都能將.proto文件生成為對于的cs文件。 服務端和客戶端都需要添加。Grpc.AspNetCore這個包會對Grpc.Tools 進行使用。引用了這個包之后。還有注意在Server和Client,都要在對應.csproj下面,修改GrpcServices這個配置的值,如果是服務端就寫Server,如果是客戶端就寫Client。
<ItemGroup> <Protobuf Include="Protos\xxxx.proto" GrpcServices="Server" /> </ItemGroup> <ItemGroup> <Protobuf Include="Protos\xxxx.proto" GrpcServices="Client" /> </ItemGroup>
需要添加 Grpc.AspNetCore 的引用
在Startup.cs中需要配置如下信息:
①ConfigureServices 中需要配置
services.AddGrpc();
②Configure 中需要配置endpoints
app.UseEndpoints(endpoints => { // Communication with gRPC endpoints must be made through a gRPC client. // To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909 endpoints.MapGrpcService<GreeterService>(); });
Grpc中的endpoints需要下面的支持:
HTTP/2
gRPC 要求 HTTP/2。gRPC for ASP.NET Core 驗證HttpRequest為HTTP/2。在大多數現代操作系統上,Kestrel支持 HTTP/2。默認情況下,Kestrel 終結點配置為支持 HTTP/1.1 和 HTTP/2 連接。
傳輸安全性Transport Layer Security (TLS).
用于 gRPC 的 Kestrel 終結點應使用 TLS 進行保護。
在開發版中,將在存在 ASP.NET Core 開發證書https://localhost:5001時,自動創建一個使用 TLS 保護的終結點。不需要配置。https前綴驗證 Kestrel 終結點是否正在使用 TLS。
在生產環境如果使用TLS中,必須顯式配置 TLS。
兩種方式配置對應的TLS:
{ "Kestrel": { "Endpoints": { "HttpsInlineCertFile": { "Url": "https://localhost:5001", "Protocols": "Http2", "Certificate": { "Path": "<path to .pfx file>", "Password": "<certificate password>" } } } } }
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { options.Listen(IPAddress.Any, 5001, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps("<path to .pfx file>", "<certificate password>"); }); }); webBuilder.UseStartup<Startup>(); });
此外TLS不僅僅適用于Client和Server間的安全傳輸,還可以用于服務協商。
我在看官網的關于服務協商時候,有些發懵,因為又說了grpc 需要Http/2 、又需要TLS,但是后面又說在不配置TLS的endpoint的時候...... 那么到底是需要TLS還是不需要TLS,Grpc到底是僅僅支持HTTP2還是會兼容HTTP1?
首先要明確幾個概念:
什么是EndPoint
什么是Grpc endpoint
什么是TLS
TLS 和 HTTP1、HTTP2
以下是我的理解:
Endpoint是一個大概念,不僅僅是grpc有endpoint,以前我們用的webservice、wcf都有,他可以是HTTP1的,也可以是HTTP2的,也可以都支持。僅僅是當我們要用Grpc的時候我們需要使用HTTP2協議。
TLS是一種安全協議,是在傳輸層上的安全協議,具體是什么樣的可以不用了解,只是在.Net Core 中配置TLS,不僅僅作用于安全傳輸,還有作用于協議的選擇,當我們的endpoint使用的是HTTP2,且不用TLS的時候,我們需要配置我們的Kestrel服務器的ListenOptions.Protocols 必須設置為HttpProtocols.Http2 ,換句話說 如果ListenOptions.Protocols= HttpProtocols.Http1AndHttp2,那么就不能使用TLS。
總結一下就是:
你配置你的Kestrel 為使用HTTP2協議的時候,你可以使用TLS作為安全傳輸,也可以不用
你配置你的Kestrel 為使用兼容HTTP1協議的時候,那么你就不能用TLS
那么對于GPRC來講,他的endpoint 必須使用HTTP2協議,TLS也不是必須要配置的。
public class GreeterService : Greeter.GreeterBase { private readonly ILogger<GreeterService> _logger; public GreeterService(ILogger<GreeterService> logger) { _logger = logger; } public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } }
①繼承一下生成出來的抽象類 Greeter.GreeterBase
②根據自己的需要,依賴注入一些必要的Service類
③實現生成出來的Virtual方法
1.發生的問題
2.Status(StatusCode=Internal, Detail="Error starting gRPC call: The SSL connection could not be established, see inner exception.")
https://docs.microsoft.com/zh-cn/aspnet/core/grpc/troubleshoot?view=aspnetcore-3.0
3.Grpc.Core.RpcException:“Status(StatusCode=Internal, Detail="Bad gRPC response. Response protocol downgraded to HTTP/1.0.")”
先按照下面的做
https://github.com/grpc/grpc-dotnet/issues/654
如果不行的話,檢查一下是否本地一直在開著抓包工具之類的代理軟件,我之前一直不行,是因為本地運行著Fiddler
和上面不一樣,grpc client 是通過 xxx.proto 文件生成的具體的類型。里面包含了我們要調用的所有的方法。一般情況下,我們都是創建一個Channel類,然后通過Channel類在創建一個Client。
如下圖所示。
var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new Greet.GreeterClient(channel);
ForAddress方法:
注:
gRPC的Channel維持了一個到遠程服務的長連接。
客戶端對象可以重用相同的通道。
與調用遠程方法相比,創建通道是一項昂貴的操作,因此通常應該為盡可能多的調用重用單個通道。
Grpc具有多種不同的方式:
Unary (一元模式)
Server streaming (服務器流)
Client streaming (客戶端流)
Bi-directional streaming (雙向流)
從客戶端發送請求到服務端開始,服務端響應請求回來結束。每一個Unary 服務都會從*.proto文件中生成兩個具體的調用方法,一個異步方法,一個同步方法。例如下面的代碼:
var reply1 = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" }); var reply2 = client.SayHello(new HelloRequest { Name = "GreeterClient" });
從客戶端發送請求到服務端開始,利用ResponseStream.MoveNext()方法讀取服務端返回的數據,知道ResponseStream.MoveNext() 返回false說明讀取結束。
var client = new Greet.GreeterClient(channel); using (var call = client.SayHellos(new HelloRequest { Name = "World" })) { while (await call.ResponseStream.MoveNext()) { Console.WriteLine("Greeting: " + call.ResponseStream.Current.Message); // "Greeting: Hello World" is written multiple times } } //或者C# 8以上通過await foreach來處理返回信息 using (var call = client.SayHellos(new HelloRequest { Name = "World" })) { await foreach (var response in call.ResponseStream.ReadAllAsync()) { Console.WriteLine("Greeting: " + response.Message); // "Greeting: Hello World" is written multiple times } }
客戶端流調用不需要在客戶端發送請求之后開始,客戶端可以選擇發送帶有RequestStream.WriteAsync的發送消息,當客戶端完成發送消息請求流時。調用CompleteAsync來通知服務,當服務返回響應消息時,調用結束。
var client = new Counter.CounterClient(channel); using (var call = client.AccumulateCount()) { //調用三次 for (var i = 0; i < 3; i++) { await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 }); } //通知服務寫好了 await call.RequestStream.CompleteAsync(); //獲取返回結果 var response = await call; Console.WriteLine($"Count: {response.Count}"); // Count: 3 }
相當于②和③的結合,也不需要等待客戶端發送完請求,使用RequestStream.WriteAsync寫入消息,通過ResponseStream.MoveNext()或者ResponseStream.ReadAllAsync()讀取服務端返回的數據。當服務端不在返回的時候,結束請求。
using (var call = client.Echo()) { Console.WriteLine("Starting background task to receive messages"); var readTask = Task.Run(async () => { await foreach (var response in call.ResponseStream.ReadAllAsync()) { Console.WriteLine(response.Message); // Echo messages sent to the service } }); Console.WriteLine("Starting to send messages"); Console.WriteLine("Type a message to echo then press enter."); while (true) { var result = Console.ReadLine(); if (string.IsNullOrEmpty(result)) { break; } await call.RequestStream.WriteAsync(new EchoMessage { Message = result }); } Console.WriteLine("Disconnecting"); await call.RequestStream.CompleteAsync(); await readTask; }
此外這里補充一下幾種方式適用的場景
(1) 簡單模式(Simple RPC)
這種模式最為傳統,即客戶端發起一次請求,服務端響應一個數據,這和大家平時熟悉的RPC沒有什么大的區別,所以不再詳細介紹。
(2) 服務端數據流模式(Server-side streaming RPC)
這種模式是客戶端發起一次請求,服務端返回一段連續的數據流。典型的例子是客戶端向服務端發送一個股票代碼,服務端就把該股票的實時數據源源不斷的返回給客戶端。
(3) 客戶端數據流模式(Client-side streaming RPC)
與服務端數據流模式相反,這次是客戶端源源不斷的向服務端發送數據流,而在發送結束后,由服務端返回一個響應。典型的例子是物聯網終端向服務器報送數據。
(4) 雙向數據流模式(Bidirectional streaming RPC)
顧名思義,這是客戶端和服務端都可以向對方發送數據流,這個時候雙方的數據可以同時互相發送,也就是可以實現實時交互。典型的例子是聊天機器人。
①在GrpcService中引入Grpc.AspNetCore包,此包會在編譯的時候,將.proto文件生成對應的服務端grpc服務代碼。
.net core需要自己去生成,3.0以后已經在上面的包中集成的生成的功能,只要生成代碼就會調用Grpc.Tools和Google.Protobuf去產生對應的cs文件
②創建一個proto文件
syntax = "proto3"; option csharp_namespace = "GrpcService"; package Hello; service Hello { //通過一元方式傳輸 rpc SayHello (HelloRequest) returns (HelloReply); //通過客戶端流的方式傳輸 rpc SayHelloClientStream (stream HelloRequest) returns (HelloReply); //通過服務端流的方式傳輸 rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply); //通過雙向流的方式傳輸 rpc SayHelloStream (stream HelloRequest) returns (stream HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
并修改對應的服務端csproj文件,將該proto文件加入到項目中,否則不會生成服務端代碼。
<ItemGroup> <Protobuf Include="Hello.proto" GrpcServices="Server" /> </ItemGroup>
③創建一個HelloService類,繼承自生成出來的抽象類XXXBase,然后可以重寫對應我們聲明的方法,以便于提供客戶端訪問。
注:xxx.xxxBase 可能在繼承的時候顯示不存在,那是因為沒有②之后沒有進行編譯,編譯之后就能生成了。
public class HelloService : Hello.HelloBase { public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { return Task.FromResult(new HelloReply { Message = "Response:" + request.Name }); } public override async Task<HelloReply> SayHelloClientStream(IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context) { var current = new HelloRequest(); while (await requestStream.MoveNext()) { current = requestStream.Current; } var task = new Task<HelloReply>(() => { var reply = new HelloReply() { Message = "Response:" + current.Name }; return reply; }); task.Start(); var result = await task; return result; } public override async Task SayHelloServerStream(HelloRequest request, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context) { await responseStream.WriteAsync(new HelloReply() { Message = "Response:" + request.Name }); } public override async Task SayHelloStream(IAsyncStreamReader<HelloRequest> requestStream, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context) { while (await requestStream.MoveNext()) { await responseStream.WriteAsync(new HelloReply() { Message = "Response:" + requestStream.Current.Name }); } } }
④因為是服務端,一旦實現了所有的方法,還需要啟動一個gRPC服務器,這樣客戶端才可以使用服務。
可以采用兩種方式來持久化服務,監聽端口,處理請求:
a.使用Grpc.Core里面的Server (適用于.net core 3.0以下版本)
b.配置startup.cs中的ConfigureServices添加服務,Configure方法中配置終結點,并且配置對應的Kestrel服務器
第一種方式:
class Program { private static Server _server; static void Main(string[] args) { _server = new Server { Services = { Hello.BindService(new HelloService()) }, //這里使用的是不安全的方式 Ports = { new ServerPort("localhost", 50001, ServerCredentials.Insecure) } }; _server.Start(); Console.WriteLine("Listen Port 50001"); Console.ReadKey(); _server?.ShutdownAsync().Wait(); } }
第二種方式:
class Program { static void Main(string[] args) { #region 使用Kestrel服務器 CreateHostBuilder(args).Build().Run(); #endregion } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { // Setup a HTTP/2 endpoint without TLS. options.ListenLocalhost(50001, o => o.Protocols = HttpProtocols.Http2); }); webBuilder.UseStartup<Startup>(); }); }
Startup.cs
public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddGrpc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGrpcService<HelloService>(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client." + " To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); }); } }
①添加相關的依賴包
Install-Package Grpc.Net.Client
Install-Package Google.Protobuf
Install-Package Grpc.Tools
②添加之后,把服務端的proto文件一樣復制一份到客戶端,并在csproj下面追加如下的代碼,并編譯一次:
<ItemGroup> <Protobuf Include="Hello.proto" GrpcServices="Client" /> </ItemGroup>
③編寫客戶端調用的具體內容
class Program { static async Task Main(string[] args) { // This switch must be set before creating the GrpcChannel/HttpClient. AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress("http://localhost:50001"); var helloClient = new Hello.HelloClient(channel); //一元調用(同步方法) var reply = helloClient.SayHello(new HelloRequest { Name = "一元同步調用" }); Console.WriteLine($"{reply.Message}"); //一元調用(異步方法) var reply2 = helloClient.SayHelloAsync(new HelloRequest { Name = "一元異步調用" }).GetAwaiter().GetResult(); Console.WriteLine($"{reply2.Message}"); //服務端流 var reply3 = helloClient.SayHelloServerStream(new HelloRequest { Name = "服務端流" }); while (await reply3.ResponseStream.MoveNext()) { Console.WriteLine(reply3.ResponseStream.Current.Message); } //客戶端流 using (var call = helloClient.SayHelloClientStream()) { await call.RequestStream.WriteAsync(new HelloRequest { Name = "客戶端流" + i.ToString() }); await call.RequestStream.CompleteAsync(); var reply4 = await call; Console.WriteLine($"{reply4.Message}"); } //雙向流 using (var call = helloClient.SayHelloStream()) { Console.WriteLine("Starting background task to receive messages"); var readTask = Task.Run(async () => { await foreach (var response in call.ResponseStream.ReadAllAsync()) { Console.WriteLine(response.Message); } }); for (var i = 0; i < 3; i++) { await call.RequestStream.WriteAsync(new HelloRequest { Name = "雙向流" + i.ToString()}); } await call.RequestStream.CompleteAsync(); await readTask; } Console.ReadKey(); } }
執行:
這里要注意一段代碼AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true),如果不使用這段代碼,并且channel當中還是http的話,那么就會出現下面的異常:
以上就是“ASP.NET Core中Grpc通信怎么使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。