当前位置: 代码迷 >> 综合 >> Netty的深入浅出--47.Scalable IO in Java的单线程版本分析-上(二)
  详细解决方案

Netty的深入浅出--47.Scalable IO in Java的单线程版本分析-上(二)

热度:3   发布时间:2023-11-29 21:57:32.0

文档地址: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里面是直接通过调用方法来实现,相比来说看起来代码更加优雅。

  相关解决方案