gRPC使用

gRPC

gRPC 是一個(gè)由Google開(kāi)源的,跨語(yǔ)言的,高性能的遠(yuǎn)程過(guò)程調(diào)用(RPC)框架。 gRPC使客戶端和服務(wù)端應(yīng)用程序可以透明地進(jìn)行通信,并簡(jiǎn)化了連接系統(tǒng)的構(gòu)建。它使用HTTP/2作為通信協(xié)議,使用 Protocol Buffers 作為序列化協(xié)議。

gRPC 的優(yōu)勢(shì)

gRPC 使用 HTTP/2 作為傳輸協(xié)議。 雖然與 HTTP 1.1 也能兼容,但 HTTP/2 具有許多高級(jí)功能:

  • 用于數(shù)據(jù)傳輸?shù)亩M(jìn)制組幀協(xié)議 - 與 HTTP 1.1 不同,HTTP 1.1 是基于文本的。
  • 對(duì)通過(guò)同一連接發(fā)送多個(gè)并行請(qǐng)求的多路復(fù)用支持 - HTTP 1.1 將處理限制為一次處理一個(gè)請(qǐng)求/響應(yīng)消息。
  • 雙向全雙工通信,用于同時(shí)發(fā)送客戶端請(qǐng)求和服務(wù)器響應(yīng)。
  • 內(nèi)置流式處理,支持對(duì)大型數(shù)據(jù)集進(jìn)行異步流式處理的請(qǐng)求和響應(yīng)。
  • 減少網(wǎng)絡(luò)使用率的標(biāo)頭壓縮。

gRPC 是輕量型且高性能的。 其處理速度可以比 JSON 序列化快 8 倍,消息小 60% 到 80%。 在WCF中,gRPC 的性能超過(guò)經(jīng)過(guò)高度優(yōu)化的 NetTCP 綁定的速度和效率。 與偏向于 Microsoft 堆棧的 NetTCP 不同,gRPC 是跨平臺(tái)的。

四種通信模式及其使用場(chǎng)景

服務(wù)類型 特點(diǎn)
簡(jiǎn)單RPC 簡(jiǎn)單rpc調(diào)用,傳入一個(gè)請(qǐng)求對(duì)象,返回一個(gè)返回對(duì)象
服務(wù)端流式RPC 傳入一個(gè)請(qǐng)求對(duì)象,服務(wù)端可以返回多個(gè)結(jié)果對(duì)象
客戶端流式RPC 客戶端傳入多個(gè)請(qǐng)求對(duì)象,服務(wù)端返回一個(gè)結(jié)果對(duì)象
雙向流式RPC 結(jié)合客戶端流式RPC和服務(wù)端流式RPC,可以傳入多個(gè)請(qǐng)求對(duì)象,返回多個(gè)結(jié)果對(duì)象

Unary PRC

 rpc UnaryCall (ExampleRequest) returns (ExampleResponse);

客戶端發(fā)起一次請(qǐng)求,服務(wù)端響應(yīng)一個(gè)數(shù)據(jù),即標(biāo)準(zhǔn)RPC通信。
這種模式,一個(gè)每一次都是發(fā)起一個(gè)獨(dú)立的tcp連接,走一次三次握手和四次揮手!

Server streaming RPC

rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);

服務(wù)端流 RPC 下,客戶端發(fā)出一個(gè)請(qǐng)求,但不會(huì)立即得到一個(gè)響應(yīng),而是在服務(wù)端與客戶端之間建立一個(gè)單向的流,服務(wù)端可以隨時(shí)向流中寫入多個(gè)響應(yīng)消息,最后主動(dòng)關(guān)閉流,而客戶端需要監(jiān)聽(tīng)這個(gè)流,不斷獲取響應(yīng)直到流關(guān)閉

應(yīng)用場(chǎng)景:股票大盤走勢(shì)

Client streaming RPC

rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);

應(yīng)用場(chǎng)景:物聯(lián)網(wǎng)終端向服務(wù)器報(bào)送數(shù)據(jù)。

Bi-directional streaming RPC

rpc GetOrderNO(stream  OrderRequest) returns ( stream OrderReply);

應(yīng)用場(chǎng)景:聊天應(yīng)用

gRPC 示例

新建gRPC 項(xiàng)目,GrpcDemo.Service

創(chuàng)建完成后,看下項(xiàng)目中的代碼

  1. 配置文件appsettings.json多了Kestrel啟用 HTTP/2 的配置,因?yàn)?gRPC 是基于HTTP/2來(lái)通信的
  1. csproj中增加了包含Protobuf

后面如果需要新增proto,需要手動(dòng)添加,這邊一次性改成通配符

<Protobuf Include="Protos\*.proto" GrpcServices="Server" />
  1. Program中將gRPC服務(wù)添加到了終結(jié)點(diǎn)路由中
