基於C#的GRPC

韩梦芫發表於2024-04-19

gRPC(gRPC Remote Procedure Call)是由Google開發的高效能、跨語言的遠端過程呼叫框架。它基於HTTP/2協議進行通訊,支援多種程式語言,包括C++, C#, Java, Python等,使不同語言的應用程式可以透過遠端呼叫相互通訊。

1.關鍵特點和用途:
高效能:gRPC採用了HTTP/2協議,具有低延遲、高吞吐量和複用連線的特點。這使得它非常適合於需要高效能通訊的應用程式。
跨語言支援:gRPC支援多種程式語言,允許不同語言的應用程式之間進行跨語言通訊。這使得它在微服務架構中非常有用,因為不同的服務可以使用不同的程式語言實現。
IDL(Interface Definition Language):gRPC使用ProtoBuf(Protocol Buffers)作為IDL,允許您定義服務介面和訊息型別。這提供了強型別的通訊,使得通訊更加清晰和高效。
多種通訊型別:gRPC支援不同型別的通訊,包括請求-響應、伺服器流、客戶端流和雙向流。這允許您選擇最適合您應用程式需求的通訊方式。
自動化程式碼生成:透過ProtoBuf定義服務介面和訊息型別,gRPC工具會自動生成客戶端和伺服器端的程式碼,這樣可以大大減少開發工作量。
認證和安全:gRPC支援各種認證和安全機制,包括SSL/TLS,OAuth等,以確保通訊的安全性。
服務發現:gRPC可以與服務發現工具(如Consul、etcd)整合,從而實現動態服務發現和負載均衡。
可擴充套件性:gRPC是一個可擴充套件的框架,支援各種自定義擴充套件和中介軟體。
2.GRPC思路
Grpc類似於一種協議,遵循網路通訊,以.proto檔案為協議模板進行客戶端與服務端的互動開發,不限制客戶端和服務端的程式碼語言風格.也可以在服務端與客戶端使用不同的語言開發

3.編寫.proto檔案

// Protocol Buffers 語法版本 proto3 版本
syntax = "proto3";
// 定義了訊息型別和服務的包名,類似於名稱空間,用於避免命名衝突。
package Calculator;
// 定義了一個 gRPC 服務。在大括號中,您可以列出服務方法的定義。
service CalculatorService 
{
    // 定義了一個服務方法。rpc 表示定義一個遠端過程呼叫(RPC)方法。MyMethod 是方法的名稱,MyRequest 是輸入引數型別,MyResponse 是輸出引數型別。
    rpc MyMethod (MyRequest) returns (MyResponse);

  // 伺服器流式方法
  rpc ServerStreamingMethod(Request1) returns (stream Response1);

  // 客戶端流式方法
  rpc ClientStreamingMethod(stream Request2) returns (Response2);

  // 雙向流式方法
  rpc BidirectionalStreamingMethod(stream Request3) returns (stream Response3);
}
message MyRequest 
{
    repeated int32 num = 10;
}
message MyResponse 
{
    repeated string strs = 10;
}

message Request1
{
    string Message = 1;
}
message Response1
{
    string Message = 1;
}
message Request2
{
    string Message = 1;
}
message Response2
{
    string Message = 1;
}
message Request3
{
    string Message = 1;
}
message Response3
{
    string Message = 1;
}
syntax = "proto3";

package Calculator;

import "Protos/Calculator.proto";

service FirstService {
    rpc MyMethod (MyRequest1) returns (MyResponse);
}
message MyRequest1 
{
    repeated int32 num = 10;
}

多個.proto檔案間是可以呼叫的

4.新增.proto檔案生成的檔案編寫服務端和客戶端程式碼

.proto檔案生成的檔案位置 新增到客戶端和服務端

5.服務端程式碼

using Calculator;
using Grpc.Core;

class CalculatorServiceImpl : CalculatorService.CalculatorServiceBase
{
    /// <summary>
    /// 釋出響應
    /// </summary>
    /// <param name="request"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task<MyResponse> MyMethod(MyRequest request, ServerCallContext context)
    {
        MyResponse myResponse = new MyResponse();
        foreach (int i in request.Num)
        {
            myResponse.Strs.Add(i.ToString());
        }
        return myResponse;
    }

    /// <summary>
    /// 伺服器流(Server Streaming)
    /// </summary>
    /// <param name="request"></param>
    /// <param name="responseStream"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task ServerStreamingMethod(Request1 request, IServerStreamWriter<Response1> responseStream, ServerCallContext context)
    {
        for (int i = 0; i < 10; i++)
        {
            Response1 response = new Response1 { Message = request.Message + $"Message {i}" };
            await responseStream.WriteAsync(response);
            await Task.Delay(500); // 模擬每秒傳送一次資料
        }
    }

