《Netty官方文档》5.0中的变化和注意点

原文地址  译者:叶扬V

这篇文档将引领你了解netty4.1 release版本之后所做的一系列显著升级和新特性,以便让你能把应用升级到新版本。

不像netty3.X4.0之间的升级变化,5.0版本虽然在设计上做出了重大突破和简化,但(在调用层面)并没有改变很多。我们尽可能让4.X版本可以平滑地升级到5.0版本,但是如果你在升级过程中遇到任何问题,请告知我们。

变化要点

精简handler类型继承关系

ChannelInboundHandler和ChannelOutboundHandler被合并成ChannelHandler。现在ChannelHandler中既有inbound handler函数也有outbound handler函数。

ChannelInboundHandlerAdapterChannelOutboundHandlerAdapterChannelDuplexHandlerAdapter被弃用然后替换为ChannelHandlerAdapter

因为现在已经无法区分一个handlerinbound handler还是outbound handler,所以CombinedChannelDuplexHandler已经被替换为ChannelHandlerAppender

如果需要更多关于handler继承关系的变化,请参考the pull request #1999

channelRead0() → messageReceived()

我知道,这(译者注:netty4中的channelRead0())是一个愚蠢的错误。如果你已经使用了SimpleChannelInboundHandler,你就必须将channelRead0()函数重命名为messageReceived()

更灵活的线程模型

Netty 4.X中,每个EventLoop都与一个固定的线程紧密耦合,这个线程会执行它注册的Channels的所有I/O事件,也会执行所有提交给它的任务。

5.0版本开始,一个EventLoop(的构造)不再直接使用线程,取而代之的是使用Executor。也就是说,它使用Executor对象作为构造函数的入参。而且,以前的方式是在循环中拉取I/O事件,现在的方式是每次迭代得到一个task,再将task提交给Executor执行。

如果没有特别指定的话,Executor默认是一个ForkJoinPoolForkJoinPool的特点是使用thread-local队列。也就是说,一个从线程A提交到ForkJoinPool的任务非常可能再次被线程A执行。这极大的提高了EventLoops的线程关联性。

而且,开发者可以使用自己的Executor(也叫做thread pool)接管EventLoops的调度。当netty只作为一个大型软件系统中作的部分时,这种设计就显得非常有用。假设一个系统已经在使用高并发处理它的任务,Netty 4.x加入时会简单的开启自己的线程,而丝毫不顾netty只是这个大型系统的一部分。从netty5.0开始,开发者可以将netty和系统中其它部分放入一个线程池中,通过使用更优的调度策略和较少的调度开支来潜在地提高性能。细节变化的讨论可以参考GitHub issue 2250

值得一提的是,这个变化并未改变ChannelHandlers的开发方式。从开发者的视角来看,唯一改变的是,一个ChannelHandler将不再一定会被同一个线程一直执行;仍然可保证的是,它不会被多个线程同时执行。Netty会负责内存可见性的问题,所以不用担心线程安全和ChannelHandlervolatile变量。

这个改变还有一个影响,NioEventLoop, NioEventLoopGroup, EpollEventLoop and EpollEventLoopGroup都不再使用ThreadFactory对象作为自己的构造函数入参。这些类的构造入参已经升级为Executor或者ExecutorFactory对象。

更好的Channel.deregister(...)

Netty 4.0引进了Channel.deregister(...),它的行为在5.0版本中被更新用以更符合Netty线程模型。

现在可以确保在一个ChannelHandler中提交到EventLoop的所有任务都会在Channel被注销之前被EventLoop执行。然而,Channel.deregister(…)仍然是一个非阻塞的操作,所以我们必须等到ChannelFuture成功返回数据,然后才能安全的把Channel重新注册到另一个EventLoop上。

在已经调用了Channel.deregister(...)后,任何尝试在ChannelHandler中提交新任务(Runnable or Callable)EventLoop的行为将会抛出RejectedExecutionException异常。只有这个Channel重新注册到另一个EventLoop后,一切才会恢复正常。

ChannelHandler通过EventLoop.schedule*(...)函数提交的任务会在Channel被注销后停止执行。当这个Channel重新注册时,任务会自动转移到新的EventLoop继续执行。这个限制只会影响那些当Channel被注销时被调度的任务。那些delay或者定时执行的任务不会受到影响。

你可以绕过上面的限制,但我们不推荐这样做。Netty 5.0引进了一个新函数EventLoop.unwrap(),这个函数可以返回一个原始的EventLoop,这个EventLoop并不执行任何健全性检查。更准确的讲, 当提交任务或者调度任务到 “unwrapped” EventLoop, 不会保证这些任务被并发执行,调度的任务也不保证被自动移到新的EventLoop.

 

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/113529.html

(0)
上一篇 2021年8月27日
下一篇 2021年8月27日

相关推荐

发表回复

登录后才能评论