background

Netty is used to implement long connection. After the Client sends a request to the Server, the Server caches a Channel. When subsequent services need to actively push data to the Client, the Server directly traverses the Channel and writes data.

Http server implementation with Netty is quite convenient, not familiar with Netty, looking at a variety of similar code, can be used, no details added to the Pipeline processor and the difference. An HttpContentCompressor was used, which failed to write the HttpResponse data.

This article documents the beginning and end of the problem.

Regular Netty HttpServer code

Building code is common for Netty as a server:

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) { channel.pipeline().addLast(new HttpServerCodec()); // provide to aggregate HttpRequest during handshake, 413Request Entity Too Large channel.pipeline().addLast(new HttpObjectAggregator(104857600)); HttpResponse channel.pipeline().addLast(new HttpContentCompressor()); channel.pipeline().addLast(new ChunkedWriteHandler()); channel.pipeline().addLast(new MyServerHandler()); }}Copy the code

The server keeps a long connection with the Client and pushes data to the Client through Channel when necessary. The above code, when actively pushing data, will report an exception.

The Netty Server fails to actively write data to the channel. Procedure

The services on the Server side need to push data to the Client through Channel, and the push code is as follows:

FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(result, CharsetUtil.UTF_8)); // Unpooled.wrappedBuffer(responseJson) response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); // HttpHeaderValues.TEXT_PLAIN.toString() response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); if (channel.isWritable()) { channel.writeAndFlush(httpResponse).addListener((ChannelFutureListener) future -> { if (Future.isSuccess ()) {logger.info(" write back successfully "); } else {logger.error(" write back failed ",future.cause()); }}); }Copy the code

In the use of theHttpContentCompressorLater,writeAndFlushCallbacks always go into the exception branch:Complete exception information:

io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: 
cannot send more responses than requests
        at io.netty.handler.codec.
        MessageToMessageEncoder.write(MessageToMessageEncoder.java:107)
Copy the code

Remove HttpContentCompressor

After removing the HttpContentCompressor from the Server, the Server will be able to send data correctly. Why does the HttpResponse processor have such a large impact on data write?

The revelation of

At first, I thought Netty realized HTTP service. The Client sends requests through THE HTTP protocol, but the Server actively pushes data because of the limitations of the HTTP protocol.

So try using the WebSocket protocol so that push back is OK. However, the Client only uses one Socket for connection. As a result, other normal Http requests are upgraded to WebSocket requests, and other Http processing processes cannot run properly.

Conclusion:

  1. If you want to useHttpContentCompressor, the reverse data push can only be carried out through WebSocket protocol. The Client needs to distinguish Http requests from Websocket requests and use different sockets to send data.
  2. To get rid ofHttpContentCompressorClass, the Client uses Http request cache Channel can push data normally, but the response Body data does not have compression function. There is no big data transfer business in the application, so removing it doesn’t matter.