原文地址 译者:叶扬V
这篇文档将引领你了解netty在4.1 release版本之后所做的一系列显著升级和新特性,以便让你能把应用升级到新版本。
不像netty在3.X和4.0之间的升级变化,5.0版本虽然在设计上做出了重大突破和简化,但(在调用层面)并没有改变很多。我们尽可能让4.X版本可以平滑地升级到5.0版本,但是如果你在升级过程中遇到任何问题,请告知我们。
变化要点
精简handler类型继承关系
ChannelInboundHandler和ChannelOutboundHandler被合并成ChannelHandler。现在ChannelHandler中既有inbound handler函数也有outbound handler函数。
ChannelInboundHandlerAdapter、ChannelOutboundHandlerAdapter和ChannelDuplexHandlerAdapter被弃用然后替换为
ChannelHandlerAdapter
。
因为现在已经无法区分一个handler是inbound 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默认是一个
ForkJoinPool
。ForkJoinPool的特点是使用
thread-local队列。也就是说,一个从线程A提交到ForkJoinPool的任务非常可能再次被线程A执行。这极大的提高了EventLoops的线程关联性。
而且,开发者可以使用自己的Executor(也叫做
thread pool)接管EventLoops的调度。当netty只作为一个大型软件系统中作的部分时,这种设计就显得非常有用。假设一个系统已经在使用高并发处理它的任务,
Netty 4.x加入时会简单的开启自己的线程,而丝毫不顾netty只是这个大型系统的一部分。从netty5.0开始,开发者可以将netty和系统中其它部分放入一个线程池中,通过使用更优的调度策略和较少的调度开支来潜在地提高性能。细节变化的讨论可以参考GitHub issue 2250。
值得一提的是,这个变化并未改变ChannelHandlers
的开发方式。从开发者的视角来看,唯一改变的是,一个ChannelHandler将不再一定会被同一个线程一直执行;仍然可保证的是,它不会被多个线程同时执行。Netty会负责内存可见性的问题,所以不用担心线程安全和ChannelHandler的volatile变量。
这个改变还有一个影响,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