Несовместимые значения задержки между метриками Dropwizard и JMeter на сервере Netty - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть HTTP-сервер Netty, который я тестирую, отправляя запросы через Apache JMeter.Я использую библиотеку метрик Dropwizard для измерения задержки на сервере.У меня проблема с метриками Dropwizard, которые показывают значения, отличные от JMeter, для задержки (Среднее и 99-й процентиль), но только время от времени.

Обработка происходит в отдельном пуле потоков, созданном с помощью класса ThreadPoolExecutor.Однако я заменил фактическую обработку, выполняемую в 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 работают на моем ноутбуке.Потоки сервера, приведенные ниже, ссылаются на значение, установленное для экземпляра ThreadPoolExecutor в NettyServer.java (значения задержки ниже в мс).1020 * 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 голосов
/ 01 декабря 2018

Задержка с точки зрения сервера (Netty здесь) и клиента (JMeter соответственно) - это совершенно разные вещи по своей конструкции, поэтому они просто НЕ МОГУТ совпадать.

Однако они могут быть такимизадержка клиента, скорее всего, будет включать задержку сервера - таким образом, значения на стороне JMeter всегда будут больше (в том, что вы показали, есть только средние значения и процентили, но это верно для них).

Просто посмотрите наОпределение задержки для Jmeter:

Задержка .JMeter измеряет задержку непосредственно перед отправкой запроса и сразу после получения первого ответа .Таким образом, время включает в себя всю обработку, необходимую для сборки запроса, а также сборку первой части ответа , которая в общем случае будет длиннее одного байта.Анализаторы протокола (такие как Wireshark) измеряют время, когда байты фактически отправляются / принимаются через интерфейс.Время JMeter должно быть ближе к тому, которое испытывает браузер или другой клиент приложения.

Видите?

И у сервера нет информации о том, какие этапы происходят там на клиенте (и считается в латентность).Никто не знает о том, что происходит по сетевому пути.

Вывод: то, что вы видите, вполне ожидаемо.

UPD: Было указано, что один изизмерения на стороне сервера превышают единицу измерения JMeter в граничном случае.Это интересно, и здесь я пытаюсь объяснить, как это возможно.

Отказ от ответственности сначала : я не знаю, что происходит в кишках используемого вами инструментария (поэтомуне бей меня слишком сильно, если я скучаю).

Хотя, с некоторой помощью здравого смысла, я могу догадаться, что:

1) Проблема в том, что ты прекратишьтаймер после сброса.Которые там кажутся синхронными.

2) Таким образом, ваше измерение задержки на стороне сервера включает полную очистку буфера.

3) Хотя JMeter измеряет задержку до первого поступления и сборки блока.

4) В большинстве случаев сервер достаточно быстр для сброса, быстрее, чем сеть + JMeter может его проглотить.

5) Но в некоторых крайних случаях сервер или сеть просто натыкаются на что-то, и последние куски запаздывают.

...