前言
Reactor多线程如果在Google上搜索"Netty 高性能 易用",在找到的一大批文章,你大概率会看到这张图,外加关键字
NIO
,Reactor多线程模型
,异步串行无锁化
,堆外内存
,pipeline
,翻看完这些文章后可以让你对Netty的原理有大致了解,但是Netty如何实现这些的呢? 本文将尽可能简单的解释Netty中Reactor多线程的实现,如有错误感谢指出.
Selector
Selector是NIO的重要组件, Selector上可以注册Channel. Channel在注册的时候会标注自己感兴趣的事件:
- OP_CONNECT: 可连接 这个事件仅有客户端能使用
- OP_ACCEPT: 新的客户端接入,即新的客户端连接.这个事件能且仅能服务端使用.
- OP_READ: 可读 可以从Channel中读取数据
- OP_WRITE: 可写 可以向Channel中写入数据
Channel,通道,为了便于理解,我把它分为三类
- ServerSocketChannel 服务端Channel 它负责
新连接接入
- SocketChannel 客户端Channel, 它负责客户端与服务端的
连接,读,写
- SocketChannel ServerSocketChannel收到连接请求时,会生成一个SocketChannel,它负责后续服务端与这个客户端的
读,写
Reactor多线程模型
Reactor多线程Reactor多线程模型可以分为三块
- 1~3 mainReactor负责客户端接入
- 4~5 acceptor负责将接入的连接移交给subReactor
- 6~9 subReactor负责连接的读写
mainReactor负责客户端接入
- serverSocketChannel注册到mainSelector上,监听新连接接入(OP_ACCEPT)
- client1开始连接
- mainSelector通知serverSocketChannel有新连接接入
acceptor负责将接入的连接移交给subReactor
-
serverSocketChannel接受新连接,获得socketChannel
-
socketChannel按照规则(轮询调度)注册到subSelector{n}上,监听读事件(OP_READ)
subReactor负责连接的读写
-
client发送数据
-
subSelector通知SocketChannel有数据到达,socketChannel做相应的业务处理
-
socketChannel需要对client发送数据,先向subSelector监听可写事件(OP_WRITE).
-
subSelector通知socketChannel可写,socketChannel进行相应的业务处理
关键知识:
- Selector,多路复用器(multiplexer),是NIO的关键基础组件,主要功能是多路复用
- acceptor是mainReactor和subReactor的连接桥梁. 运行时,它只是作为mainReactor中的一段逻辑存在.
- subReactor有多个,新的连接会按照规则选择其中一个进行注册监听.
多线程
说的subReactor - Selector和线程的关系是1:1.也就是说 mainReactor占据一个线程, 每个subReactor占据一个线程.
NioEventLoop
NioEventLoopNioEventLoop就是
对Reactor的封装
. EventLoopGroup的基本组成如下图,结合Reactor模型,上面的mainReactor就是只有一个NioEventLoop的NioEventLoopGroup
(为什么只有一个? 因为ServerSocketChannel只有一个),subReactor就是有多个NioEventLoop的EventLoopGroup
- task queue 是一个 MPSCQ( multi producer single consumer queue),Netty中,channel的很多操作在执行前都会检查是否在对应的NioEventLoop线程中(SingleThreadEventExecutor.inEventLoop()),如果不在会将操作封装成任务丢到task queue里,以此实现Netty的
异步串行无锁化
- schedule task queue 是一个非线程安全的Priority Queue
运行流程图
NioEventLoop运行流程关键知识:
- 当外部线程第一次向NioEventLoop的task queue放入任务时,会启动NioEventLoop对应的线程并开始这个流程
- 就绪事件包含所有事件类型.OP_CONNECT(客户端会用到),OP_ACCEPT,OP_READ,OP_WRITE, 也就是说mainReactor和subReactor都在这里处理.
- selector阻塞获取阶段和运行task queue中的所有task阶段的运行时间有一个平衡, 确保事件处理和任务执行的平衡.
- schedule task在运行结束后会放回到schedule task queue里
ChannelPipeline
ChannelPipelineChannelPipeline的设计思想是责任链设计模式,是由ChannelHandlerContext组成的双向链表
, ,首尾固定为HeadContext
和TailContext
,它们作为哨兵存在.当我们添加一个ChannelHandler到ChannelPipeline时,会先包装成ChannelHandlerContext
再添加进去.
inbound事件传播
客户端向服务端发送消息,这个流向就称为inbound. 消息会从Head开始由左向右传递直到Tail,由Tail进行收尾操作
outbound事件传播
服务端向客户端发送信息,这个流向称为outbound,消息会从Tail开始由右向左传递知道Head,由Head进行收尾操作
异常传递
当某个ChannelHandler操作抛出异常,会从该handler开始向Tail传递.由Tail做收尾操作.
结语
学习Netty,要理解Reactor模型,并把它和Netty的实现结合起来, 我学习Netty的时候就因为这块认识不深刻,浪费了很多时间也没有成效,共勉
https://blog.csdn.net/difffate/article/details/69458588
https://blog.csdn.net/jjzhk/article/details/39553613
https://www.jianshu.com/p/a9b2fec31fd1
https://www.jianshu.com/p/a9d030fec081
https://juejin.im/post/5b4570cce51d451984695a9b
网友评论