Reactor 典型的 NIO 编程模型
Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 编程模型的思想,大部分 NIO 框架和一些中间件的 NIO 编程都与它一样或是它的变体。本文结合 PPT 按照自己的理解整理而来,最终编写了一个简单的 NIO 回显服务。
Reactor 之所以高效是因为采用了分而治之和事件驱动的设计。大部分网络服务像 Web 服务器、分布式对象的通信等大多数具有相同的基本处理流程:
- 读取请求数据 - read
- 按协议解析请求 - decode
- 业务处理 - process
- 按协议编码内容生成响应 - encode
- 发送响应 - send
传统的服务器设计是:一连接一处理线程,也就是我们常说的 BIO 编程。BIO 在读取和发送时是阻塞的,在请求的整个生命周期内,不管有没有数据可读或待发送,都绑定和占用了这个处理线程。
传统模型中,线程的处理单元是一次完整的请求,为了把线程解放出来,Reactor 对这个处理单元进行了分解。
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 对其定义如下:
Reactor 是一个或多个输入事件的处理模式,用于处理并发传递给服务处理程序的服务请求。服务处理程序判断传入请求发生的事件,并将它们同步的分派给关联的请求处理程序。
Reactor 模式按照职责不同,通常可以把线程分为 Reactor 线程、IO 线程和业务线程:
- Reactor 线程:轮询通知发生IO的通道,并分派合适的 Handler 处理
- IO 线程:执行实际的读写操作
- 业务线程:执行应用程序的业务逻辑
PPT 中介绍了三种版本的实现。
3.1 单线程版本

单线程版本就是用一个线程完成事件的通知、实际的 I/O 操作和业务处理。Reactor 作用就是要迅速的触发 Handler ,显然 Handler 处理的过程会导致 Reactor 变慢,此时可以将 非 IO 操作从 Reactor 线程中分离。
3.2 多线程版本

多线程版本将业务处理和 I/O 操作进行分离,Reactor 线程只关注事件分发和实际的 IO 操作,业务处理如协议的编解码都分配给线程池处理。可能会有这样的情况发生,业务处理很快,大部分的 Reactor 线程都在处理 IO,导致 CPU 闲置,降低了响应速度。
3.3 主从 Reactor 版本

