传统的服务器设计是:一连接一处理线程,也就是我们常说的 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 执行非阻塞读/写 任务
分享到:
相关推荐
服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种: (1)同步阻塞IO(Blocking IO):即传统的IO模型。 (2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置...
这篇文章是在《程序员》上发表的 在豆丁上要3分 拿过来分享下 要个1分 安慰下
Scalable IO in Java是java.util.concurrent包的作者,大师Doug Lea关于分析与构建可伸缩的高性能IO服务的一篇经典文章,在文章中Doug Lea通过各个角度,循序渐进的梳理了服务开发中的相关问题,以及在解决问题的...
(3)IO多路复用(IOMultiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。(4)异步IO(AsynchronousIO):即经典的Proactor设计模式,也称为异步非...
分析常见高性能网络设计模式,涵盖了reactor, proactor。
NIO是基于Reactor模式,面向缓冲区并结合通道的IO模型。客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求就进行处理。 AIO : Asynchronous IO,即异步非阻塞,采用了 Proactor 模式,特点...
11.4 高性能定时器 11.4.1 时间轮 11.4.2 时间堆 第12章 高性能IO框架库Libevent 12.1 IO框架库概述 12.2 Libevent源码分析 12.2.1 一个实例 12.2.2 源代码组织结构 12.2.3 event结构体 12.2.4 往注册事件...
genericgcc version 7.5.0Ubuntu 18.04.1cmake version 3.17.0Build ./build.shDescription 基于C++11、部分C++14/17特性的一个高性能并发网络服务器,包括目前已实现日志、线程池、内存池、定时器、网络io等...
说起,然后接着阐述了阻塞和非阻塞的区别,接着介绍了阻塞IO和非阻塞IO的区别,然后介绍了同步IO和异步IO的区别,接下来介绍了5种IO模型,后介绍了两种和高性能IO设计相关的设计模式(Reactor和Proactor)。...
57_Reactor模式透彻理解及其在Netty中的应用 58_Reactor模式与Netty之间的关系详解 59_Acceptor与Dispatcher角色分析 60_Netty的自适应缓冲区分配策略与堆外内存创建方式 61_Reactor模式5大角色彻底分析 62_Reactor...
DougLee可扩展的网络服务事件驱动Reactor模式基础版多线程版其他变体java.io包中分阻塞IOAPI一览Web服务器,分布式对象系统等等它们的共同特点Read请求解码请求报文业务处理编码响应报文发送响应实际应用中每一个...
基于Reactor模式,效率较高,并且代码精简(4.15版本8000多行),是学习事件驱动编程的很好的资源。 下载链接: Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它...