繁体   English   中英

网络工作者线程和吞吐量

[英]Netty worker threads and throughput

我创建了一个具有服务器工作线程的netty服务器,以检查线程数量的增加如何改变吞吐量。 这是我使用的代码。 它是可在Netty网站上找到的Writing and echo服务器的略微修改版本。

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节点正在运行netty程序,而Client节点正在运行JMeter。 该服务器具有Intel Xeon 5160 CPU和16GB RAM。 客户端具有Intel Xeon E5506 CPU和8GB RAM。 它们之间的链接是1Gbps。

您在这里面临几个问题:

Java太聪明了

for (int i=0; i<2000; i++)
        result = result.multiply(BigInteger.valueOf(i));

JIT会将其检测为无效代码,并完全将其删除。 这意味着您的代码将在不到一毫秒的时间内完成。 要将此添加result固定为响应,以便无法消除它。

您的硬件有限

更多的工作线程并不一定意味着更多的吞吐量,因为您的计算机实际上需要能够处理工作负载。 如果您还在同一台计算机上运行JMeter,则在threads >= amount_of_cpus / 2数量threads >= amount_of_cpus / 2的情况下,您不会看到吞吐量的任何增加。请记住,如果您使用的是Intel CPU,则超级内核会注册为“真正的” CPU,但不会做任何工作。 因此,如果您在Intel Quad-Core上运行此程序,则不要期望第二个辅助线程之后的吞吐量有任何增加。

穿线需要时间

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

线程混乱

线程的执行顺序是不确定的。 因此,如果您运行足够的线程,它们将开始相互占用执行时间。 有些可能会立即完成,而另一些可能会排队等待几秒钟。 在50个线程中,您将看到完成时间大大增加,这仅仅是因为有这么多线程一直处于等待状态。 您可以通过比较最小和最大执行时间来看到这一点,随着时间的增加,线程执行时间应该开始出现差异。

由于大多数http服务器都有两个限制的工作线程。

一个是“端点”的工作线程的最大数量。另一个是服务器的工作线程的最大数量。

基于ip加法器(或会话)的端点,一个端点的默认最大线程数为5。因此,尽管您创建了1000个要测试的用户,但您的1000个用户属于一个端点,所以最大吞吐量始终为5。

该限制旨在过多地欺骗一个端点请求服务器,这将导致没有工作要处理其他端点,这将是严重而危险的。

我的想法:

  1. 删除执行乘法并重新测试的循环
  2. 检查您的JMeter配置。 您能发表您的线程配置吗?
  3. 当前每秒请求数/吞吐量是多少? 您的系统配置是什么? 可能您已达到系统限制。

Netty旨在支持非阻塞I / O,这意味着未阻塞线程。 从一个线程切换到另一个线程需要花费一定的成本,因此固定数量的线程效率更高。 该数字通常是number_of_core × 2 Java的Executor是在可用线程之间分配任务的正确抽象。

试想一下,一旦每个内核都满负荷运行,创建更多线程将无济于事。 将线程池大小增加到可用物理线程数以上仅在阻止I / O处理更多同时连接的情况下有用。

如果您的任务用时不长,则可以在与EventLoopGroup相同的线程池中运行它们。 不要尝试调整线程数。 相反,请调整如何在I / O和任务之间分配线程总数。 您可以使用两个单独的ExecutorsEventLoopGroup为1),也可以调整EventLoopGroup#setIoRatio的值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM