簡體   English   中英

一台用於一元和雙向 stream 的 grpc 服務器,帶 netty

[英]one grpc server for both unary and bidi stream with netty

我需要在 java 中實現一個 grpc 服務器,它能夠處理grpc 一元和雙向流。使用 grpc bidi-streaming 的服務每秒可以發送大量消息。(可能每 2000 條消息第二個或更多)我有兩種實現方式,有點困惑哪一種最適合我的要求。

1. grpc 一元和 grpc 雙向使用相同的服務器。

使用這種方法時,由於grpc unary 和bidi stream 使用相同的端口,因此將為unary 和bidi stream 分配一個凸台線程。 所以我不確定在雙向流每秒接收大量消息的情況下它的性能如何。 (我的意思是老板線程是否會忙於雙向流並且對一元不可用)

final EventLoopGroup bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
    final EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);
    int blockingQueueLength = 1000;
    final BlockingQueue blockingQueue = new LinkedBlockingQueue(blockingQueueLength);
    final Executor executor = new ThreadPoolExecutor(400, 500, 30, TimeUnit.SECONDS, blockingQueue);
    Server server = NettyServerBuilder.forPort(PORT).maxConcurrentCallsPerConnection(50)
            .keepAliveTime(60, TimeUnit.SECONDS).bossEventLoopGroup(bossGroup)
            .workerEventLoopGroup(workerGroup).addService(new ExtAuthService()).addService(new RateLimitService())
            .channelType(NioServerSocketChannel.class)
            .executor(executor).build();
            
    try{
        server.start();
        erver.awaitTermination();
    }catch(Exception e){
        Logger.Error("Execption", new Error(e));
    }

2. 使用兩台服務器,一台用於 grpc 一元,一台用於 grpc bidi 流。

這里不存在前面提到的問題,因為我們為每個 grpc 一元和 bidi stream 分配了 2 個老板線程。 但是對於我使用的服務,我使用的是使用 java ThreadPoolExecutor 的執行器,我的問題是我應該為使用 grpc 一元和雙向流的兩個服務使用 2 個線程池嗎?

final EventLoopGroup bossGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
    final EventLoopGroup workerGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2);
    int blockingQueueLength = 1000;
    final BlockingQueue blockingQueue = new LinkedBlockingQueue(blockingQueueLength);
    final Executor executor = new ThreadPoolExecutor(400, 500, 30, TimeUnit.SECONDS, blockingQueue);

    // I have used here the same executor for both servers. 
    Server server1 = NettyServerBuilder.forPort(PORT_1).maxConcurrentCallsPerConnection(50)
            .keepAliveTime(60, TimeUnit.SECONDS).bossEventLoopGroup(bossGroup)
            .workerEventLoopGroup(workerGroup).addService(new ExtAuthService())
            .channelType(NioServerSocketChannel.class)
            .executor(executor).build();

    Server server2 = NettyServerBuilder.forPort(PORT_2).maxConcurrentCallsPerConnection(50)
            .keepAliveTime(60, TimeUnit.SECONDS).bossEventLoopGroup(bossGroup)
            .workerEventLoopGroup(workerGroup).addService(new RateLimitService())
            .channelType(NioServerSocketChannel.class)
            .executor(executor).build();
            
    try{
        server1.start();
        server2.start();
        server1.awaitTermination();
        server2.awaitTermination();
    }catch(Exception e){
        Logger.Error("Execption", new Error(e));
    }

使用單個服務器。

boss線程僅用於accept()ing新連接。 它不用於實際處理。 這是由工作人員事件循環完成的。 每個連接都分配給一個事件循環,一個事件循環可以服務多個連接。

在單個 stream 上,Netty 每秒可以處理 100k 條消息。 但這實際上很慢。 查找消息邊界由與傳遞消息不同的線程處理,並且這兩個線程之間的通信會增加延遲。 增加的延遲會減慢速度。 借助避免延遲的requests(5)技巧,單個 Netty stream 每秒可以處理 1250k 條消息。 (這些性能數字會因您運行它們的機器而異,但它們顯然比您需要的要高得多。)請參閱https://github.com/grpc/grpc-java/issues/6696 ,其中討論了延遲問題.

但是,假設您需要更高的性能,或者想要將一元流量與流式流量分開。 在這種情況下,我們建議使用兩個不同的渠道 每個通道將使用自己的連接和(可能)單獨的工作事件循環。

只有當您非常擔心延遲時,您才應該費心將兩種類型的流量分成不同的服務器。 (這樣您也有基准來顯示它有多大幫助。)是的,使用帶有自己的workerEventLoopGroup()的單獨服務器和通道(在通道上,默認情況下,通道使用共享事件循環組)。 線程數量有限,因此每個線程都可以擁有自己的處理器內核進行處理; 但我預計這將是一種罕見的情況。 您很快就會接近要將服務器二進制文件分成兩部分以避免 GC 和服務之間類似的相互作用的地步。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM