我创建了一个具有多个工作线程的netty服务器,以检查线程数量的增加如何改变吞吐量。 这是我使用的代码。它是Writing and echo server的略微修改版本,可以在Netty网站上找到。
EchoServerCompute
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServerCompute {
private int port;
public EchoServerCompute(int port) {
this.port = port;
}
public void run(int threadCount) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(threadCount);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoServerComputeHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new EchoServerCompute(port).run(Integer.parseInt(args[0]));
}
}
EchoServerComputeHandler
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.lang.Math;
import java.math.BigInteger;
public class EchoServerComputeHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
BigInteger result = BigInteger.ONE;
for (int i=0; i<2000; i++)
result = result.multiply(BigInteger.valueOf(i));
ctx.write(msg);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
我使用5个工作线程运行此服务器,然后运行50个工作线程,并使用JMeter和1000个用户对其进行测试。但是我在两个实例中收到的吞吐量几乎相同。
我预计在使用更多工作线程时会看到吞吐量增加。那么有人可以告诉我,如果我在这里做错了吗?
修改的
我运行它的测试环境有2个节点,指定为Server和Client。 Server节点正在运行netty程序,而Client节点正在运行JMeter。该服务器具有Intel Xeon 5160 CPU和16GB RAM。客户端具有Intel Xeon E5506 CPU和8GB RAM。它们之间的链接是1Gbps。
答案 0 :(得分:2)
您在这里面临几个问题:
Java太聪明了
for (int i=0; i<2000; i++)
result = result.multiply(BigInteger.valueOf(i));
JIT会将此检测为死代码并将其完全删除。这意味着您的代码将在几分之一毫秒内完成。要解决此问题,请将result
添加到响应中,以便无法将其消除。
您的硬件有限
更多的工作线程并不一定意味着更高的吞吐量,因为您的机器实际上需要能够处理工作负载。如果你也在同一台机器上运行JMeter,那么在threads >= amount_of_cpus / 2
的数量上你就不会看到任何吞吐量的增加。请记住,如果你使用的是英特尔CPU,那么超级内核会注册为“真实的” “CPU,但不会做任何工作。因此,如果您在英特尔四核处理器上运行此功能,请不要指望在第二个工作线程之后吞吐量会有任何增加。
线程需要时间
管理线程并在它们之间切换实际上需要时间。因此,在系统中的一定数量的线程之后,您的吞吐量将显着下降。你从5点到50点的步数太大而无法检测到这一点,尝试以2个线程的步长前进。
线程混乱
使用线程执行的顺序是未定义的。因此,如果运行足够的线程,它们将开始相互窃取执行时间。有些可能会立即完成,而其他人则排队等待几秒钟。在50个线程中,您将看到完成的时间大大增加,原因很简单,因为许多线程一直处于保持状态。你可以通过比较最小和最大执行时间来看到这一点,这应该开始与越来越多的线程分开。
答案 1 :(得分:0)
由于大多数http服务器都有两个workthread限制。
一个是&#39;端点&#39;的工作线程的最大数量。另一个是服务器的工作线程的最大数量。
基于ip加法器(或会话)的端点,一个端点的默认最大线程数为5.因为虽然您创建了1000个用户进行测试,但您的1000个用户属于一个端点,因此最大吞吐量始终为5. / p>
这个限制旨在过多地解决一个端点请求服务器,这将导致无法处理其他端点的工作,这将是严重且危险的。
答案 2 :(得分:0)
我的想法:
答案 3 :(得分:0)
Netty旨在支持非阻塞I / O,这意味着线程未被阻塞。从一个线程切换到另一个线程需要花费一定的成本,因此固定数量的线程效率更高。该数字通常为number_of_core × 2
。 Java的Executor
是在可用线程之间分配任务的正确抽象。
只需认为一旦每个内核都以最大容量运行,创建更多线程就无济于事。将线程池大小增加到可用物理线程数以上仅在阻止I / O处理更多同时连接的情况下有用。
如果您的任务用时不长,则可以在与EventLoopGroup
相同的线程池中运行它们。不要尝试调整线程数。相反,请调整如何在I / O和任务之间分配线程总数。您可以使用两个单独的Executors
(EventLoopGroup
是一个),也可以调整EventLoopGroup#setIoRatio
的值。