在高性能的I/O设计中,有两个著名的模型:Reactor模型和Proactor模型,其中Reactor模型用于同步I/O,而Proactor模型运用于异步I/O操作 。
想要了解两种模型,需要了解一些IO、同步异步的基础知识,彻底搞懂JAVA的网络IO
服务端的线程模型无论是Reactor模型还是Proactor模型,对于支持多连接的服务器,一般可以总结为2种fd和3种事件,如下图:
文章插图
2种fd
- listenfd:一般情况,只有一个 。用来监听一个特定的端口(如80) 。
- connfd:每个连接都有一个connfd 。用来收发数据 。
- listenfd进行accept阻塞监听,创建一个connfd
- 用户态/内核态copy数据 。每个connfd对应着2个应用缓冲区:readbuf和writebuf 。
- 处理connfd发来的数据 。业务逻辑处理,准备response到writebuf 。
Reactor模型中定义的三种角色:
- Reactor:负责监听和分配事件,将I/O事件分派给对应的Handler 。新的事件包含连接建立就绪、读就绪、写就绪等 。
- Acceptor:处理客户端新连接,并分派请求到处理器链中 。
- Handler:将自身与事件绑定,执行非阻塞读/写任务,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel 。可用资源池来管理 。
读取操作:
- 应用程序注册读就绪事件和相关联的事件处理器
- 事件分离器等待事件的发生
- 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器
1.单Reactor单线程模型
Reactor线程负责多路分离套接字,accept新连接,并分派请求到handler 。redis使用单Reactor单进程的模型 。
文章插图
消息处理流程:
- Reactor对象通过select监控连接事件,收到事件后通过dispatch进行转发 。
- 如果是连接建立的事件,则由acceptor接受连接,并创建handler处理后续事件 。
- 如果不是建立连接事件,则Reactor会分发调用Handler来响应 。
- handler会完成read->业务处理->send的完整业务流程 。
对于一些小容量应用场景,可以使用单Reactor单线程模型 。但是对于高负载、大并发的应用场景却不合适,主要原因如下:
- 即便Reactor线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送 。
- 当Reactor线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重Reactor线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈 。
- 一旦Reactor线程意外中断或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障 。
2.单Reactor多线程模型
该模型在事件处理器(Handler)部分采用了多线程(线程池) 。
文章插图
消息处理流程:
- Reactor对象通过Select监控客户端请求事件,收到事件后通过dispatch进行分发 。
- 如果是建立连接请求事件,则由acceptor通过accept处理连接请求,然后创建一个Handler对象处理连接完成后续的各种事件 。
- 如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应 。
- Handler只负责响应事件,不做具体业务处理,通过Read读取数据后,会分发给后面的Worker线程池进行业务处理 。
- Worker线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给Handler进行处理 。
- Handler收到响应结果后通过send将响应结果返回给Client 。
推荐阅读
- 搞懂CSS 字体单位
- 天窗只是用来开窗的?天窗的优点你没搞懂,就别买了!
- 一文彻底了解Hadoop的来龙去脉
- 一文搞懂MySQL的Join,聊一聊秒杀架构设计
- 2个实例搞懂Python循环嵌套——九九乘法表以及质数的索引
- 彻底解决"手机-电脑"互传大文件的难题 电脑-手机快捷互联互通
- 蛀牙本来是小事,为何补牙的时候反而要将牙洞磨大?看完算搞懂了
- 茶具养壶好方法
- 彻底搞懂JDBC的运行过程
- 彻底搞懂Redis的线程模型