我需要在java中实现一个grpc服务器,它能够处理grpc一元和双向流。使用grpc双向流的服务可以发送大量每秒的消息数。(可能每秒 2000 条消息或更多)我有两种实现方式,但我很困惑哪一种最适合我的要求。
1.对 grpc 一元和 grpc 双向使用相同的服务器。
使用这种方法时,由于grpc一元流和双向流使用相同的端口,因此将为一元流和双向流分配一个boss线程。所以我不确定在双向流每秒接收大量消息的情况下它的表现如何。 (我的意思是boss线程是否会因为bidi流而变得忙碌而对unary不可用)
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 unary 和 bidi 流的两个服务使用 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));
}
答案 0 :(得分:0)
使用单个服务器。
boss 线程仅用于接受()新连接。它不用于实际处理。这是由工作程序事件循环完成的。每个连接分配给一个事件循环,一个事件循环可以服务多个连接。
在单个流上,Netty 每秒可以处理 10 万条消息。但这实际上很慢。查找消息边界由与传递消息不同的线程处理,并且这两个线程之间的通信会增加延迟。增加的延迟会减慢速度。使用避免延迟的 requests(5)
技巧,单个 Netty 流每秒可以处理 1250k 条消息。 (这些性能数字会因运行它们的机器而异,但它们显然比您需要的要高得多。)请参阅讨论延迟问题的 https://github.com/grpc/grpc-java/issues/6696。
但假设您需要更高的性能,或者想将一元流量与流媒体流量分开。在这种情况下,我们建议使用两个不同的渠道。每个通道将使用自己的连接和(可能)单独的工作事件循环。
只有当您非常关心延迟时,您才应该费心将两种类型的流量分成不同的服务器。 (这样你也有显示它有多大帮助的基准。)是的,使用单独的服务器和通道和它们自己的 workerEventLoopGroup()
(在通道上也是如此!通道默认使用共享事件循环组),与有限数量的线程,因此每个线程都可以有自己的处理器内核进行处理。但我希望这是一种罕见的情况。您很快就要将服务器二进制文件一分为二,以避免 GC 和服务之间的类似交互。