GRPC與.net core

老六程式碼發表於2019-07-06

系列章節

GRPC與.net core

GRPC截止時間與後設資料

GRPC與netcore Identity

GRPC與netcore IdentityServer4

概述

GRPC的資料互動模式有:

1.單項RPC,最簡單的資料交換方式,客戶端發出單個請求,收到單個響應

2.服務端流式RPC,是在服務端收到客戶端的請求之後,返回一個應答流,客戶端收到流之後處理。

3.客戶端流式RPC,與單項類似,但客戶端傳送的是流式RPC

4.雙向流式RPC,呼叫由客戶端呼叫方法來初始化,而服務端則接收到客戶端的後設資料,方法名和截止時間。服務端可以選擇傳送回它的初始後設資料或等待客戶端傳送請求。下一步怎樣發展取決於應用,因為客戶端和服務端能在任意順序上讀寫 - 這些流的操作是完全獨立的。例如服務端可以一直等直到它接收到所有客戶端的訊息才寫應答,或者服務端和客戶端可以像"乒乓球"一樣:服務端後得到一個請求就回送一個應答,接著客戶端根據應答來傳送另一個請求,以此類推。

單項RPC較簡單不做示例了。

首先在vs2019中net core3.0中新建GRPC專案。然後定義響應的proto檔案,根據proto檔案生成響應的服務端與客戶端程式碼。

1.服務端流式RPC

1.定義 protofile

syntax = "proto3";

option csharp_namespace = "GrpcGreeter";

package Greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc GetStreamContent (StreamRequest) returns (stream StreamContent) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}
message StreamRequest {
  string fileName = 1;
}
message StreamContent {
  bytes content = 1;
}

2.實現服務端Service

重新生成專案,然後實現GetStreamContent,簡單的讀取檔案內容,並將內容返回給Client

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;

namespace GrpcGreeter
{
    public class GreeterService : Greeter.GreeterBase
    {
        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = "Hello " + request.Name
            });
        }

        public override Task GetStreamContent(StreamRequest request, IServerStreamWriter<StreamContent> responseStream, ServerCallContext context)
        {
            return Task.Run(async () =>
             {
                 using (var fs = File.Open(request.FileName, FileMode.Open)) // 從 request 中讀取檔名並開啟檔案流
                 {
                     var remainingLength = fs.Length; // 剩餘長度
                     var buff = new byte[1048576]; // 緩衝區,這裡我們設定為 1 Mb
                     while (remainingLength > 0) // 若未讀完則繼續讀取
                     {
                         var len = await fs.ReadAsync(buff); // 非同步從檔案中讀取資料到緩衝區中
                         remainingLength -= len; // 剩餘長度減去剛才實際讀取的長度

                         // 向流中寫入我們剛剛讀取的資料
                         await responseStream.WriteAsync(new StreamContent
                         {
                             Content = ByteString.CopyFrom(buff, 0, len)
                         });
                     }
                 }
             });
        }
    }
}

 

3.實現Client

新建一個netcore 3.0的Console專案,並引入Nuget包

Install-Package Grpc.Net.Client -Version 0.1.22-pre1
Install-Package Google.Protobuf -Version 3.8.0
Install-Package Grpc.Tools -Version 1.22.0

 

編輯專案檔案,修改如下節點

<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />

 

重新生成專案,Client端主要實現傳送請求,請求是一個伺服器端的檔案路徑。然後實現接收服務端的流,並儲存到Client本地。

using Grpc.Net.Client;
using GrpcGreeter;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;

namespace GrpcGreeterClient
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            AppContext.SetSwitch(
                "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",
                true);
            var httpClient = new HttpClient();
            // The port number(50051) must match the port of the gRPC server.
            httpClient.BaseAddress = new Uri("http://localhost:50051");
            var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
            //
            var reply = await client.SayHelloAsync(
                              new HelloRequest { Name = "GreeterClient" });
            Console.WriteLine("Greeting: " + reply.Message);
            Console.ReadKey();

            //
            var result = client.GetStreamContent(new StreamRequest { FileName = @"D:\Docs.zip" });  //傳送請求
            var iter = result.ResponseStream;
            using (var fs = new FileStream(@"D:\Docs2.zip", FileMode.Create)) // 新建一個檔案流用於存放我們獲取到資料
            {
                while (await iter.MoveNext()) // 迭代
                {
                    iter.Current.Content.WriteTo(fs); // 將資料寫入到檔案流中
                }
            }

            Console.ReadKey();
        }
    }
}

 