主从 Reactor 版本设计了一个 主Reactor 用于处理连接接收事件,多个 从Reactor 处理实际的 I/O,分工合作,匹配 CPU 和 IO 速率。
3.4 一个变体
以上的三个版本中,Reactor 线程都参与了 IO 操作,有一种变体是把实际的 IO 操作也转移到线程池线程中,这样从数据的读取到业务的处理都是由一个线程完成,可以避免锁的竞争。
小结
知易行难,为了更好的理解,这里对 PPT 中介绍的3个版本实现了一个简单的回显服务,内部有比较详细的代码注释。
源码地址:https://github.com/tonwu/reactor
Reactor 典型的 NIO 编程模型的更多相关文章
- Nio编程模型总结
终于,这两天的考试熬过去了, 兴致冲冲的来整理笔记来, 这篇博客是我近几天的NIO印象笔记汇总,记录了对Selector及Selector的重要参数的理解,对Channel的理解,常见的Channel ...
- NIO 编程模型
NIO 编程模型 Doug Lea 在 Scalable IO in Java 的 PPT 中描述了 Reactor 编程模型的思想,大部分 NIO 框架和一些中间件的 NIO 编程都与它一样或是它的 ...
- 手动搭建I/O网络通信框架3:NIO编程模型,升级改造聊天室
第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 在第二章中用BIO编程模型,简单的实现了一 ...
- Java网络编程和NIO详解3:IO模型与Java网络编程模型
Java网络编程和NIO详解3:IO模型与Java网络编程模型 基本概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32 ...
- 谈谈传统BIO网络编程模型的局限性与NIO
先来看看我们的server端: 创建一个serversocket,进行监听,每来一个客户端,就启动一个新启动为其服务: private void createListenSocket() { //如果 ...
- NIO&AIO编程模型
NIO线程模型 什么是NIO线程模型? 上图是NIO的线程模型, 基于select实现, 这种线程模型的特点: 多条channel通过一个选择器和单挑线程绑定, 并且在这种编程模型中, Cha ...
- Linux IO模型和网络编程模型
术语概念描述: IO有内存IO.网络IO和磁盘IO三种,通常我们说的IO指的是后两者. 阻塞和非阻塞,是函数/方法的实现方式,即在数据就绪之前是立刻返回还是等待. 以文件IO为例,一个IO读过程是文件 ...
- 手动搭建I/O网络通信框架4:AIO编程模型,聊天室终极改造
第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 第三章:手动搭建I/O网络通信框架3:NI ...
- Reactor 模型(一)基本并发编程模型
Reactor 模型(一)基本并发编程模型 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) 在讲解 Reactor 线程模 ...
随机推荐
- bzoj3435 [Wc2014]紫荆花之恋
如果这棵树不变的话,就是一个裸的点分树套平衡树,式子也很好推$di+dj<=ri+rj$,$ri-di>=dj-rj$ 平衡树维护$dj-rj$,然后查$ri-di$的$rank$即可. ...
- [Poi2012]Festival 差分约束+tarjan
差分约束建图,发现要在每个联通块里求最长路,600,直接O(n3) floyed #include<cstdio> #include<cstring> #include< ...
- LOJ_6045_「雅礼集训 2017 Day8」价 _最小割
LOJ_6045_「雅礼集训 2017 Day8」价 _最小割 描述: 有$n$种减肥药,$n$种药材,每种减肥药有一些对应的药材和一个收益. 假设选择吃下$K$种减肥药,那么需要这$K$种减肥药包含 ...
- 夏娜的菠萝包 JDFZ1098
Description 问题描述:夏娜很喜欢吃菠萝包,她的经纪人RC每半个月就要为她安排接下来的菠萝包计划.今天是7月份,RC又要去商场进货买菠萝包了.这次RC总共买了N种菠萝包,每种一个.每个菠萝包 ...
- 我的新纪元Day01
在我刚刚开通博客园后,想了好久.不知道第一次随笔该写点什么,想写些自己学到的知识,但技术上还是菜鸟的我完全不知道我能向别人分享什么,想到这里有些沮丧. 但万事开头难,只要我入了门,广阔的编程语言的世界 ...
- (5)STM32使用HAL库实现串口通讯——实战操作
功能需求: (1)对接收的字符串原样返回(每10个字符一次). (2)发送一个字符串完成后改变LED的状态. 1.创建工程 使用的是F407Discovery,4个LED对应PD12-PD14. (1 ...
- 用js制作数码时钟
实现效果 实现效果 图片素材 图片素材 原理分析 用setInterval(fn, 1000)来循环刷新图片. 用date对象的getHours().getMinutes().getSeconds() ...
- Java转Ruby【快速入门】
最近参加实习了,公司的技术栈中需要用到 Ruby 以及 Rails 框架,所以算是开始了踩坑之旅吧.. Ruby 简介 网上的简介要搜都能搜到,具体涉及的包括历史啦之类这里不再赘述,提几个关键词吧: ...
- [dotnet] 封装一个同时支持密码/安全密钥认证的SFTP下载器,简单易用。
前言 最近在开发订单对账系统,先从各种支付平台获取订单销售数据,然后与公司商城订单数据进行对账兜底.总体上,各个支付平台提供数据的方式分为两类,一般以接口的方式提供实时数据,比如:webservice ...
- Stackoverflow上有哪些声望高or值得关注的国人
Stackoverflow上有哪些声望高/值得关注的国人? 以下回答并不严格按照 Reputation 排名来列,也不收录不确定是Chinese(中国人或华人)的用户,欢迎补充- 1.李杨 @Li L ...