原文链接:http://xueliang.org/article/detail/20200712234015993
前言
在Netty的线程模型中,对于一个TCP连接的读写操作,都是由一个单线程完成的,对于刚入门Netty的新手,这完全颠覆我们熟知的多线程能够加快处理速度,缩短处理时间的常规思路。
实际上,Netty采用了异步通信模式,一个IO 线程可以并发处理N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞IO 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。
源码阅读
将 Channel
注册到 Worker 线程组上
调用 NioEventLoopGroup
的 next()
从 Worker 线程组中获取一个 eventLoop
根据线程组个数不同,会调用 PowerOfTwoEventExecutorChooser
或者 GenericEventExecutorChooser
的 next()
方法,如果线程数是 2 的 N 次方,就选用 PowerOfTwoEventExecutorChooser
这个 EventLoop
选择类,使用位运算提高效率
调用选取的 eventLoop
的 register()
方法,可以看到,将 this
也就是当前 EventLoop
当做参数传入 promise.channel().unsafe().register()
方法
继续进到 promise.channel().unsafe().register
方法,到这里,终于将 eventLoop
赋值给了 Channel
,即 Channel
与 eventLoop
建立了绑定关系。
但Channel还未与线程绑定,继续往下看,当我们平时在Handler里调用 ctx
(即 ChannelHandlerContext
类对象)的 write()
时,实际是获取 ctx
的 executor
执行写操纵事件,若未给 ctx
指定 executor
,则 ctx
会使用 对应的 channel
的 eventLoop
执行 eventLoop
的 execute()
方法
进到 execute()
方法内,先通过调用 inEventLoop()
方法,判断当前线程是否是 eventLoop
绑定的那个线程
如果不是,则可能 eventLoop
还没有绑定线程,则调用 startThread
方法创建一个线程
最终调用 eventLoop
的 doStartThread()
,由 executor
指定创建线程的任务。
到此,Channel - EventLoop - Thread 绑定在了一起,同时也能看出多个 Channel 可能绑定到 一个EventLoop上
总结
Netty将一个TCP连接和一个固定的线程绑定,不需要进行线程切换以及线程同步,即节省资源又提高吞吐效率,除此之外我们在阅读源码的过程中,从EventLoop的选取,根据不同的线程数,使用不同的轮询器,可以看出Netty对于高性能的极致追求。