檔案生成成功

 2.客戶端流式RPC

1.定義 protofile

syntax = "proto3";

option csharp_namespace = "GRPC.TEST";

package Greet;

// The greeting service definition.
service Greeter {
  rpc getResult (stream Value) returns (Result) {}
}


//定義Value訊息型別,用於客戶端訊息
message Value {
    int32 value = 1;
}
//定義Result訊息型別,包含總和,數字數量和平均值,用於服務端訊息返回
message Result {
    int32 sum = 1;
    int32 cnt = 2;
    double avg = 3;
}

 

2.實現服務端Service

重新生成專案,並實現如下

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;

namespace GRPC.TEST
{
    public class GreeterService : Greeter.GreeterBase
    {
        public override async Task<Result> getResult(IAsyncStreamReader<Value> requestStream, ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var point = requestStream.Current;
            }
            return new Result { Sum = 1 };
        }
    }
}

 

3.實現Client

新建一個netcore 3.0的Console專案,並引入Nuget包,安裝nuget包與其他操作同上一個例子,實現程式碼如下

using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;

namespace GRPC.TEST.CLIENT
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            AppContext.SetSwitch(
                 "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",
                 true);
            var httpClient = new HttpClient();
            // The port number(50051) must match the port of the gRPC server.
            httpClient.BaseAddress = new Uri("http://localhost:50051");
            var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
            using (var call = client.getResult())
            {
                await call.RequestStream.WriteAsync(new Value { Value_ = 1 });
                await call.RequestStream.CompleteAsync();
                var response = await call.ResponseAsync;
            }

            Console.ReadKey();
        }
    }
}

 

 3.雙向流式RPC

1.定義proto

syntax = "proto3";

option csharp_namespace = "GRPC.TEST";

package Greet;

// The greeting service definition.
service Greeter {
  rpc getResult (stream Value) returns (stream Result) {}
}


//定義Value訊息型別,用於客戶端訊息
message Value {
    int32 value = 1;
}
//定義Result訊息型別,包含總和,數字數量和平均值,用於服務端訊息返回
message Result {
    int32 sum = 1;
    int32 cnt = 2;
    double avg = 3;
}

 

2.服務端實現

重新生成專案,並實現如下

public override async Task getResult(IAsyncStreamReader<Value> requestStream, IServerStreamWriter<Result> responseStream, ServerCallContext context)
        {
            while (await requestStream.MoveNext())
            {
                var note = requestStream.Current;

                await responseStream.WriteAsync(new Result { Sum = 100 });
            }
        }

 

3.客戶端程式碼

新建一個netcore 3.0的Console專案,並引入Nuget包,安裝nuget包與其他操作同上一個例子,實現程式碼如下

using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

namespace GRPC.TEST.CLIENT
{
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            AppContext.SetSwitch(
       "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",
       true);
            var httpClient = new HttpClient();
            // The port number(50051) must match the port of the gRPC server.
            httpClient.BaseAddress = new Uri("http://localhost:50051");
            var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
            using (var call = client.getResult())
            {
                var responseReaderTask = Task.Run(async () =>
                {
                    while (await call.ResponseStream.MoveNext())
                    {
                        var note = call.ResponseStream.Current;
                        Console.WriteLine("Received " + note);
                    }
                });

                await call.RequestStream.WriteAsync(new Value { Value_ = 12 });
                await call.RequestStream.CompleteAsync();
                await responseReaderTask;
            }

            Console.ReadKey();
        }
    }
}

 

至此,GRPC的幾種資料互動分享完畢

相關文章