在Netty服务器中Dropwizard指标和JMeter之间的延迟值不一致

时间:2018-11-30 12:33:52

标签: java jmeter netty dropwizard metrics

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

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

我的代码如下

LatencyTester.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();
    }
}

Test.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实例设置的值(下面的延迟值以ms为单位) 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,11295,14965,16121,20002-大差异

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

我该怎么做才能在服务器端准确测量请求延迟,以便它们显示从接收请求到发送答复所花费的时间?

1 个答案:

答案 0 :(得分:3)

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

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

只需查看Jmeter的延迟定义:

  

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

看到了吗?

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

结论:您所看到的完全是预期的。

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

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

尽管如此,在常识推理的帮助下,我可以猜到:

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

2)因此,服务器端的等待时间测量包括缓冲区的全部刷新。

3)当JMeter测量直到第一个块到达和组装的延迟时。

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

5)但是在某些极端情况下,服务器或网络只是偶然发现了某些东西,而最后的数据块却迟到了。