app.MapGrpcService<GreeterService>();
  1. 查看PB協(xié)議文件greet.proto
  1. 服務(wù)類GreeterService,服務(wù)類集成的Greeter.GreeterBase


創(chuàng)建gRPC客戶端

添加包Google.Protobuf、Grpc.Net.Client、Grpc.Tools

項(xiàng)目文件中添加GrpcDemo.Service的proto文件

<Protobuf Include="..\GrpcDemo.Service\Protos\*.proto" GrpcServices="Client" 
Link="Protos\%(RecursiveDir)%(Filename)%(Extension)" />

Program中調(diào)用服務(wù)

using Grpc.Core;
using Grpc.Net.Client;
using GrpcDemo.Service;

//創(chuàng)建grpc通道
var channel = GrpcChannel.ForAddress("http://localhost:5274");

//創(chuàng)建Greeter服務(wù)客戶端
var greetClient = new Greeter.GreeterClient(channel);
HelloReply reply = greetClient.SayHello(new HelloRequest()
{
    Name = "Curry"
});
Console.WriteLine(reply.Message);
Console.ReadLine();

運(yùn)行程序,測(cè)試

Client發(fā)起了HTTP/2 POST請(qǐng)求,服務(wù)端返回了Hello Curry。

新增Proto文件

新增PB協(xié)議文件


由于我們已經(jīng)處理過(guò)項(xiàng)目文件,此處不需要過(guò)多處理,直接修改文件

syntax = "proto3";
option csharp_namespace = "GrpcDemo.Service";

//標(biāo)識(shí)proto文件的命名空間
package order;

//定義服務(wù)
service Order {
  //發(fā)送
  rpc GetOrderInfo (OrderReq) returns (OrderResp);
}

message OrderReq{
    string OrderNo=1;
}

message OrderResp{
    string Result=1;
}

生成解決方案后,新增Service

using Grpc.Core;
using GrpcDemo.Service;

namespace GrpcDemo.Service.Services
{
    public class OrderService : Order.OrderBase
    {
        private readonly ILogger<OrderService> _logger;
        public OrderService(ILogger<OrderService> logger)
        {
            _logger = logger;
        }

        public override Task<OrderResp> GetOrderInfo(OrderReq request, ServerCallContext context)
        {
            Console.WriteLine($"接收到參數(shù)了:{request.OrderNo}");
            return Task.FromResult(new OrderResp
            {
                Result = "獲取到的傳入?yún)?shù)是: " + request.OrderNo
            });
        }
    }
}

program中將服務(wù)增加到路由

app.MapGrpcService<OrderService>();

客戶端調(diào)用測(cè)試

調(diào)用完成,測(cè)試正常

服務(wù)流、客戶流和雙向流的使用

//定義服務(wù)
service Greeter {
  //一元
  rpc UnaryCall (ExampleRequest) returns (ExampleResponse);
  //服務(wù)流
  rpc StreamingFromServer (ExampleRequest) returns (stream ExampleResponse);
  //客戶流
  rpc StreamingFromClient (stream ExampleRequest) returns (ExampleResponse);
  //雙向流
  rpc GetOrderNO(stream  ExampleRequest) returns ( stream ExampleResponse);
}

message ExampleRequest {
    int32 pageIndex = 1;
    int32 pageSize = 2;
}

message ExampleResponse{
    string result=1;
}

具體方法實(shí)現(xiàn)

//服務(wù)流
public override async Task StreamingFromServer(ExampleRequest request, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
    int i = 0;
    while (!context.CancellationToken.IsCancellationRequested)
    {
        i++;
        await responseStream.WriteAsync(new ExampleResponse()
        {
            Result = "現(xiàn)在的數(shù)值是:" + i
        });
        await Task.Delay(TimeSpan.FromSeconds(1), context.CancellationToken);
    }
}

//客戶流
public override async Task<ExampleResponse> StreamingFromClient(IAsyncStreamReader<ExampleRequest> requestStream, ServerCallContext context)
{
    int result = 0;
    while (await requestStream.MoveNext())
    {
        result += requestStream.Current.PageSize * requestStream.Current.PageIndex;
    }
    return await Task.FromResult(new ExampleResponse() { Result = result.ToString() });
}

//雙向流
public override async Task StreamingBothWays(IAsyncStreamReader<ExampleRequest> requestStream, IServerStreamWriter<ExampleResponse> responseStream, ServerCallContext context)
{
    while (!context.CancellationToken.IsCancellationRequested && await requestStream.MoveNext())
    {
        int index = requestStream.Current.PageIndex;
        int size = requestStream.Current.PageSize;
        Console.WriteLine($"傳入?yún)?shù),PageIndex:{index},PageSize:{size}");
        if (!context.CancellationToken.IsCancellationRequested)
        {
            await responseStream.WriteAsync(new ExampleResponse()
            {
                Result = $"index*size={index * size}"
            });
        }
    }
}

客戶端調(diào)用