    /// <summary>
    /// 客戶端流(Client Streaming)
    /// </summary>
    /// <param name="requestStream"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task<Response2> ClientStreamingMethod(IAsyncStreamReader<Request2> requestStream, ServerCallContext context)
    {
        string str = "";
        while (requestStream.MoveNext().Result)
        {
            str += requestStream.Current.Message;
            Console.WriteLine(requestStream.Current.Message);
        }
        return new Response2 { Message = str };
    }
    /// <summary>
    /// 雙向流(Bidirectional Streaming)
    /// </summary>
    /// <param name="requestStream"></param>
    /// <param name="responseStream"></param>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task BidirectionalStreamingMethod(IAsyncStreamReader<Request3> requestStream, IServerStreamWriter<Response3> responseStream, ServerCallContext context)
    {
        while (requestStream.MoveNext().Result)
        {
            // 處理客戶端傳送的請求
            Console.WriteLine(requestStream.Current.Message);
            Response3 response = new Response3 { Message = requestStream.Current.Message + "abc" };
            await responseStream.WriteAsync(response); // 傳送響應
            await Task.Delay(500);
        }
    }

}

class Program
{
    static void Main(string[] args)
    {
        const int Port = 50051;

        Server server = new Server
        {
            Services = { CalculatorService.BindService(new CalculatorServiceImpl()) },
            Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
        };

        server.Start();

        Console.WriteLine("Calculator server listening on port " + Port);
        Console.WriteLine("Press any key to stop the server...");
        Console.ReadKey();

        server.ShutdownAsync().Wait();
    }
}

6.客戶端程式碼

using Calculator;
using Grpc.Core;

class Program
{
    const string ServerAddress = "localhost";
    const int Port = 50051;
    static void Main(string[] args)
    {
        MyMethod();
        ServerStreamingMethod();
        SelfIncreaseClient();
        BidirectionalStreamingMethod();

        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
    /// <summary>
    /// 釋出響應
    /// </summary>
    private static void MyMethod()
    {
        Channel channel = new Channel($"{ServerAddress}:{Port}", ChannelCredentials.Insecure);
        var client = new CalculatorService.CalculatorServiceClient(channel);
        MyRequest myRequest = new MyRequest { Num = { 1, 2, 3, 4 } };
        MyResponse myResponse = client.MyMethod(myRequest);
        Console.WriteLine($"Result: {myResponse.Strs}");
        channel.ShutdownAsync().Wait();
    }

    /// <summary>
    /// 伺服器流(Server Streaming)
    /// </summary>
    private static void ServerStreamingMethod()
    {
        Channel channel = new Channel($"{ServerAddress}:{Port}", ChannelCredentials.Insecure);
        var client = new CalculatorService.CalculatorServiceClient(channel);
        Request1 request1 = new Request1 { Message = "ceshi" };
        AsyncServerStreamingCall<Response1> response1s = client.ServerStreamingMethod(request1);
        while (response1s.ResponseStream.MoveNext().Result)
        {
            Console.WriteLine(response1s.ResponseStream.Current.Message);
        }
        channel.ShutdownAsync().Wait();
    }

    /// <summary>
    /// 客戶端流(Client Streaming)
    /// </summary>
    private static async void SelfIncreaseClient()
    {
        Channel channel = new Channel($"{ServerAddress}:{Port}", ChannelCredentials.Insecure);
        var client = new CalculatorService.CalculatorServiceClient(channel);
        var call = client.SelfIncreaseClient();
        for (int i = 0; i < 10; i++)
        {
            await call.RequestStream.WriteAsync(new Request2() { Message = $"第{i}個" });
            await Task.Delay(500);
        }
        await call.RequestStream.CompleteAsync();
        Console.WriteLine($"Result: {call.ResponseAsync.Result.Message}");
        channel.ShutdownAsync().Wait();
    }
    /// <summary>
    /// 雙向流(Bidirectional Streaming)
    /// </summary>
    private static async void BidirectionalStreamingMethod()
    {
        Channel channel = new Channel($"{ServerAddress}:{Port}", ChannelCredentials.Insecure);
        var client = new CalculatorService.CalculatorServiceClient(channel);
        var call = client.BidirectionalStreamingMethod();
        for (int i = 1; i <= 5; i++)
        {
            // 傳送請求
            await call.RequestStream.WriteAsync(new Request3 { Message = i.ToString() }); 
            await Task.Delay(500);
        }
        await call.RequestStream.CompleteAsync();

        while (call.ResponseStream.MoveNext().Result)
        {
            // 處理伺服器傳送的響應
            Console.WriteLine(call.ResponseStream.Current.Message);
        }
        channel.ShutdownAsync().Wait();
    }
}

7.Demo示例

原文連結:https://blog.csdn.net/qq1084517825/article/details/134272985

相關文章