参考:http://www.infoq.com/cn/articles/netty-version-upgrade-history-thread-part/
1.netty3.x线程模型
2.netty4.x的线程模型
upstream/inbound:主要包括TCP链路建立事件、链路激活事件、读事件、I/O异常事件、链路关闭事件等
downstream/outbound:主要包括读写事件、连接事件、监听绑定事件、刷新事件等
inbound的读事件是I/O线程发起的操作
outbound的读写事件是用户的业务线程发起的操作。比如读事件,用户线程告诉I/O线程要读取数据,此时会调用ChannelHandlerContext 的read()方法->UnSafe.beginRead()->调用Selector的read()方法->channelPipe.channelRead ()->channelPipe. fireChannelRead()->channelPipe.channelReadComplete()设置自动读取后面的数据。
3. 3.x和4.x的线程模型最大区别:
4.x中ChannelPipeline中的Handler链统一由I/O线程串行调度,无论是读还是写操作,3.x中的write操作时由业务线程处理Handler链。4.x中可以降低线程之间的上下文切换带来的时间消耗,但是3.x中业务线程可以并发执行Handler链。如果有一些耗时的Handler操作会导致4.x的效率低下,但是可以考虑将这些耗时操作放在业务线程最先执行,不放在Handler里处理。由于业务线程可以并发执行,同样也可以提高效率。
4. 4.x的串行话线程模型设计
一个NioEventLoop聚合了一个多路复用器Selector,因此可以处理成百上千的客户端连接,Netty的处理策略是每当有一个新的客户端接入,则从NioEventLoop线程组中顺序获取一个可用的NioEventLoop,当到达数组上限之后,重新返回到0,通过这种方式,可以基本保证各个NioEventLoop的负载均衡。一个客户端连接只注册到一个NioEventLoop上,这样就避免了多个I/O线程去并发操作它。
Netty通过串行化设计理念降低了用户的开发难度,提升了处理性能。利用线程组实现了多个串行化线程水平并行执行,线程之间并没有交集,这样既可以充分利用多核提升并行处理能力,同时避免了线程上下文的切换和并发保护带来的额外性能损耗。
了解完了Netty 4的串行化设计理念之后,我们继续看Netty 3线程模型存在的问题,总结起来,它的主要问题如下:
1)Inbound和Outbound实质都是I/O相关的操作,它们的线程模型竟然不统一,这给用户带来了更多的学习和使用成本;
2)Outbound操作由业务线程执行,通常业务会使用线程池并行处理业务消息,这就意味着在某一个时刻会有多个业务线程同时操作ChannelHandler,我们需要对ChannelHandler进行并发保护,通常需要加锁。如果同步块的范围不当,可能会导致严重的性能瓶颈,这对开发者的技能要求非常高,降低了开发效率;
3)Outbound操作过程中,例如消息编码异常,会产生Exception,它会被转换成Inbound的Exception并通知到ChannelPipeline,这就意味着业务线程发起了Inbound操作,即需要将该异常交由I/O线程去处理,从而产生了额外的线程的上下文切换!它打破了Inbound操作由I/O线程操作的模型,如果开发者按照Inbound操作只会由一个I/O线程执行的约束进行设计,则会发生线程并发访问安全问题。由于该场景只在特定异常时发生,因此错误非常隐蔽!一旦在生产环境中发生此类线程并发问题,定位难度和成本都非常大。