首页 > 解决方案 > Netty 服务器中 Dropwizard 指标和 JMeter 之间的延迟值不一致

问题描述

我有一个 Netty HTTP 服务器,我通过 Apache JMeter 发送请求来测试它。我正在使用 Dropwizard 指标库来测量服务器上的延迟。我遇到了 Dropwizard 指标的问题,它显示的延迟值与 JMeter 不同(平均和第 99 个百分位),但只是有时。

处理发生在使用 ThreadPoolExecutor 类创建的单独线程池中。但是,我用 sleep 语句替换了要在 Test.java 中完成的实际处理,以便我知道处理需要多长时间。

我的代码如下

延迟测试器.java

public class LatencyTester {

    public static void main(String[] args) throws Exception {
        Executors.newScheduledThreadPool(1);
        displayMetrics.scheduleAtFixedRate(new Metrics(), 10, 10, TimeUnit.SECONDS);
        new NettyServer().run();
    }
}

NettyServer.java

public class NettyServer {

    ThreadPoolExecutor executor;

    public NettyServer() {
    }

    public void run() throws Exception {

        executor = new ThreadPoolExecutor(7,7,100, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            Timer.Context context = Metrics.TIMER.time(); //Start Dropwizard metrics timer
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new HttpServerCodec());
                            p.addLast("aggregator", new HttpObjectAggregator(1048576));
                            p.addLast(new NettyServerHandler(executor, context));
                        }
                    }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(15000).sync();

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

NettyServerHandler.java

public class NettyServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private Future<ByteBuf> result;
    private Timer.Context cntx;
    private ThreadPoolExecutor threadPool;

    public NettyServerHandler(ThreadPoolExecutor pool, Timer.Context cntx) {
        this.cntx = cntx;
        this.threadPool = pool;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        Test tst = new Test();
        result = threadPool.submit(tst);
        boolean keepAlive = HttpUtil.isKeepAlive(msg);
        FullHttpResponse response = null;
        response = new DefaultFullHttpResponse(HTTP_1_1, OK, result.get());
        String contentType = msg.headers().get(HttpHeaderNames.CONTENT_TYPE);
        if (contentType != null) {
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
        }
        response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
        if (!keepAlive) {
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        } else {
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
            ctx.write(response);
        }
        ctx.flush();
        cntx.stop();  //Stop Dropwizard metrics timer
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

测试.java

public class Test implements Callable<ByteBuf> {

    public Test() {
    }

    @Override
    public ByteBuf call() throws Exception {
        TimeUnit.SECONDS.sleep(5);
        return (Unpooled.copiedBuffer("Done".getBytes()));
    }
}

以下是我在 JMeter 上运行几个测试后得到的一些结果,每个测试持续时间为 5 分钟。服务器和 JMeter 都在我的笔记本电脑上运行。下面的服务器线程是指 NettyServer.java 中为 ThreadPoolExecutor 实例设置的值(下面的延迟值以毫秒为单位) ServerThreads, JMeterThreads, MetricsAverage, Metrics99thP, JMeterAvg, JMeter99thP
1, 1, 5018, 5167, 5012, 5031
1, 7, 33407, 35165, 33380, 35003
5, 17, 15695, 19998, 16667,19970 - 平均相差 1 秒
50, 50, 8963, 15032, 15356, 29959 - 相差很大
7, 23, 112195, 149065, 1, 2巨大差距

为什么其中一些测试显示与 JMeter 和 Metrics 结果不一致?我在启动和停止 Dropwizard Metrics 计时器的地方做错了吗?

我可以做些什么来准确测量服务器端的请求延迟,以便它们显示从收到请求到发送回复所花费的时间?

标签: javajmeternettydropwizardmetrics

解决方案


从服务器(此处为 Netty)和客户端(分别为 JMeter)的角度来看,延迟在它们的设计上是完全不同的东西,因此它们根本无法匹配。

但是,它们可能是客户端的延迟很可能包括服务器的延迟 - 因此,JMeter 端的值总是会更大(您所显示的只有平均值和百分位数 - 但对他们来说确实如此)。

只需查看 Jmeter 的延迟定义:

延迟。JMeter 测量从发送请求之前到接收到第一个响应之后的延迟。因此,时间包括组装请求所需的所有处理以及组装响应的第一部分,通常会长于一个字节。协议分析器(例如 Wireshark)测量通过接口实际发送/接收字节的时间。JMeter 时间应该更接近浏览器或其他应用程序客户端所体验的时间。

看?

并且服务器对客户端上发生的那些阶段的了解为零(并计入延迟)。它也不知道网络路径上发生了什么。

结论:你所看到的完全是意料之中的。

UPD:有人指出,服务器端测量值之一在边缘情况下超过了 JMeter。这很有趣,在这里我试图解释这是怎么可能的。

首先免责声明:我不知道您在那里使用的工具包的内部会发生什么(所以如果我错过了,请不要打我太重)。

虽然,在常识推理的帮助下,我可以猜到:

1)问题是您在冲洗后停止计时器。那里似乎是同步的。

2)因此,您在服务器端的延迟测量包括缓冲区的完全刷新。

3)虽然 JMeter 测量延迟到第一个块到达和组装。

4)在大多数情况下,服务器的刷新速度足够快,比网络+ JMeter 可以吞下它还要快。

5)但在某些边缘情况下,服务器或网络只是偶然发现了一些东西,最后的块迟到了。


推荐阅读