转载

Netty各版本之间的变化和需要注意的地方

本文翻译自官方文档 New and noteworthy in 5.0 .

本文带你了解Netty 5.0 的值得注意的改变和新特性,帮助你将应用程序迁移到最新的版本.

不像 3.x 和 4.0 之间的改变那么大, 5.0 并没有很大的改变,尽管它在设计的简化性上做了一些突破. 我们尽可能平滑地使4.x转换到5.0,但是如果在迁移的过程中有问题请让我们知道.

其它版本之间的改变和新特性文档请看:

Netty 4.1中的新变化和注意点

Netty 4.0中的新变化和注意点

核心改变

简化 handler 类型继承关系

ChannelInboundHandlerChannelOutboundHandler 被合并到 ChannelHandler . 现在ChannelHandler拥有inbound 和 outbound handler 方法.

ChannelInboundHandlerAdapter , ChannelOutboundHandlerAdapter , 和 ChannelDuplexHandlerAdapter 弃用了, 被 ChannelHandlerAdapter 取代.

因为现在你无法区分一个 handler是 inbound handler 或者 outbound handler, 所以 CombinedChannelDuplexHandlerChannelHandlerAppender 取代.

想了解这个改变的更多信息,请看 pull request #1999 .

channelRead0() → messageReceived()

我知道,这是一个 傻傻的错误 . 如果你正在使用 SimpleChannelInboundHandler , 你不得不将 channelRead0() 重命名为 messageReceived() .

更灵活的线程模型

在Netty 4.x中, 每个 EventLoop 和一个固定的线程紧密耦合, 这个线程会执行它注册的channel的所有的I/O事件,以及指派给它的任务.

在5.0中, 一个EventLoop不再直接使用线程,而是使用一个 Executor . 也就是, 它使用一个 Executor 对象作为构造函数的参数,以前是在一个无尽的循环中拉取I/O事件, 吸纳在是每次迭代的结果是一个task,将此task提交给 Executor 执行.

如果没有特别指定, Executor 默认使用 ForkJoinPool . ForkJoinPool 使用thread-local 队列. 也就是说, 从线程A中提交到 ForkJoinPool 到非常可能再由线程A执行. 这提供了EventLoop高层次的thread affinity.

而且,程序员也可以提供他们自己的 Executor (也叫做 thread pool) 调度 EventLoop . 一个场景可以证明它有用:当 Netty用作大规模的软件系统中. 假定此系统已经使用一个线程池并发地执行它的任务. Netty 4.x简单的产生大量的线程,完全不顾它是一个大规模系统的一部分. 自Netty 5.0起, 开发者可以运行 Netty 和系统的其它部分在同一个线程池中, 通过应用更好的调度策略和较少的调度开支可以潜在地提高性能. 细节讨论可以参照 GitHub issue 2250 .

应该提到的是,这个改变不会影响 ChannelHandlers 的方式. 在开发者看来,唯一改变的是不再保证同一个 ChannelHandler 会被同一个线程执行. 然而可以保证的是, 它不会同时被两个或者两个以上的线程执行.此外, Netty 会负责内存可见性的问题,所以不必担心线程安全性和 ChannelHandler的 volatile 变量.

这个改变的另一个影响就是 NioEventLoop , NioEventLoopGroup , EpollEventLoopEpollEventLoopGroup 不再使用 ThreadFactory 作为构造函数的参数. 这些构造函数现在取而代之使用 ExecutorExecutorFactory 对象.

更好的 Channel.deregister(...)

Netty 4.0引入了 Channel.deregister(...) , 5.0中它的行为被更新了以便符合Netty的线程模型.

现在可以保证在 ChannelHandler 中提交到 EventLoop 中的所有task在Channel取消注册(deregister)前都会被EventLoop执行

然而 Channel.deregister(...) 保留了非阻塞的操作,所以你不得不等待 返回的 ChannelFuture 成功后才能将它安全的注册到另一个 EventLoop .

当调用 Channel.deregister(...) 后任何尝试在这个 ChannelHandler 中提交新的task (Runnable 或 Callable) 到会触发 RejectedExecutionException . 一旦这个 Channel 注册到另外一个 EventLoop , 一切归于正常.

ChannelHandler 通过 EventLoop.schedule*(...) 方法提交的task当Channel取消注册后会停止执行, 当Channel再次注册时这些task会自动移到新的 EventLoop 中继续执行. 这个限制只会影响在Channel取消注册时被调度的task. 那么delay的或者定期执行的task不受影响.

你也可以突破这个限制,尽管不被推荐. Netty 5.0 引入了一个新的方法 EventLoop.unwrap() , 它返回原始的 EventLoop 并不会执行一个健全的检查. 更准确的讲, 当提交task或者调度task到 "unwrapped" EventLoop时, 不会保证这些task会被并发执行,调度的task也不保证自动移到新的EventLoop.

正文到此结束
Loading...