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

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

前言

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

源码阅读

Channel 注册到 Worker 线程组上
register()架构

调用 NioEventLoopGroupnext() 从 Worker 线程组中获取一个 eventLoop
next()并发

根据线程组个数不一样,会调用 PowerOfTwoEventExecutorChooser 或者 GenericEventExecutorChoosernext() 方法,若是线程数是 2 的 N 次方,就选用 PowerOfTwoEventExecutorChooser 这个 EventLoop 选择类,使用位运算提升效率
choose executor异步

调用选取的 eventLoopregister() 方法,能够看到,将 this 也就是当前 EventLoop 当作参数传入 promise.channel().unsafe().register() 方法
eventLoop.register()oop

继续进到 promise.channel().unsafe().register 方法,到这里,终于将 eventLoop 赋值给了 Channel,即 ChanneleventLoop 创建了绑定关系。
channel - eventloop性能

但Channel还未与线程绑定,继续往下看,当咱们平时在Handler里调用 ctx (即 ChannelHandlerContext 类对象)的 write() 时,实际是获取 ctxexecutor 执行写操纵事件,若未给 ctx 指定 executor,则 ctx 会使用 对应的 channeleventLoop
ctx.eventLoop()this

执行 eventLoopexecute() 方法
executor.execute()spa

进到 execute() 方法内,先经过调用 inEventLoop() 方法,判断当前线程是不是 eventLoop 绑定的那个线程
eventLoop.inEventLoop()线程

若是不是,则可能 eventLoop 尚未绑定线程,则调用 startThread 方法建立一个线程
eventLoop.execute()

最终调用 eventLoopdoStartThread() ,由 executor 指定建立线程的任务。
eventLoop.doStartThread()

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

总结

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

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

相关文章
相关标签/搜索