文档地址:http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf
它是从传统的阻塞式I/O开始展开,进行分析
文档首页
文档目录:
可伸缩性的服务
事件驱动处理:netty中的所有操作的都是异步的。全都以事件为核心,当发生读事件、写事件,响应的处理器都会发生一个回调。
Reactor模式:
基础版本;
多线程版本;
其他变体;
NIO非阻塞API的一些介绍
网站服务、分布式对象等;
更多的是有一些基础结构:
读请求
解码请求
处理服务
编码响应
发送响应
但是对于上面说到的每一个步骤,它们所消耗的资源本质上是不一样的:
XML解析,文件传输,网页的生成,计算服务
传统的服务设计
每个处理器都会在自己的线程里面处理,
传统的ServerSocket循环
在run方法中:serverSocket()绑定端口。在while方法中,创建一个线程,在该线程中添加处理器。
在handler方法中传入socket对象,tyr{}catch{}里面描述了一个读写的I/O操作。
个人总结:上面的传统I/O存在的问题主要是线程太多,每连接一个客户端就要创建一个线程。如果有10万个服务器连接,那你就要产生10万个线程,而且在一般服务器上如果线程数急剧增加,到达一个零界点后,这个吞吐量就会急剧下降,因为线程与线程之间的切换是比较耗费资源的。而且每个系统承载的线程是有限制的。所以传统的I/O方式只适合处理一些连接不大的网络通信。
可伸缩性的目标
当客户端增加的时候可以实现优雅降级;
在资源(CPU,内存,磁盘,宽带)增加时,能够持续的进行性能的优化;
也能够满足可用性和性能的一些目标;
短的延迟;
满足高峰期的需求。
对服务质量可以调节
“分解和攻克”(divide andconquer)通常是最好的获取完成一些可伸缩目标的方法
将Divide处理成一些更小的任务
每个任务执行的动作都是在不阻塞的情况下
在可用的时候执行每一个任务(因为任务都有可用状态和不可用状态)
每一个IO操作通常都是作为触发器来服务的。(当io操作完成之后就会触发一些事件,比如说:读、解码、计算、编码,发送)
在nio包下提供的基本机制
非阻塞的读和写
分发一些感知到IO事件的一些任务(其实也就是说当检测到IO操作后,将会触发相关事件)
存在无穷的变化可能
存在一系列的事件驱动设计
通常它比其他可供选择的方式更具效果
更少的资源
*不需要一个线程对应一个客户端
总体性能支出更少
*更少的内容切换,以及更少的锁
但是分发可能会变得更慢
*必须手动将动作绑定到事件上
通常编程是更加困难一些的
必须分解成一些简单的非阻塞动作
*相似于GUI事件驱动动作
不能消除所有的阻塞如:GC、页面失败等
必须跟踪服务的逻辑状态(毕竟是异步的,异步只有一个线程,所有可以在执行这个任务的时候,又接着执行下一个任务了,所以必须时刻监听他们的状态)
这里给了一个例子:
简单逻辑:当用户点击了AWT Event Queue事件中某个Event事件,就会触发ActionListener的监听,调用actionPerformed()方法
Event-driven IO 使用的是和它相似的想法,但是设计上却是不同的。
reactor模式
reactor响应IO事件通过分发合适的处理器,简单来说就是A事件由A处理器执行,B事件由B处理器执行
类似于AWT线程
处理器执行是非阻塞方式的
类似于AWT动作监听者
通过绑定事件处理器来进行管理,简单来说就是哪个事件产生了,哪个处理器会被调用
类似于AWT增加事件监听者
基础版本的Reactor设计
单线程版本
Channels
连接到文件、socket等,提供了非阻塞式读
Buffers
类似于数组的对象能够直接读取或者写入通过channels
selectors
通知在一组channels中哪个channel有IO事件(之前在NIO中我们讲过,channel是注册到selector里面的)
selectionKeys
维持IO事件状态和绑定
Reactor1实例:
传递一个端口号,然后通过open方法创建了selector和serverSocket对象;
serverSocket对象绑定上端口,并设置为非阻塞模式
然后配置好之后,将serverSocket注册到selector里面。返回的selectionKey调用attach方法:可以附加相应的对象
这个是runnable类中的run方法
selector.select()返回的是产生时间的数量(基本上没啥用)
获取selectionKeys集合,然后派发(dispatch)出去,清理掉完成了的key对象(selected)
在dispatch方法中获取runnable 对象,它这里其实是将上面那个new Acceptor()强制转换成Runnable对象
当进入到Acceptor进入到run 方法说明有连接建立了,创建handler并且传入selector和socketChannel对象
这里说一下sel.wakep()方法其实主要是当selector调用select之后再阻塞状态下会立即返回。
这里要注意一下:在netty3里面,直接通过判断state状态是READING或者SENDING来调用方法;在netty4里面是直接通过调用方法来实现,相比来说看起来代码更加优雅。