How to implement efficient HTTP server with Netty

Keywords: Netty http

1 Overview

HTTP is based on request / response mode: the client sends an HTTP request to the server, and then the server will return an HTTP response. Netty provides a variety of encoders and decoders to simplify the use of this protocol. An HTTP request / response may consist of multiple data parts. FullHttpRequest and FullHttpResponse messages are special subtypes, representing the complete request and response respectively. All types of HTTP messages (FullHttpRequest, LastHttpContent, etc.) implement the HttpObject interface.

(1) HttpRequestEncoder take HttpRequest,HttpContent and LastHttpContent The message is encoded in bytes.
(2) HttpResponseEncoder take HttpResponse,HttpContent and LastHttpContent The message is encoded in bytes.
(3) HttpRequestDecoder Decode bytes into HttpRequest,HttpContent and LastHttpContent News.
(4) HttpResponseDecoder Decode bytes into HttpResponse,HttpContent and LastHttpContent News.
(5) HttpClientCodec and HttpServerCodec The request and response are combined.

Since HTTP requests and responses may consist of many parts, you need to aggregate them to form a complete message. 1.1 aggregate HTTP messages

To eliminate this tedious task, Netty provides an aggregator, HttpObjectAggregator, which can eliminate multiple
The information part is merged into FullHttpRequest or FullHttpResponse messages. In this way, you will always look
To the complete message content.

1.2 HTTP compression

When using HTTP, it is recommended to turn on the compression function to reduce the size of transmitted data as much as possible. Although compression will bring
To some CPU clock cycle overhead, but it's usually a good idea, especially for text data
Say. Netty provides a ChannelHandler implementation for compression and decompression, which supports both gzip and deflate coding.

2 code implementation

2.1 pom

<dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.28.Final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
        <!--tool-->
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
        <!--journal-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>



    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


2.2 HttpConsts

public class HttpConsts {

    private HttpConsts() {

    }

    public static final Integer PORT = 8888;

    public static final String HOST = "127.0.0.1";


}

2.3 server

2.3.1 HttpServer

@Slf4j
public class HttpServer {

    public static void main(String[] args) throws InterruptedException {

        HttpServer httpServer = new HttpServer();
        httpServer.start();
    }


    public void start() throws InterruptedException {


        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new HttpServerHandlerInitial());
            ChannelFuture channelFuture = serverBootstrap.bind(HttpConsts.PORT).sync();
            log.info("The server is turned on......");
            channelFuture.channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }


    }


}

 

2.3.2 HttpServerBusinessHandler

@Slf4j
public class HttpServerBusinessHandler extends ChannelInboundHandlerAdapter {


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        //Parse byteBuf into FullHttpRequest through codec
        if (msg instanceof FullHttpRequest) {

            //Get httpRequest
            FullHttpRequest httpRequest = (FullHttpRequest) msg;

            try {
                //Get request path, request body and request method
                String uri = httpRequest.uri();
                String content = httpRequest.content().toString(CharsetUtil.UTF_8);
                HttpMethod method = httpRequest.method();
                log.info("The server received the request:");
                log.info("request uri:{},request content:{},request method:{}", uri, content, method);

                //response
                String responseMsg = "Hello World";
                FullHttpResponse response = new DefaultFullHttpResponse(
                        HttpVersion.HTTP_1_1,HttpResponseStatus.OK,
                        Unpooled.copiedBuffer(responseMsg,CharsetUtil.UTF_8)
                );
                response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain;charset=UTF-8");
                ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
            } finally {
                httpRequest.release();
            }

        }
    }
}

2.3.3 HttpServerHandlerInitial

public class HttpServerHandlerInitial extends ChannelInitializer<SocketChannel> {


    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        ChannelPipeline pipeline = ch.pipeline();

        //http request codec, request decoding, response encoding
        pipeline.addLast("serverCodec", new HttpServerCodec());
        //The http request message is aggregated into a complete message, and the maximum request message is 10M
        pipeline.addLast("aggregator", new HttpObjectAggregator(10 * 1024 * 1024));
        //Response message compression
        pipeline.addLast("compress", new HttpContentCompressor());
        //Business processing handler
        pipeline.addLast("serverBusinessHandler", new HttpServerBusinessHandler());

    }
}

2.4 client
2.4.1 HttpClient

public class HttpClient {


    public static void main(String[] args) throws InterruptedException {

        HttpClient httpClien = new HttpClient();
        httpClien.start();

    }

    public void start() throws InterruptedException {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new HttpClientHandlerInitial());

            ChannelFuture f = bootstrap.connect(HttpConsts.HOST, HttpConsts.PORT).sync();
            f.channel().closeFuture().sync();

        } finally {
            eventLoopGroup.shutdownGracefully();
        }


    }

}


2.4.2 HttpClientBusinessHandler

@Slf4j
public class HttpClientBusinessHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //byteBuf is parsed into FullHttpResponse by codec
        if (msg instanceof FullHttpResponse) {
            FullHttpResponse httpResponse = (FullHttpResponse) msg;
            HttpResponseStatus status = httpResponse.status();
            ByteBuf content = httpResponse.content();
            log.info("Client receives response information:");
            log.info("status:{},content:{}", status, content.toString(CharsetUtil.UTF_8));
            httpResponse.release();
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //Encapsulate request information
        URI uri = new URI("/test");
        String msg = "Hello";
        DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
                HttpMethod.GET, uri.toASCIIString(), Unpooled.wrappedBuffer(msg.getBytes(CharsetUtil.UTF_8)));

        //Build http request
        request.headers().set(HttpHeaderNames.HOST, HttpConsts.HOST);
        request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());

        // Send http request
        ctx.writeAndFlush(request);
    }
}

2.4.3 HttpClientHandlerInitial

public class HttpClientHandlerInitial extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        ChannelPipeline pipeline = ch.pipeline();

        //Client encoder, decoder, request encoding, response decoding
        pipeline.addLast("clientCodec", new HttpClientCodec());
        //The http aggregator aggregates http requests into a complete message
        pipeline.addLast("aggregator", new HttpObjectAggregator(10 * 1024 * 1024));
        //http response decompression
        pipeline.addLast("decompressor", new HttpContentDecompressor());
        //Business handler
        pipeline.addLast("clientBusinessHandler", new HttpClientBusinessHandler());

    }
}


Start the server:

2.5 testing

Start client:

The above is the details of how to use Netty to implement an efficient HTTP server. For more information on how to use Netty to implement an HTTP server, please pay attention to other relevant articles of script home!

Articles you may be interested in:

Posted by JustGotAQuestion on Wed, 03 Nov 2021 02:50:59 -0700