Netty工作线程和吞吐量

Nil*_*sta 4 java multithreading netty

我创建了一个具有多个工作线程的 Netty 服务器,以检查线程数量的增加如何改变吞吐量。这是我使用的代码。它是编写和回显服务器的稍微修改版本,可以在 Netty 网站中找到。

Echo服务器计算

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]));
    }
}
Run Code Online (Sandbox Code Playgroud)

EchoServer计算处理程序

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用 5 个工作线程运行此服务器,然后使用 50 个工作线程运行该服务器,并使用 JMeter 和 1000 个用户对其进行测试。但我在这两种情况下收到的吞吐量几乎相同。

我预计使用更多工作线程时吞吐量会有所增加。那么有人可以告诉我我在这里做错了什么吗?

编辑
我运行的测试环境有 2 个指定为服务器和客户端的节点。Server节点运行netty程序,Client节点运行JMeter。该服务器配备 Intel Xeon 5160 CPU 和 16GB RAM。该客户端拥有 Intel Xeon E5506 CPU 和 8GB RAM。它们之间的链路是1Gbps。

Two*_*The 6

您在这里面临几个问题:

Java太聪明了

for (int i=0; i<2000; i++)
        result = result.multiply(BigInteger.valueOf(i));
Run Code Online (Sandbox Code Playgroud)

JIT 会将其检测为死代码,然后将其完全删除。这意味着您的代码将在不到一毫秒的时间内完成。要修复此问题,请添加result到响应中,使其无法消除。

你的硬件有限

更多工作线程并不一定意味着更高的吞吐量,因为您的机器实际上需要能够处理工作负载。如果您还在同一台计算机上运行 JMeter,那么您不会看到吞吐量有任何增加,请记住threads >= amount_of_cpus / 2,如果您使用 Intel CPU,则其超核会注册为“真正的”CPU,但会赢得不做任何工作。因此,如果您在英特尔四核上运行此程序,则不要指望第二个工作线程之后吞吐量会有任何增加。

线程化需要时间

实际上需要时间来管理线程并在它们之间切换。因此,在系统中的线程达到一定数量后,吞吐量将明显下降。从 5 到 50 的步长太大,无法检测到这一点,请尝试以 2 个线程的步长前进。

线程混乱

线程的执行顺序是未定义的。因此,如果您运行足够多的线程,它们将开始互相窃取执行时间。有些可能会立即完成,而另一些则需要排队等待几秒钟。当线程数达到 50 时,您会发现完成时间大大增加,原因很简单,有如此多的线程一直处于搁置状态。您可以通过比较最小和最大执行时间来看到这一点,随着线程数量的增加,最小和最大执行时间应该开始出现差异。