Netty解析-线程模型
本文中心路线
- 传统IO模型(NIO出现之前,也就是BIO)
- Reactor模型(异步、非阻塞,事件驱动模型)
- Netty对Reactor模型的实现
1、传统IO模型(NIO出现之前,也就是BIO)
我们先来看BIO的线程通讯模型
解释
- Application(应用端)发起请求(用户线程)到Kernel(计算机内核)
- 内核处理中数据尚未准备好,测试Application端请求处于等待数据状态
- 直到Kernel数据处理完毕,将数据从内核拷贝到用户线程
- 拷贝完数据后返回到Applicatoin,处理数据
题外话:整个过程,线程从发起从用户态到内核态再到用户态都是同步的,并且用户线程一直处于等待状态直到结果返回
2、Reactor模型(异步、非阻塞,事件驱动模型)
Reactor模型(反应器模式)分为3类:单线程、多线程、主从多线程
1、单线程Reactor模型
解释
- 多个Client同时发送请求到Server后注册通知事件,直接返回,不阻塞等待(Selecter单线程死循环扫描)
- Server端用一个线程去接收请求并分配Handler去处理(每个handler是一个线程)
- Handler处理完毕后调用第1步注册的通知事件返回结果
缺点:
虽然我们实现了Client请求不阻塞,请求异步处理,单图中可看到接收请求并分配handler仍是单线程处理,依然存在问题
1、单线程浪费CPU资源
2、如果在图中单线程部分有处理异常,就会出现阻塞,Client并发太多时也会出现瓶颈
所以引来多线程
2、多线程Reactor模型
解释
- 和单线程相比目前只有接收请求线程的地方是处于单线程
- 接收线程分配handler处理时使用线程池技术进行多线程异步处理,handler之间互不影响
缺陷:
我们看到acceptor线程部分仍处于单线程接收状态,如果有大IO操作势必影响其它
从而引入了主从多线程
3、主从多线程Reactor模型
解释:
1、与多线程相比,Reactor由一个分为mainReactor和subReactor
2、mainReactor:主要用于接收客户端请求,将接收到的SocketChannel传递给subReactor,由subReactor负责跟客户端进行通讯
3、好处:mainReactor只负责接收客户端请求并适配subReactor不负责真正通讯,每个subReactor线程存在独立的Selector、线程、事件逻辑,不会因为大IO阻塞其它线程
3、Netty对Reactor模型的实现
1、模式对应:
Netty服务端使用了“多Reactor线程模式”
mainReactor ———— bossGroup(NioEventLoopGroup) 中的某个NioEventLoop
subReactor ———— workerGroup(NioEventLoopGroup) 中的某个NioEventLoop
acceptor ———— ServerBootstrapAcceptor
ThreadPool ———— 用户自定义线程池
2、Netty启动流程(摘自网络,尚未验证,大概流程应该正确)
- 当服务器程序启动时,会配置ChannelPipeline,ChannelPipeline中是一个ChannelHandler链,所有的事件发生时都会触发Channelhandler中的某个方法,这个事件会在ChannelPipeline中的ChannelHandler链里传播。然后,从bossGroup事件循环池中获取一个NioEventLoop来现实服务端程序绑定本地端口的操作,将对应的ServerSocketChannel注册到该NioEventLoop中的Selector上,并注册ACCEPT事件为ServerSocketChannel所感兴趣的事件。
- NioEventLoop事件循环启动,此时开始监听客户端的连接请求。
- 当有客户端向服务器端发起连接请求时,NioEventLoop的事件循环监听到该ACCEPT事件,Netty底层会接收这个连接,通过accept()方法得到与这个客户端的连接(SocketChannel),然后触发ChannelRead事件(即,ChannelHandler中的channelRead方法会得到回调),该事件会在ChannelPipeline中的ChannelHandler链中执行、传播。
- ServerBootstrapAcceptor的readChannel方法会该SocketChannel(客户端的连接)注册到workerGroup(NioEventLoopGroup) 中的某个NioEventLoop的Selector上,并注册READ事件为SocketChannel所感兴趣的事件。启动SocketChannel所在NioEventLoop的事件循环,接下来就可以开始客户端和服务器端的通信了。