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:
- Analysis of Java Netty HTTP service implementation process
- Netty combined with Protobuf for encoding and decoding
- The principle and application of Netty direct memory are analyzed
- Explain Netty encoder and decoder
- JAVA Netty implementation of chat room + private chat function example code
- Solution to the problem of Netty sticking and unpacking
- How to develop HTTP/HTTPS applications based on Netty