跳过导航

5.0 中的新增功能和注意事项

您知道此页面是根据 Github Wiki 页面 自动生成的。您可以 在此处 自行改进!

此文档将引导您了解 Netty 主要版本(自 4.1 以来)中值得注意的更改和新功能列表,以便您了解如何将应用程序移植到新版本。

与 3.x 和 4.0 之间的更改不同,5.0 并没有改变很多,尽管它在设计简洁性方面取得了相当大的突破。我们尝试尽可能平滑地过渡到 4.x 到 5.0,但如果您在迁移过程中遇到任何问题,请告诉我们。

核心更改

新的 Buffer API 取代 ByteBuf

Netty 5 引入了新的 Buffer API,它比 ByteBuf 更简单、更安全。有关详细信息,请参阅 请求拉取 #11347。总之,新 API 具有以下主要区别

  • 不再允许别名。换句话说,您不能再让多个缓冲区引用同一内存。
    • 这意味着 sliceduplicate 以及它们的保留变体已消失。
    • 引入了新 API 作为替代:splitreadSplitsend。这些方法的契约禁止别名。
  • 引用计数已有效消失。
    • retainrelease 方法已消失。相反,缓冲区具有 close 方法,该方法将在缓冲区的生命周期结束时调用。
    • API 和集成应设计为缓冲区始终具有唯一且明确的所有权。“借用”缓冲区应在 API 中最小化并加以阻止,因为引用计数不再可用于在运行时跟踪借用或引用。
    • 大多数使用 retain 的地方实际上只是尝试取消超类中无条件 release 的效果。在这些情况下,您可以使用 split,因为您很可能只想传递缓冲区的可读部分。
  • send 方法和 Send 接口可用于对类型系统中的所有权转移进行编码。
  • 缓冲区始终是大端,*LE 方法已消失。
    • 要执行小端读取或写入,请结合缓冲区上的大端读取和写入方法,对 IntegerLong 等使用 reverseBytes 系列方法。
    • BufferUtil 具有用于反转“中等”(3 字节)整数的方法。
  • 缓冲区实现具有更高的测试覆盖率和更一致的行为。

ChannelHandler

简化的处理程序类型层次结构

ChannelInboundHandlerChannelOutboundHandler 已合并到 [ChannelHandler] 中。[ChannelHandler] 现在同时具有入站和出站处理程序方法。

ChannelInboundHandlerAdapterChannelOutboundHandlerAdapterChannelDuplexHandlerAdapter 已被移除,并由 ChannelHandlerAdapter 取代。

由于现在无法判断处理程序是入站处理程序还是出站处理程序,因此 CombinedChannelDuplexHandler 已被 ChannelHandlerAppender 取代。

有关此更改的更多信息,请参阅 请求拉取 #1999

SimpleChannelInboundHandler.channelRead0()messageReceived()

如果您正在使用 SimpleChannelInboundHandler,则必须将 channelRead0() 重命名为 messageReceived()

ChannelHandler 方法签名更改。

现在,所有 ChannelHandler 出站方法(除了 flushread)都返回 Future<Void>。这确保了正确的传播和链接。除此之外,我们还将 exceptionCaught(...) 重命名为 channelExceptionCaught(...),以明确表示它处理入站异常。

用户事件

现在可以通过管道向两个方向发送用户/自定义事件。对于入站事件,您将使用 fireChannelInboundEvent(...)(替换 fireUserEventTriggered(...)),对于出站事件,您将使用 sendOutboundEvent(...)。这两个事件都可以通过 ChannelHandler 中定义的方法拦截,就像往常一样。

添加了 ChannelHandler.pendingOutboundBytes(...) 方法

现在可以轻松地通过 ChannelPipeline 中包含的 ChannelHandler 影响 Channel 的可写性。当 ChannelHandler 本身缓冲出站数据时,这使得可以影响反压。

传输

半关闭

Netty5 现在在其核心支持半关闭构建。为此,引入了 ChannelHandler.shutdownChannelHandler.channelShutdown。除此之外,还添加了 Channel.isShutdown(...)ChannelOutboundInvoker.shutdown(...)。这取代了旧的 DuplexChannel 抽象,后者已被完全删除。有关更多详细信息,请参阅 请求拉取 #12468

ChannelHandlerContext 不再扩展 AttributeMap

我们更改了 ChannelHandlerContext,使其不再扩展 AttributeMap。如果您使用属性,则应直接使用仍扩展 AttributeMapChannel

删除了 ChannelPipeline.add*(EventExecutorGroup...)

在 netty 4.x 中,我们添加了使用显式 EventExecutorGroupChannelHandler 添加到 ChannelPipeline 的功能。虽然这看起来像一个好主意,但事实证明,在生命周期方面存在一些问题

  • handlerRemoved(...)handlerAdded(...) 可能会在“错误时间”调用。这会导致很多问题。最坏的情况可能是调用 handlerRemoved(...),并且处理程序释放了一些本机内存,因为它期望该处理程序永远不会再使用。这里可能发生的情况是,在调用它之后,可能会调用 channelRead(...),然后尝试访问先前释放的内存,从而导致 JVM 崩溃。
  • 正确实现管道并发访问/修改方面的“可见性”也相当有难度。

