`
maosheng
  • 浏览: 550124 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

高性能 IO 设计 Reactor 模式

NIO 
阅读更多
传统的服务器设计是:一连接一处理线程,也就是我们常说的 BIO 编程。BIO 在读取和发送时是阻塞的,在请求的整个生命周期内,不管有没有数据可读或待发送,都绑定和占用了这个处理线程。

BIO 多线程并发模式,一个连接一个线程的优点是:
  一定程度上极大地提高了服务器的吞吐量,因为之前的请求在read阻塞以后,不会影响到后续的请求,因为他们在不同的线程中。这也是为什么通常会讲“一个线程只能对应一个socket”的原因。另外有个问题,如果一个线程中对应多个socket连接不行吗?语法上确实可以,但是实际上没有用,每一个socket都是阻塞的,所以在一个线程里只能处理一个socket,就算accept了多个也没用,前一个socket被阻塞了,后面的是无法被执行到的。

BIO 多线程并发模式,一个连接一个线程的缺点是:
  缺点在于资源要求太高,系统中创建线程是需要比较高的系统资源的,如果连接数太高,系统无法承受,而且,线程的反复创建-销毁也需要代价。

改进方法是:

  采用基于事件驱动的设计,当有事件触发时,才会调用处理器进行数据处理。使用Reactor模式,对线程的数量进行控制,一个线程处理大量的事件。


传统模型中,线程的处理单元是一次完整的请求,为了把线程解放出来,Reactor 对这个处理单元进行了分解。


Reactor 之所以高效是因为采用了分而治之和事件驱动的设计。大部分网络服务像 Web 服务器、分布式对象的通信等大多数具有相同的基本处理流程:

读取请求数据 - read
按协议解析请求 - decode
业务处理 - process
按协议编码内容生成响应 - encode
发送响应 - send


1. 分而治之
Reactor 模式将处理过程分为多个小任务,每个任务执行一个非阻塞的操作,任务通常由一个 IO 事件触发执行。这种机制在 java.nio 中提供了支持:

非阻塞的读和写
调度与发生的 IO 事件关联的任务

BIO 线程是以 read->decode->process->encode->send 的顺序串行处理

NIO 将其分成了三个执行单元:读取、业务处理和发送,处理过程如下:
读取:如果无数据可读,线程返回线程池;发生读IO事件,申请一个线程处理读取,读取结束后处理业务

业务处理:线程同步处理完业务后,生成响应内容并编码,返回线程池

发送:发生写IO事件,申请一个线程进行发送

可以看出一个明显的区别就是,一次请求的处理过程是由多个不同的线程完成的,感觉和指令的串行执行和并行执行有点类似。

分而治之的关键在于非阻塞,这样就能充分利用线程,压榨 CPU,提高系统的吞吐能力。

2. 事件驱动
通常比其他模型更高效,它使用的资源更少,不用针对每个请求启用一条线程,减少了上下文切换,减少阻塞。但任务调度可能会慢,必须手动将事件和处理动作绑定。

通常编程比较困难,它必须为服务设计多个逻辑状态,以便跟踪和中断恢复,这也是在非阻塞编程中有大量状态机运用的原因。

NIO 中总共设计了 4 种事件,每个事件发生都会调度关联的任务,分别是:

OP_ACCEPT: 服务端监听到了一个连接,准备接收
OP_CONNECT: 客户端与服务器连接建立成功
OP_READ: 读事件就绪,通道有数据可读
OP_WRITE: 写事件就绪,可以向通道写入数据

java.nio 对事件驱动也提供了支持:
Channels: 连接到文件、Socket 等,支持非阻塞读取
Buffers: 类似数组的对象,可由通道直接读取或写入
Selectors: 通知哪组通道有 IO 事件
SelectionKeys: 维护 IO 事件的状态和绑定信息


3. Reactor 模式
Reactor 是一种设计模式,wikipedia 对其定义如下:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.


Reactor 是一个或多个输入事件的处理模式,用于处理并发传递给服务处理程序的服务请求。服务处理程序判断传入请求发生的事件,并将它们同步的分派给关联的请求处理程序。

可以看出以下关键点 :

1)事件驱动(event handling)

2)可以处理一个或多个输入源(one or more inputs)

3)通过Service Handler同步的将输入事件(Event)采用多路复用分发给相应的Request Handler(多个)处理


Reactor 模式按照职责不同,通常可以把线程分为 Reactor 线程、IO 线程和业务线程:

Reactor 线程:轮询通知发生IO的通道,并分派合适的 Handler 处理
IO 线程:执行实际的读写操作
业务线程:执行应用程序的业务逻辑


自POSA2 中的关于Reactor Pattern 介绍中,我们了解了Reactor 的处理方式:

1)同步的等待多个事件源到达(采用select()实现)

2)将事件多路分解以及分配相应的事件服务进行处理,这个分派采用server集中处理(dispatch)

3)分解的事件以及对应的事件服务应用从分派服务中分离出去(handler)


基于Reactor Pattern 处理模式中,定义以下三种角色:

1)Reactor 将I/O事件分派给对应的Handler

2)Acceptor 处理客户端新连接,并分派请求到处理器链中

3)Handlers 执行非阻塞读/写 任务










分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics