Linux背景
流:阻塞模式、非阻塞模式
R/W方式演进:
[流在阻塞模式]
通过I/O事件来同步写入和读取进程
[非阻塞忙轮询]
流切换为非阻塞模式。
缺点:如果所有的流都没有数据,那么只会白白浪费CPU。
[非阻塞轮询]
引入select/poll代理来监听I/O事件。
在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,于是我们的程序就会轮询一遍所有的流。
缺点:O(n)的无差别轮询复杂度。处理的流越多,无差别轮询时间就越长。
[epoll] NIO
epoll会把哪个流发生了怎样的I/O事件通知我们。
复杂度降低到了O(1)。
http://www.importnew.com/24794.html
BIO NIO AIO
BIO : 同步并阻塞,服务器实现模式为一个连接一个线程
NIO : 同步非阻塞,服务器实现模式为一个请求一个线程
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理
https://blog.csdn.net/skiof007/article/details/52873421
几种线程职责(实际中可能1个线程有多个职责)
- 处理accept事件Thread
- IO Thread:负责R/W数据
- Data Handler Thread:负责对数据进一步处理
一般的处理流程
- Socket连接请求事件(accept)处理
- 等待客户端数据就绪
- R/W 操作
- handle data
首先要明确一点:一次客户端连接周期中,是可以有多次的请求
。BIO与NIO的区别不是在于对于socket连接请求事件处理
这步的处理上,而是在于等待客户端数据就绪
这步:
BIO的
阻塞
与NIO的非阻塞
体现在IO Thread R/W socket
上:
BIO而言,在该IO Thread
对socket的就绪状态获取方式是阻塞的。例如读取时,reader.readLine()
包含了就绪状态获取和数据读取2步,如果没有就绪则一直处理线程是阻塞
NIO而言,selector.selectedKeys()
获取有注册事件到达的channel(具体实现通过Linux的select/epoll),IO Thread
处理的都是就绪状态的socket(因此IO Thread
可以非阻塞
读取数据)推荐结合代码看看。
NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。
NIO是一种同步非阻塞的I/O模型,也是I/O多路复用的基础。
https://www.jianshu.com/p/1ccbc6a348db
在理解了NIO的基础上,看AIO,区别在于AIO是等读写过程完成后再去调用回调函数。
NIO是同步非阻塞的
(同步指的是netty通过select/poll
向IO查询数据包是否到达)
AIO是异步非阻塞的
(异步指的是操作系统通知进程,数据包被复制到哪个内存区域,去读取)
https://my.oschina.net/hosee/blog/615269
I/O流 vs Buffer
基于buffer
传统的I/O是面向字节流或字符流的,以流式的方式顺序地从一个Stream 中读取一个或多个字节, 因此也就不能随意改变读取指针的位置。在NIO中, 抛弃了传统的 I/O流, 而是引入了Channel 和 Buffer的概念. 在NIO中, 只能从Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel。
基于buffer操作不像传统IO的顺序操作, NIO 中可以随意地读取任意位置的数据
https://juejin.im/post/5bea1d2e51882523d3163657
事件分发器模式
事件分发器的两种模式称为:Reactor和Proactor。
Reactor模式是基于同步I/O的,而Proactor模式是基于异步I/O的。
https://www.jianshu.com/p/1ccbc6a348dbReactor模型有3个变种:
- 单Reactor单线程
- 单Reactor多线程
- 主从Reactor多线程
特别说明的是: 虽然Netty的线程模型基于
主从Reactor多线程
,借用了MainReactor和SubReactor的结构,但是实际实现上,SubReactor和Worker线程在同一个线程池中。
https://juejin.im/post/5bea1d2e51882523d3163657

https://www.jianshu.com/p/1ccbc6a348db
Netty 与 Reactor模式对应
结构对应:
NioEventLoop ———— Initiation Dispatcher
Selector ———— Synchronous EventDemultiplexer
ChannelHandler ———— Event Handler
具体的ChannelHandler的实现 ———— ConcreteEventHandler模式对应:
Netty服务端使用了多Reactor线程模式
mainReactor ———— bossGroup(NioEventLoopGroup) 中的某个NioEventLoop
subReactor ———— workerGroup(NioEventLoopGroup) 中的某个NioEventLoop
acceptor ———— ServerBootstrapAcceptor
ThreadPool ———— 用户自定义线程池
https://www.jianshu.com/p/1ccbc6a348db
Netty

https://juejin.im/post/5bea1d2e51882523d3163657
总结:
1个
NioEventLoopGroup
包含多个NioEventLoop
;每个
NioEventLoop
有属于自己的1个Selector
和1个taskQueue
Boss Group
包含1个NioEventLoopGroup
;Worker Group
可以包含多个NioEventLoopGroup
;(修正)
NioEventGroup
应写为NioEventLoopGroup
有个问题:数据怎么写回客户端
A: 实际上当客户机有消息发送过来时会调用channelRead0(ChannelHandlerContext ctx, String msg)
这个方法,处理完发来的数据后直接向channel(ChannelHandlerContext ctx
)写回即可
https://www.jianshu.com/p/222fe9f2a564
Summary
- 无论什么情况,read/write操作本身都是‘阻塞’的。阻塞/非阻塞区别在于read/write操作之前的就绪状态是如何得到的
- NIO是一种同步非阻塞的I/O模型。Netty基于NIO
Main Ref
https://www.jianshu.com/p/1ccbc6a348db
https://juejin.im/post/5bea1d2e51882523d3163657
https://blog.zhenlanghuo.top/2017/05/21/IO%E4%B8%AD%E7%9A%84%E5%90%8C%E6%AD%A5%E5%BC%82%E6%AD%A5%E3%80%81%E9%98%BB%E5%A1%9E%E9%9D%9E%E9%98%BB%E5%A1%9E/
Netty源码(占小狼):
https://www.jianshu.com/p/e577803f0fb8
https://www.jianshu.com/p/6b48196b5043
https://www.jianshu.com/p/1ad424c53e80
网友评论