简体   繁体   English

在 Net Core 3.0 中同时使用 GRPC 通道

[英]An using GRPC Channel concurrently in Net Core 3.0

According the documentation , a gRPC client is created from a channel.根据文档,gRPC 客户端是从通道创建的。 Multiple concrete gRPC clients can be created from a channel, including different types of clients, but I didn't find any information about concurrency.可以从一个通道创建多个具体的 gRPC 客户端,包括不同类型的客户端,但我没有找到任何有关并发的信息。

So, my question, Can I use the channel for concurrent calls like below?所以,我的问题是,我可以使用该通道进行如下并发呼叫吗?

var channel = GrpcChannel.ForAddress("https://localhost:5001");

// the first task
Task.Run(() => {
     var client = new Greet.GreeterClient(channel);
     var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });

     Console.WriteLine("Greeting: " + response.Message); 
 });
// the second task
Task.Run(() => {
     var client = new Greet.GreeterClient(channel);
     var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });

     Console.WriteLine("Greeting: " + response.Message); 
 });

Or I need to create own channel for each thread(task).或者我需要为每个线程(任务)创建自己的通道。

As @Mike has noted, The GrpcChannel is using HttpClient under the hood.正如@Mike 所指出的,GrpcChannel 在后台使用 HttpClient。 So, according this , we probably can create a single http client and reuse it throughout the life of an application.因此,根据这个,我们可能可以创建单个 http 客户端并在应用程序的整个生命周期中重用它。

HttpClient is intended to be instantiated once and re-used throughout the life of an application. HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重复使用。

I have built a simple example to investigate the area.我已经建立了一个简单的示例来调查该区域。

GRPS service: GRPS服务:

public class GreeterService
    : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    //Server side handler of the SayHello RPC
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        _logger.LogInformation($"Sending hello to {request.Name}");
        Thread.Sleep(500);
        return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
    }
}

The single channel for all threads所有线程的单一通道

        string uri = $"https://localhost:5001";

        var channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions()
        {
            HttpClient = HttpClientFactory.Create(),
            DisposeHttpClient = true
        });

        int threadCount = 6;
        Thread[] threads = new Thread[threadCount];

        for(int i = 0; i < threadCount; ++i)
        {
            threads[i] = new Thread((index) =>
            {
                Console.WriteLine($"Thread({(int)index}) has been started!");
                for (int req = 0; req < 75; ++req)
                {
                    var client = new Greeter.GreeterClient(channel);
                    client.SayHello(new HelloRequest()
                    {
                        Name = $"Thread {(int)index}"
                    });
                }

                Console.WriteLine($"Thread({(int)index}) has been finished!");
            });
        }

        for (int i = 0; i < threadCount; ++i)
        {
            threads[i].Start(i);
        }

        for (int i = 0; i < threadCount; ++i)
        {
            threads[i].Join();
        }

Own channel for each thread每个线程都有自己的频道

        string uri = $"https://localhost:5001";

        int threadCount = 6;
        Thread[] threads = new Thread[threadCount];

        for(int i = 0; i < threadCount; ++i)
        {
            threads[i] = new Thread((index) =>
            {
                var channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions()
                {
                    HttpClient = HttpClientFactory.Create(),
                    DisposeHttpClient = true
                });

                Console.WriteLine($"Thread({(int)index}) has been started!");
                for (int req = 0; req < 75; ++req)
                {
                    var client = new Greeter.GreeterClient(channel);
                    client.SayHello(new HelloRequest()
                    {
                        Name = $"Thread {(int)index}"
                    });
                }

                Console.WriteLine($"Thread({(int)index}) has been finished!");
            });
        }

        Thread.Sleep(1000 * 10);

        for (int i = 0; i < threadCount; ++i)
        {
            threads[i].Start(i);
        }

        for (int i = 0; i < threadCount; ++i)
        {
            threads[i].Join();
        }

Summary, I can make a conclusion that we can create a single GRPC channel and reuse it in the app.总结,我可以得出一个结论,我们可以创建一个单一的 GRPC 通道并在应用程序中重用它。 But I don't know how effective using one channel for many request is但我不知道使用一个渠道处理多个请求的效果如何

i see that the question is answered but I wanted to add that there are sections in docs about client concurrency.我看到这个问题得到了回答,但我想补充一点,文档中有关于客户端并发的部分。 If someone new read this question i hope this will be helpful.如果有人新阅读了这个问题,我希望这会有所帮助。

Microsoft docs微软文档

Introduction to gRPC on .NET Core .NET Core 上的 gRPC 介绍

Chapter " Reuse gRPC channels " on gRPC - Performance and practices .关于gRPC 性能和实践的“重用 gRPC 通道”一章。

Channels are safe to share and reuse between gRPC calls:通道可以安全地在 gRPC 调用之间共享和重用:

  • gRPC clients are created with channels. gRPC 客户端是使用通道创建的。 gRPC clients are lightweight objects and don't need to be cached or reused. gRPC 客户端是轻量级对象,不需要缓存或重用。
  • Multiple gRPC clients can be created from a channel, including different types of clients.可以从一个通道创建多个 gRPC 客户端,包括不同类型的客户端。
  • A channel and clients created from the channel can safely be used by multiple threads.一个通道和从通道创建的客户端可以被多个线程安全地使用。
  • Clients created from the channel can make multiple simultaneous calls.从通道创建的客户端可以同时进行多个调用。

" Connection concurrency " chapter on this topic is also very helpful. 连接并发”一章对这个主题也很有帮助。

NOTE: Look topic Configuration for configuring services and client channels.注意:查看主题配置以配置服务和客户端通道。

gRPC API Documentation gRPC API 文档

Namespace Grpc.Net.Client 命名空间 Grpc.Net.Client

GrpcChannel GrpcChannel

Represents a gRPC channel.表示一个 gRPC 通道。 Channels are an abstraction of long-lived connections to remote servers.通道是与远程服务器的长期连接的抽象。 Client objects can reuse the same channel.客户端对象可以重用相同的通道。 Creating a channel is an expensive operation compared to invoking a remote call so in general you should reuse a single channel for as many calls as possible.与调用远程调用相比,创建通道是一项昂贵的操作,因此通常您应该为尽可能多的调用重用单个通道。

gRPC Documentation gRPC 文档

There are no specifics about client concurrency, but Core concepts topic is also very helpful by giving glimpses on how gRPC works underhood.没有关于客户端并发的细节,但核心概念主题也非常有帮助,让我们了解 gRPC 在底层是如何工作的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM