io.vertx.benchmarks.HttpServerHandlerBenchmark.java Source code

Java tutorial

Introduction

Here is the source code for io.vertx.benchmarks.HttpServerHandlerBenchmark.java

Source

/*
 * Copyright (c) 2011-2017 Contributors to the Eclipse Foundation
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 */

package io.vertx.benchmarks;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.impl.HttpHandlers;
import io.vertx.core.http.impl.Http1xServerHandler;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.EventLoopContext;
import io.vertx.core.impl.VertxInternal;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.impl.HandlerHolder;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author <a href="mailto:julien@julienviet.com">Julien Viet</a>
 */
@State(Scope.Thread)
public class HttpServerHandlerBenchmark extends BenchmarkBase {

    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
    public static void consume(final ByteBuf buf) {
    }

    ByteBuf GET;
    int readerIndex;
    int writeIndex;
    VertxInternal vertx;
    EmbeddedChannel vertxChannel;
    EmbeddedChannel nettyChannel;

    static class Alloc implements ByteBufAllocator {

        private final ByteBuf buf = Unpooled.buffer();
        private final int capacity = buf.capacity();

        @Override
        public ByteBuf buffer() {
            buf.clear();
            return buf;
        }

        @Override
        public ByteBuf buffer(int initialCapacity) {
            if (initialCapacity <= capacity) {
                return buffer();
            } else {
                throw new IllegalArgumentException();
            }
        }

        @Override
        public ByteBuf buffer(int initialCapacity, int maxCapacity) {
            if (initialCapacity <= capacity) {
                return buffer();
            } else {
                throw new IllegalArgumentException();
            }
        }

        @Override
        public ByteBuf ioBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf ioBuffer(int initialCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf heapBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf heapBuffer(int initialCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf directBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf directBuffer(int initialCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeBuffer(int maxNumComponents) {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeHeapBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeDirectBuffer() {
            throw new UnsupportedOperationException();
        }

        @Override
        public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isDirectBufferPooled() {
            throw new UnsupportedOperationException();
        }

        @Override
        public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
            throw new UnsupportedOperationException();
        }
    }

    private static final CharSequence RESPONSE_TYPE_PLAIN = io.vertx.core.http.HttpHeaders
            .createOptimized("text/plain");

    private static final String HELLO_WORLD = "Hello, world!";
    private static final Buffer HELLO_WORLD_BUFFER = Buffer.buffer(HELLO_WORLD);

    private static final CharSequence HEADER_SERVER = io.vertx.core.http.HttpHeaders.createOptimized("server");
    private static final CharSequence HEADER_DATE = io.vertx.core.http.HttpHeaders.createOptimized("date");
    private static final CharSequence HEADER_CONTENT_TYPE = io.vertx.core.http.HttpHeaders
            .createOptimized("content-type");
    private static final CharSequence HEADER_CONTENT_LENGTH = io.vertx.core.http.HttpHeaders
            .createOptimized("content-length");

    private static final CharSequence HELLO_WORLD_LENGTH = io.vertx.core.http.HttpHeaders
            .createOptimized("" + HELLO_WORLD.length());
    private static final CharSequence SERVER = io.vertx.core.http.HttpHeaders.createOptimized("vert.x");
    private static final CharSequence DATE_STRING = io.vertx.core.http.HttpHeaders.createOptimized(
            java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now()));

    @Setup
    public void setup() {
        vertx = (VertxInternal) Vertx.vertx();
        HttpServerOptions options = new HttpServerOptions();
        vertxChannel = new EmbeddedChannel(
                new HttpRequestDecoder(options.getMaxInitialLineLength(), options.getMaxHeaderSize(),
                        options.getMaxChunkSize(), false, options.getDecoderInitialBufferSize()),
                new HttpResponseEncoder());
        vertxChannel.config().setAllocator(new Alloc());

        ContextInternal context = new EventLoopContext(vertx, vertxChannel.eventLoop(), null, null, null,
                new JsonObject(), Thread.currentThread().getContextClassLoader());
        Handler<HttpServerRequest> app = request -> {
            HttpServerResponse response = request.response();
            MultiMap headers = response.headers();
            headers.add(HEADER_CONTENT_TYPE, RESPONSE_TYPE_PLAIN).add(HEADER_SERVER, SERVER)
                    .add(HEADER_DATE, DATE_STRING).add(HEADER_CONTENT_LENGTH, HELLO_WORLD_LENGTH);
            response.end(HELLO_WORLD_BUFFER);
        };
        HandlerHolder<HttpHandlers> holder = new HandlerHolder<>(context, new HttpHandlers(app, null, null, null));
        Http1xServerHandler handler = new Http1xServerHandler(null, new HttpServerOptions(), "localhost", holder,
                null);
        vertxChannel.pipeline().addLast("handler", handler);

        nettyChannel = new EmbeddedChannel(
                new HttpRequestDecoder(options.getMaxInitialLineLength(), options.getMaxHeaderSize(),
                        options.getMaxChunkSize(), false, options.getDecoderInitialBufferSize()),
                new HttpResponseEncoder(), new SimpleChannelInboundHandler<HttpRequest>() {

                    private final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(CharsetUtil.UTF_8);
                    private final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length;
                    private final ByteBuf PLAINTEXT_CONTENT_BUFFER = Unpooled
                            .unreleasableBuffer(Unpooled.directBuffer().writeBytes(STATIC_PLAINTEXT));
                    private final CharSequence PLAINTEXT_CLHEADER_VALUE = new AsciiString(
                            String.valueOf(STATIC_PLAINTEXT_LEN));

                    private final CharSequence TYPE_PLAIN = new AsciiString("text/plain");
                    private final CharSequence SERVER_NAME = new AsciiString("Netty");
                    private final CharSequence CONTENT_TYPE_ENTITY = HttpHeaderNames.CONTENT_TYPE;
                    private final CharSequence DATE_ENTITY = HttpHeaderNames.DATE;
                    private final CharSequence CONTENT_LENGTH_ENTITY = HttpHeaderNames.CONTENT_LENGTH;
                    private final CharSequence SERVER_ENTITY = HttpHeaderNames.SERVER;

                    private final DateFormat FORMAT = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z");
                    private final CharSequence date = new AsciiString(FORMAT.format(new Date()));

                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
                        writeResponse(ctx, msg, PLAINTEXT_CONTENT_BUFFER.duplicate(), TYPE_PLAIN,
                                PLAINTEXT_CLHEADER_VALUE);
                    }

                    @Override
                    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                        ctx.flush();
                    }

                    private void writeResponse(ChannelHandlerContext ctx, HttpRequest request, ByteBuf buf,
                            CharSequence contentType, CharSequence contentLength) {

                        // Build the response object.
                        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                                HttpResponseStatus.OK, buf, false);
                        HttpHeaders headers = response.headers();
                        headers.set(CONTENT_TYPE_ENTITY, contentType);
                        headers.set(SERVER_ENTITY, SERVER_NAME);
                        headers.set(DATE_ENTITY, date);
                        headers.set(CONTENT_LENGTH_ENTITY, contentLength);

                        // Close the non-keep-alive connection after the write operation is done.
                        ctx.write(response, ctx.voidPromise());
                    }
                });
        nettyChannel.config().setAllocator(new Alloc());

        GET = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer(("GET / HTTP/1.1\r\n" + "\r\n").getBytes()));
        readerIndex = GET.readerIndex();
        writeIndex = GET.writerIndex();
    }

    @Benchmark
    public void vertx() {
        GET.setIndex(readerIndex, writeIndex);
        vertxChannel.writeInbound(GET);
        ByteBuf result = (ByteBuf) vertxChannel.outboundMessages().poll();
        consume(result);
    }

    @Fork(value = 1, jvmArgsAppend = { "-Dvertx.threadChecks=false", "-Dvertx.disableContextTimings=true",
            "-Dvertx.disableTCCL=true ", })
    @Benchmark
    public void vertxOpt() {
        GET.setIndex(readerIndex, writeIndex);
        vertxChannel.writeInbound(GET);
        ByteBuf result = (ByteBuf) vertxChannel.outboundMessages().poll();
        consume(result);
    }

    @Benchmark
    public void netty() {
        GET.setIndex(readerIndex, writeIndex);
        nettyChannel.writeInbound(GET);
        ByteBuf result = (ByteBuf) nettyChannel.outboundMessages().poll();
        consume(result);
    }
}