Skip navigation

Netty 4.1.41.Final released

I am happy to announce the release of netty 4.1.41.Final. This is a bug-fix release but also contains a few performance enhancements and features. For more details please read-on.

The most important changes in this release are:

  • Http2EmptyDataFrameConnectionDecoder.frameListener() should return unwrapped Http2FrameListener (#9467)
  • Don't zero non-readable buffer regions when capacity is decreased (#9427)
  • AsciiString contentEqualsIgnoreCase fails when arrayOffset is non-zero (#9477)
  • Detect truncated responses caused by EDNS0 and MTU miss-match (#9468)
  • Ensure we replace WebSocketServerProtocolHandshakeHandler before doing the handshake (#9472)
  • Fix unexpected IllegalReferenceCountException on decode multipart request (#8660)
  • Correctly handle client side http2 upgrades when Http2FrameCodec is used together with Http2MultiplexHandler (#9501)
  • Reduce GC produced by AbstractByteBuf.indexOf(..) implementation (#9502)
  • Support cancellation in the Http2StreamChannelBootstrap (#9519)
  • Include native-image properties in the netty-all jar (#9518)
  • Fix sending an empty String like "" causes an error (#9512)
  • HttpPostStandardRequestDecoder leaks memory when constructor throws ErrorDataDecoderException (#9517)
  • Update GraalVM Native Image configuration for 19.2.0 (#9515)
  • Fix for incorrect values from CompositeByteBuf#component(int) (#9525)
  • Correctly protect DefaultChannelPipeline nodes when concurrent removals happen due handlerAdded(...) throwing (#9530)
  • Add support for recvmmsg when using epoll transport (#9509)
  • Avoid CancellationException construction in DefaultPromise (#9534)
  • Also support sendmmsg(...) on connected UDP channels when using native epoll transport (#9536)
  • Add support for recvmmsg(...) even with connected datagram channels when using native epoll transport (#9539)
  • SimpleChannelPool POOL_KEY attribute name is easy to get conflict from user code. (#9548)
  • SocksAuthRequest constructor occasionally throws IllegalStateException (#9558)
  • Fix HttpContentEncoder does not handle multiple Accept-Encoding (#9557)

For the details and all changes, please browse our issue tracker for 4.1.41.Final.

Important notes

Enable usage of recvmmsg(...) to reduce syscalls

On linux you can use recvmmsg(...) to reduce syscalls when using UDP. This allows to read multiple datagram packets via one syscall. This can drastically reduce the overhead and increase performance. To prove this we did a very naive benchmark which basically tried to read X messages as fast as possible while being flooded by 4 concurrent clients. With recvmmsg(...) the time of reading the messages decreased easily by a factor of 4-5x. This is by no means a "fixed number", but shows the possible gains.

For more details on the change and and the benchmark check (#9509).

To enable recvmmsg(...) you will need to specify a new EpollChannelOption when bootstrap your EpollDatagramChannel. You will see the best possible ratio of memory overhead <-> performance improvements when using it together with the AdaptiveRecvByteBufAllocator.

This example illustrate how you can change your existing code to use the new feature:

EventLoopGroup group = new EpollEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
         .channel(EpollDatagramChannel.class)
         .option(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(512))
         .handler(new SimpleChannelInboundHandler<DatagramPacket>() {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) {
                // Do something
            }
         })
         .bind(new InetSocketAddress(9999)).syncUninterruptibly().channel();

Enable the usage of recvmmsg(...):

EventLoopGroup group = new EpollEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
         .channel(EpollDatagramChannel.class)
         .option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(512, 512, 64 * 1024))
         .option(EpollChannelOption.MAX_DATAGRAM_PAYLOAD_SIZE, 512)
         .handler(new SimpleChannelInboundHandler<DatagramPacket>() {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) {
                // Do something
            }
         })
         .bind(new InetSocketAddress(9999)).syncUninterruptibly().channel();

The above configuration will start with trying to read one DatagramPacket first and then increase the number of DatagramPackets in a graceful manner depending on how many could be read on the last try. It will also decrease the number when needed.

Fix of performance regression when calling *Future.cancel(...) / *Promise.cancel(...).

This release also fixes a performance regression that was introduced in an earlier release which made the usage of *Future.cancel(...) and *Promise.cancel(...) quite expensive in some cases.

The regression was caused by creating new CancellationException instances and so caused fillInStackTrace() to be called quite frequently. This is a very expensive operation when executed on the "hot-path".

This showed up in flamegraphs during profiling and so was fixed:

alt text

For more details see (#9522).

Thank You

Every idea and bug-report counts and so we thought it is worth mentioning those who helped in this area. Please report an unintended omission.