考虑到这一点,我们意识到用户最想要的是让传入消息由另一个线程处理,以处理业务逻辑。最好由用户提供自定义实现来完成此操作,因为用户可以更好地控制何时可以销毁事物或不能销毁事物。

Channel.eventLoop() 重命名为 Channel.executor()

在 netty 5.x 中,我们向 ChannelOutboundInvoker 添加了 executor() 方法,并且由于此方法返回 EventExecutor,因此我们决定从 Channel 中删除 eventLoop() 方法,并仅覆盖 executor() 以返回 ChannelEventLoop

添加了 EventLoopGroup.isCompatible(...) 方法

现在可以在尝试使用 Channel 子类型之前检查它是否与 EventLoopGroup / EventLoop 兼容。这有助于选择正确的 Channel 子类型。

添加了 EventLoop.registerForIo(...)EventLoop.deregisterForIo

EventLoop 接口添加了新方法,以允许注册和取消注册 Channel。这些方法不应由用户直接使用,而应由 Channel 实现本身使用。

删除 Channel.Unsafe

Channel.Unsafe 接口已完全删除,因此最终用户无法弄乱内部结构。

ChannelOutboundBuffer 不再是 Channel API 的一部分

ChannelOutboundBuffer 是我们 AbstractChannel 实现的实现细节,因此已从 Channel 本身完全删除。

删除了 Channel.beforeBeforeWritable(),并将 Channel.bytesBeforeUnwritable() 重命名为 writableBytes()

我们删除了 Channel.beforeBeforeWritable() 方法,因为它根本没有被使用,并将 Channel.bytesBeforeUnwritable() 重命名为 Channel.writableBytes

Future / Promise

删除了 ProgressiveFuture / ProgressivePromiseChannelProgressiveFuture / ChannelProgressivePromise

在 netty 5 中完全删除了对 ProgressiveFuture / ProgressivePromise 的支持。原因是,虽然它有时可能有用,但它还要求管道中的所有处理程序在此处执行特殊操作,如果它们对 promise 进行链接。实际上并非如此,并且在现实中这样做非常麻烦。

由于存在上述问题,我们决定最好直接移除此功能,因为此功能的用处不大。完全不支持某项功能总比只在“有时”起作用要好。这也意味着要维护的代码更少。

VoidChannelPromise 已移除

在 netty 4.1.x 中,可以使用 voidPromise() 方法获取一个特殊的 ChannelPromise 实现,该实现可用于各种 IO 操作(如 write),以减少创建的对象数量。虽然此功能的初衷是好的,但事实证明,此特殊情况的 ChannelPromise 确实带来了很多问题

  • 向该承诺添加 ChannelFutureListener 的每个 ChannelHandler 都必须首先调用 unvoid(),以确保可以安全地添加侦听器。如果未执行此操作,则在调用 addListener 时将导致 RuntimeException
  • 根本不支持 wait() / sync() 操作。
  • 某些操作允许使用 VoidChannelPromise,而某些操作则不允许。

PromiseFuture 的 API 更改

  • 已移除 Future.addListeners()Future.removeListeners()Future.removeListener()。我们移除了移除先前添加的侦听器的功能。此功能并未真正使用,因此它允许我们移除一些复杂性并移除一些 API 表面。
  • 已移除 syncawait 方法的不可中断变体。
  • 已添加 Future.isFailed() 方法,该方法检查 future 是否已完成且失败。这类似于现有的 Future.isSuccess(),它检查 future 是否已完成且成功。
  • Promise.setUncancellable 现在仅在 promise 从“未完成”过渡到“不可取消”时才返回 true。具体来说,如果 promise 已完成,则此方法现在返回 false,而在 4.1 中,它在这种情况下将返回 true
  • 已添加新的 Future.map()Future.flatMap() 方法,这使得基于现有 future 轻松组合和创建新的 future 变得容易。这些方法通过传播正确处理故障和取消。
  • 所有阻塞方法都已从 Future 接口中移除,因为人们很容易误用这些方法并阻塞 EventLoop。如果您仍需要从 EventLoop 外部进行阻塞,则需要通过 Future.asStage() 转换 Future。返回的 FutureCompletionStage 提供阻塞方法。

编解码器更改

压缩支持

我们对所有压缩实现进行了更改,以利用新的 压缩 API,以便在不同的编解码器中更轻松地重复使用,而无需创建额外的 EmbeddedChannel

HTTP 编解码器

很少使用的编解码器移至 Netty Contrib

为了精简代码库并减轻维护负担,以下编解码器和处理程序已移至 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

GraalVM 和 Native Image

Netty 现在在运行时自动初始化,在 native image 中具有最小的开销。现在支持的最低 Graal 版本是 22.1,Java 17。

上次检索时间为 2024 年 7 月 19 日