//服務(wù)流
async static Task StreamingFromServerTest()
{
    //創(chuàng)建gRPC通道
    var channel = GrpcChannel.ForAddress(ServiceAddress);
    //創(chuàng)建Greeter服務(wù)客戶端
    var greetClient = new Greeter.GreeterClient(channel);

    var result = greetClient.StreamingFromServer(new ExampleRequest()
    {
        PageIndex = 1,
        PageSize = 10
    });
    var cts = new CancellationTokenSource();

    var iter = result.ResponseStream; // 拿到響應(yīng)流
    int num = 0;
    try
    {
        while (await iter.MoveNext(cts.Token)) // 迭代
        {
            num++;
            Console.WriteLine(iter.Current.Result);  // 將數(shù)據(jù)寫入到文件流中
            if (num >= 5)
            {
                cts.Cancel();//調(diào)用5次后自動(dòng)取消
            }
        }
    }
    catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
    {
        Console.WriteLine("Stream cancelled.");
    }

    //輸出返回結(jié)果
    Console.WriteLine("OK");
}

//客戶流
async static Task StreamingFromClientTest()
{
    //創(chuàng)建gRPC通道
    var channel = GrpcChannel.ForAddress(ServiceAddress);
    //創(chuàng)建Greeter客戶端
    var client = new Greeter.GreeterClient(channel);
    //創(chuàng)建客戶流對(duì)象
    var clientStream = client.StreamingFromClient();
    for (var i = 1; i < 10; i++)
    {
        await clientStream.RequestStream.WriteAsync(new ExampleRequest() { PageIndex = i, PageSize = 10 });
    }
    await clientStream.RequestStream.CompleteAsync();
    var resut = await clientStream;
    Console.WriteLine($"客戶端流請(qǐng)求結(jié)果{resut.Result}");
}

//雙向流
async static Task StreamingBothWaysTest()
{
    //創(chuàng)建gRPC通道
    var channel = GrpcChannel.ForAddress(ServiceAddress);
    //創(chuàng)建Greeter客戶端
    var client = new Greeter.GreeterClient(channel);
    //創(chuàng)建雙向流對(duì)象
    var GetOrderNO = client.StreamingBothWays();
    //CancellationTokenSource 管理是否關(guān)閉流
    //CancellationTokenSource.CancelAfter() 規(guī)定時(shí)間關(guān)閉流
    //CancellationTokenSource.Cancel() 立即關(guān)閉流
    var cts = new CancellationTokenSource();
    //響應(yīng)事件
    var backTask = Task.Run(async () =>
    {
        int current = 0;
        try
        {
            //從響應(yīng)流獲取數(shù)據(jù)(cts.Token: 是否關(guān)閉流)
            while (await GetOrderNO.ResponseStream.MoveNext(cts.Token))
            {
                current++;
                var back = GetOrderNO.ResponseStream.Current;
                Console.WriteLine($"{back.Result},加載進(jìn)度{((double)current / count) * 100}%");
                if (current >= 5)
                {
                    cts.Cancel();
                }
            }
        }
        catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
        {
            Console.WriteLine("Stream cancelled.");
        }
    });

    for (int i = 0; i < count; i++)
    {
        await GetOrderNO.RequestStream.WriteAsync(new ExampleRequest()
        {
            PageIndex = i,
            PageSize = 10
        });
    }

    //等待發(fā)送完成
    await GetOrderNO.RequestStream.CompleteAsync();

    Console.WriteLine("等待加載...");

    //等待響應(yīng)完成
    await backTask;

    Console.WriteLine("已全部加載完畢");
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • grpc 整理(nodejs) gRPC 是什么? 在 gRPC 里客戶端應(yīng)用可以像調(diào)用本地對(duì)象一樣直接調(diào)用另一臺(tái)...
    秋楓殘紅閱讀 1,849評(píng)論 0 2
  • 一、grpc的golang環(huán)境配置 protoc的安裝比較簡(jiǎn)單,golang使用protoc-gen-go進(jìn)行pr...
    羅文才閱讀 2,717評(píng)論 0 0
  • RPC 遠(yuǎn)程過(guò)程調(diào)用 可以區(qū)別于IPC A想要調(diào)用B服務(wù)器上的提供的函數(shù)/方法 單一 RPC 無(wú)法實(shí)現(xiàn) push,...
    轉(zhuǎn)身是天涯閱讀 6,044評(píng)論 3 5
  • 核心工作 在一個(gè) .proto 文件內(nèi)定義服務(wù). 用 protocol buffer 編譯器生成服務(wù)器和客戶端代碼...
    迷糊銀兒閱讀 7,706評(píng)論 0 1
  • 一、gRPC簡(jiǎn)介 gRPC(Remote Produce Call)是google開(kāi)發(fā)的一個(gè)高性能、開(kāi)源的通...
    Software泥瓦匠閱讀 850評(píng)論 0 0

友情鏈接更多精彩內(nèi)容