Netty源码阅读之如何将TCP的读写操作和指定线程绑定

原文链接http://xueliang.org/article/detail/20200712234015993

前言

在Netty的线程模型中,对于一个TCP连接的读写操作,都是由一个单线程完成的,对于刚入门Netty的新手,这完全颠覆我们熟知的多线程能够加快处理速度,缩短处理时间的常规思路。
实际上,Netty采用了异步通信模式,一个IO 线程可以并发处理N 个客户端连接和读写操作,这从根本上解决了传统同步阻塞IO 一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

源码阅读

Channel 注册到 Worker 线程组上
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第1张图片

调用 NioEventLoopGroupnext() 从 Worker 线程组中获取一个 eventLoop
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第2张图片

根据线程组个数不同,会调用 PowerOfTwoEventExecutorChooser 或者 GenericEventExecutorChoosernext() 方法,如果线程数是 2 的 N 次方,就选用 PowerOfTwoEventExecutorChooser 这个 EventLoop 选择类,使用位运算提高效率
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第3张图片

调用选取的 eventLoopregister() 方法,可以看到,将 this 也就是当前 EventLoop 当做参数传入 promise.channel().unsafe().register() 方法
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第4张图片

继续进到 promise.channel().unsafe().register 方法,到这里,终于将 eventLoop 赋值给了 Channel,即 ChanneleventLoop 建立了绑定关系。
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第5张图片

但Channel还未与线程绑定,继续往下看,当我们平时在Handler里调用 ctx (即 ChannelHandlerContext 类对象)的 write() 时,实际是获取 ctxexecutor 执行写操纵事件,若未给 ctx 指定 executor,则 ctx 会使用 对应的 channeleventLoop
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第6张图片

执行 eventLoopexecute() 方法
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第7张图片

进到 execute() 方法内,先通过调用 inEventLoop() 方法,判断当前线程是否是 eventLoop 绑定的那个线程
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第8张图片

如果不是,则可能 eventLoop 还没有绑定线程,则调用 startThread 方法创建一个线程
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第9张图片

最终调用 eventLoopdoStartThread() ,由 executor 指定创建线程的任务。
Netty源码阅读之如何将TCP的读写操作和指定线程绑定_第10张图片

到此,Channel - EventLoop - Thread 绑定在了一起,同时也能看出多个 Channel 可能绑定到 一个EventLoop上

总结

Netty将一个TCP连接和一个固定的线程绑定,不需要进行线程切换以及线程同步,即节省资源又提高吞吐效率,除此之外我们在阅读源码的过程中,从EventLoop的选取,根据不同的线程数,使用不同的轮询器,可以看出Netty对于高性能的极致追求。

原文链接http://xueliang.org/article/detail/20200712234015993

你可能感兴趣的:(java,netty)