【问题标题】:one grpc server for both unary and bidi stream with netty一个用于一元和双向流的 grpc 服务器,带有 netty
【发布时间】:2021-05-01 02:39:33
【问题描述】:

我需要在 java 中实现一个 grpc 服务器,它能够处理 grpc 一元和双向流。使用 grpc bidi-streaming 的服务可以发送大量每秒消息数。(可能每秒 2000 条消息或更多)我有两种实现方式,有点困惑哪一种最适合我的要求。

1.为 grpc 一元和 grpc 双向使用相同的服务器。

当使用这种方法时,由于grpc unary 和bidi stream 使用相同的端口,一个boss thread 将被分配给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 双向流。

这里不存在前面提到的问题,因为我们为每个 grpc 一元和双向流分配了 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));
    }

【问题讨论】:

  • 您是否考虑过针对这两种实现编写任何压力测试?
  • 是的,想过这样做,但没有时间这样做。会做一些性能测试,看看。
  • @drfloob 但我想这更像是一个常见的用例。所以应该有一个标准的方法来做到这一点?

标签: java netty grpc http2 grpc-java


【解决方案1】:

使用单个服务器。

boss线程只用于accept()ing新连接。它不用于实际处理。这是由工作人员事件循环完成的。每个连接都分配给一个事件循环,一个事件循环可以服务多个连接。

在单个流上,Netty 每秒可以处理 100k 条消息。但这实际上很慢。查找消息边界由与传递消息不同的线程处理,并且这两个线程之间的通信会增加延迟。增加的延迟会减慢速度。通过避免延迟的requests(5) 技巧,单个 Netty 流每秒可以处理 1250k 条消息。 (这些性能数据会因您运行它们的机器而异,但它们显然比您需要的要高得多。)请参阅讨论延迟问题的https://github.com/grpc/grpc-java/issues/6696

但是假设您需要更高的性能,或者想要将一元流量与流式流量分开。在这种情况下,我们建议使用两个不同的渠道。每个通道都将使用自己的连接和(可能)独立的工作事件循环。

只有当您非常担心延迟时,您才应该费心将两种类型的流量分成不同的服务器。 (这样你也有基准来显示它有多大帮助。)是的,使用单独的服务器和通道和他们自己的workerEventLoopGroup()s(在通道上也是如此!通道默认使用共享事件循环组),有限线程数,因此每个线程都可以有自己的处理器内核进行处理。但我希望这是一种罕见的情况。您很快就会接近要将服务器二进制文件一分为二以避免 GC 和服务之间类似的相互作用的地步。

【讨论】:

  • 非常感谢,这是有道理的。顺便说一下, requests(5) 的把戏是什么?谷歌了它,没有任何相关的。
  • 我认为那是ClientCall.request() 调用,例如github.com/grpc/grpc-java/blob/master/benchmarks/src/jmh/java/…
  • Issue 6696 谈到了它,但只显示了一个黑客来查看行为。诀窍是每个 RPC 只调用“serverCall.request(5)”一次。这可以在拦截器中完成,或者将StreamObserver 转换为ServerCallStreamObserver,后者具有相同的request() 方法。这个技巧通过形成一个请求队列来工作,这样就不会在另一个线程上等待。
猜你喜欢
  • 1970-01-01
  • 2018-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-08
  • 1970-01-01
相关资源
最近更新 更多