gRPC的四種調用方式

上一篇 介紹了gRPC的使用場景及基本使用,本文將介紹gRPC的四種調用方式。

一元調用

普通RPC調用,客戶端帶一個請求對象進行調用,服務端返回一個響應對象。

  • proto
syntax = "proto3";

option csharp_namespace = "GrpcDemoServices";

package Greet;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply);
  rpc SayHellos (HelloRequest) returns (stream HelloReply);
}

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

// The response message containing the greetings.
message HelloReply {
  string message = 1;
}

  • Client C# Code
private static async Task UnaryStreamCallExample(Greeter.GreeterClient client)
{
    var reply = await client.SayHelloAsync(new HelloRequest { Name = "張三" });
    Console.WriteLine(reply.Message);
}
  • Server C# Code
public class GreeterService : Greeter.GreeterBase
{
    //private readonly ILogger<GreeterService> _logger;
    //public GreeterService(ILogger<GreeterService> logger)
    private readonly ILogger _logger;
    public GreeterService(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<GreeterService>();
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        _logger.LogInformation($"Sending hello to {request.Name}");
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

服務端流式調用

客戶端帶一個請求對象進行調用,服務端返回多個響應對象

proto同上

  • Client C# Code
private static async Task ServerStreamingCallExample(Greeter.GreeterClient client)
{
    var cts = new CancellationTokenSource();
    cts.CancelAfter(TimeSpan.FromSeconds(3.0));

    using (var call = client.SayHellos(new HelloRequest { Name = "張三" }, cancellationToken: cts.Token))
    {
        try
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                Console.WriteLine($"reply:{response.Message}");
            }
        }
        catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
        {
            Console.WriteLine("Stream cancelled.");
        }
    }
}
  • Server C# Code
public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger _logger;
    public GreeterService(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<GreeterService>();
    }

    public override async Task SayHellos(HelloRequest request, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        var i = 0;
        while (!context.CancellationToken.IsCancellationRequested)
        {
            var message = $"How are you {request.Name}?{++i}";
            await responseStream.WriteAsync(new HelloReply { Message = message });
            await Task.Delay(1000);
        }
    }
}

客戶端流式調用

客戶端帶有多個對象進行請求,服務端返回一個響應對象

  • proto
syntax = "proto3";

option csharp_namespace = "GrpcDemoServices";

package Count;

service Counter{
    // Increment count through multiple counts
    rpc AccumulateCount (stream CounterRequest) returns (CounterReply);
}

// The reqeust message containing the count to increment by
message CounterRequest {
    int32 count = 1;
}

// The response message containing the current count
message CounterReply {
    int32 count = 1;
}
  • Client C# Code
private static async Task ClientStreamingCallExample(Counter.CounterClient client)
{
    using (var call = client.AccumulateCount())
    {
        for (int i = 0; i < 3; i++)
        {
            var count = RNG.Next(5);
            Console.WriteLine($"Accumulating with {count}");
            await call.RequestStream.WriteAsync(new CounterRequest { Count = count });
            await Task.Delay(2000);
        }
        await call.RequestStream.CompleteAsync();

        var response = await call;
        Console.WriteLine($"Count:{response.Count}");
    }
}
  • Server C# Code
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IncrementingCounter>(); // Configure IncrementingCounter as a singleton
}
public class IncrementingCounter
{
    public int Count { get; private set; }

    public void Increment(int amount)
    {
        Count += amount;
    }
}
public class CounterService : Counter.CounterBase
{
    private readonly ILogger _logger;
    private readonly IncrementingCounter _counter;

    public CounterService(IncrementingCounter counter, ILoggerFactory loggerFactory)
    {
        _counter = counter;
        _logger = loggerFactory.CreateLogger<CounterService>();
    }

    public override async Task<CounterReply> AccumulateCount(IAsyncStreamReader<CounterRequest> requestStream, ServerCallContext context)
    {
        await foreach (var request in requestStream.ReadAllAsync())
        {
            _logger.LogInformation($"Incrementing count by {request.Count}");
            _counter.Increment(request.Count);
        }
        return new CounterReply { Count = _counter.Count };
    }
}

雙向流式調用

客戶端帶多個請求對象進行調用,服務端返回多個響應對象

  • proto
syntax = "proto3";
option csharp_namespace = "GrpcDemoServices";
package Race;

service Racer {
  rpc ReadySetGo (stream RaceMessage) returns (stream RaceMessage);
}

message RaceMessage {
  int32 count = 1;
}
  • Client C# Code
private static async Task BidirectionalStreamingExample(Racer.RacerClient client)
{
    var headers = new Metadata { new Metadata.Entry("race-duration", RaceDuration.ToString()) };

    Console.WriteLine("Ready, set, go!");
    using (var call = client.ReadySetGo(new CallOptions(headers)))
    {

        // Read incoming messages in a background task
        RaceMessage? lastMessageReceived = null;
        var readTask = Task.Run(async () =>
        {
            await foreach (var message in call.ResponseStream.ReadAllAsync())
            {
                lastMessageReceived = message;
            }
        });

        // Write outgoing messages until timer is complete
        var sw = Stopwatch.StartNew();
        var sent = 0;
        while (sw.Elapsed < RaceDuration)
        {
            await call.RequestStream.WriteAsync(new RaceMessage { Count = ++sent });
        }

        // Finish call and report results
        await call.RequestStream.CompleteAsync();
        await readTask;

        Console.WriteLine($"Messages sent: {sent}");
        Console.WriteLine($"Messages received: {lastMessageReceived?.Count ?? 0}");
    }
}
  • Server C# Code
public class RacerService : Racer.RacerBase
{
    public override async Task ReadySetGo(IAsyncStreamReader<RaceMessage> requestStream, IServerStreamWriter<RaceMessage> responseStream, ServerCallContext context)
    {
        var raceDuration = TimeSpan.Parse(context.RequestHeaders.Single(h => h.Key == "race-duration").Value);

        // Read incoming messages in a background task
        RaceMessage? lastMessageReceived = null;
        var readTask = Task.Run(async () =>
        {
            await foreach (var message in requestStream.ReadAllAsync())
            {
                lastMessageReceived = message;
            }
        });

        // Write outgoing messages until timer is complete
        var sw = Stopwatch.StartNew();
        var sent = 0;
        while (sw.Elapsed < raceDuration)
        {
            await responseStream.WriteAsync(new RaceMessage { Count = ++sent });
        }

        await readTask;
    }
}

所有代碼均可在Github倉庫下找到,本文中的代碼在GrpcClientGrpcServer目錄下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章