New and noteworthy in 5.0
This document walks you through the list of notable changes and new features in the major Netty release (since 4.1) to give you an idea to port your application to the new version.
Unlike the changes between 3.x and 4.0, 5.0 did not change a lot although it made quite a bit of breakthrough in its design simplicity. We tried to make the transition from 4.x to 5.0 as smooth as possible, but please let us know if you encounter any issues during migration.
Netty 5 introduces a new Buffer API, that is simpler and safer to use than ByteBuf. For details, see the pull request #11347. In summary, the new API has the following headline differences:
- Aliasing is no longer allowed. In other words, you can no longer have multiple buffers referring to the same memory.
- This means
slice
,duplicate
, and their retaining variants are gone. - New APIs are introduced as replacements:
split
,readSplit
, andsend
. The contracts of these methods prevent aliasing.
- This means
- Reference counting is effectively gone.
- The
retain
andrelease
methods are gone. Buffers instead have aclose
method, which will be called at the end of the lifetime for the buffer. - APIs and integrations should be designed such that buffers always have a unique and clear ownership. "Borrowing" of buffers should be minimised and discouraged in APIs, since reference counting can no longer be used to keep track of borrows or references at runtime.
- Most places where
retain
was used were effectively just trying to cancel the effect of an unconditionalrelease
in a super-class. In these cases, you can usesplit
, since you most likely only want to pass down the readable part of the buffer anyway.
- The
- The
send
method and Send interface can be used to encode the transfer of ownership in the type system. - Buffers are always big-endian, and the
*LE
methods are gone.- To perform little-endian reads or writes, use the
reverseBytes
family of methods onInteger
,Long
, etc. in conjunction with the big-endian read and write methods on the buffer. -
BufferUtil
has a method for reversing "medium" (3 byte) integers.
- To perform little-endian reads or writes, use the
- Buffer implementations have higher test coverage, and more consistent behaviours.
ChannelInboundHandler
and ChannelOutboundHandler
have been merged into [ChannelHandler
]. [ChannelHandler
] now has both inbound and outbound handler methods.
ChannelInboundHandlerAdapter
, ChannelOutboundHandlerAdapter
, and ChannelDuplexHandlerAdapter
have been removed and replaced by ChannelHandlerAdapter
.
Because it is now impossible to tell if a handler is an inbound handler or an outbound handler, CombinedChannelDuplexHandler
has been replaced by ChannelHandlerAppender
.
For more information about this change, please refer to the pull request #1999.
If you are using SimpleChannelInboundHandler
, you have to rename channelRead0()
to messageReceived()
.
All ChannelHandler
outbound methods (except flush
and read
) now return a Future<Void>
. This ensures proper propagation and chaining.
Beside this we also renamed exceptionCaught(...)
to channelExceptionCaught(...)
to make it clear that this handles inbound exceptions.
It's now possible to fire user / custom events in both directions through the pipeline. For inbound events you would use fireChannelInboundEvent(...)
(replacement of fireUserEventTriggered(...)
and for outbound events sendOutboundEvent(...)
.
Both of these can be intercepted by methods defined in ChannelHandler
as usual.
It's now easily possible to influence the writability of the Channel
by the contained ChannelHandler
in the ChannelPipeline
. This makes it possible to influence back pressure when the ChannelHandler
itself buffers outbound data.
Netty5 has support for half-closure build in its core now. For this ChannelHandler.shutdown
and ChannelHandler.channelShutdown
were introduced. Beside this also Channel.isShutdown(...)
and ChannelOutboundInvoker.shutdown(...)
where added. This replace the old DuplexChannel
abstraction, which was completely removed. For more details see the pull request #12468.
We Changed ChannelHandlerContext to not extend AttributeMap anymore. In the case of you using attributes you should directly use Channel
which still extends AttributeMap
.
In netty 4.x we added the ability to add ChannelHandler
s to a ChannelPipeline
with an explicit EventExecutorGroup
. While this seemed like a good idea it turned out that it has quite some problems when it comes to life-cycles:
-
handlerRemoved(...)
,handlerAdded(...)
may be called at the "wrong time". This can result in a lot of problems. At worse it could mean thathandlerRemoved(...)
is called and the handler frees some native memory as it expect that the handler will never used anymore. What could happen here is that after it is calledchannelRead(...)
may be called which then try to access the previous freed memory and so crash the JVM. - correctly implement "visibility" in terms of concurrent access / modifications of the pipeline is quite problematic as well.
With this in mind we realised that really what the user mostly want is to have the incoming messages handled by another thread to process the business-logic. This is better be done in a custom implementation provided by the user as the user has a better handle on when things can be destroyed or not.
In netty 5.x we added the executor()
method to ChannelOutboundInvoker
and as this method returns EventExecutor
we did decide to remove the eventLoop()
method from Channel
and just override executor()
to return EventLoop
for Channel
.
It's now possible to check if a Channel
sub-type is compatible with the EventLoopGroup
/ EventLoop
before trying to use it. This can help to select the correct Channel
sub-type.
New methods were added to the EventLoop
interface to allow registration and deregistration of Channel
s. These should not be used by the user directly but by Channel
implementations itself.
The Channel.Unsafe interface was completely removed so its not possible for the end-user to mess-up internals.
The ChannelOutboundBuffer
is an implementation details of our AbstractChannel
implementation and so was removed for the Channel
itself completely.
Channel.beforeBeforeWritable()
removed and Channel.bytesBeforeUnwritable()
renamed to writableBytes()
.
We removed the Channel.beforeBeforeWritable()
method as it was not used at all and renamed Channel.bytesBeforeUnwritable()
to Channel.writableBytes
.
ProgressiveFuture
/ ProgressivePromise
and ChannelProgressiveFuture
/ ChannelProgressivePromise
removal
The support for ProgressiveFuture / ProgressivePromise was completely removed in netty 5. The reason for this was that while it may have been useful sometimes it also did require that all handlers in the pipeline take special action here if they did chain up promises. This was not really the case and is quite cumbersome to do in reality.
Due the stated problems we decided that it would be best to just remove this feature all together as there was not much usage of this feature anyway. It is better to not support something at all than have something only work "sometimes". This also means there is less code to maintain.
In netty 4.1.x it was possible to use the voidPromise()
method to obtain a special ChannelPromise
implementation that could be used for various IO operations (like write
) to reduce the number of objects created. While the motivation of this feature was good it turned out that this special case of a ChannelPromise
did actually bring a lot of problems:
- Each
ChannelHandler
that did add aChannelFutureListener
to the promise did have to callunvoid()
first to ensure that it is safe to add a listener. Missing to do so would lead to aRuntimeException
onceaddListener
was called. - wait() / sync() operations were not supported at all.
- Some operations would allow to use the
VoidChannelPromise
and some not.
- The
Future.addListeners()
,Future.removeListeners()
, andFuture.removeListener()
have been removed. We removed the ability to remove previous added listeners. This feature was not really used and so it allowed us to remove some complexity and remove some API surface. - The uninterruptible variants of the
sync
andawait
methods have been removed. - A
Future.isFailed()
method has been added, that checks that the future is both completed and failed. This is similar to the existingFuture.isSuccess()
which checks that a future is both completed and successfully so. -
Promise.setUncancellable
now only returnstrue
if the promise transitions from "incomplete" to "uncancellable". Specifically, this method now returnsfalse
if the promise has already been completed, where in 4.1 it would returntrue
in such cases. - New
Future.map()
andFuture.flatMap()
methods have been added, which makes it easy to compose and create new futures based on existing ones. These methods handle failures and cancellation properly through propagation. - All blocking methods were removed from the
Future
interface as it was easy for people to miss-use these and so block theEventLoop
. If you still need to block from outside theEventLoop
you will need to convert theFuture
viaFuture.asStage()
. The returnedFutureCompletionStage
provides blocking methods.
All our compression implementations were changed to make use of the new Compression API to make it easier to re-use in different codecs without the need to create an extra EmbeddedChannel
.
- Dropped support for multi-part from netty-core. This is to be migrated to netty 5 as a contributor repository in the future. (https://github.com/netty/netty/pull/11830)
- Dropped older websocket drafts (https://github.com/netty/netty/pull/11831)
- HTTP/2 Header validation is now enabled by default. This will cause malformed requests to be rejected by default.
To slim the code base down and ease the maintenance burden, the following codecs and handlers have been moved to Netty Contrib:
- netty-codec-xml
- netty-codec-redis
- netty-codec-memcache
- netty-codec-stomp
- netty-codec-haproxy
- netty-codec-mqtt
- netty-codec-socks
- netty-handler-proxy
io.netty.handler.codec.json
io.netty.handler.codec.marshalling
io.netty.handler.codec.protobuf
io.netty.handler.codec.serialization
io.netty.handler.codec.xml
io.netty.handler.pcap
Netty is now automatically initialised at runtime, with minimum size overhead in the native image. The minimum supported Graal version is now 22.1, Java 17.