高性能IO模型:Reactor和Proactor reactor模型 proactor
suiw9 2024-12-17 16:13 25 浏览 0 评论
前言
在网络模型一文中,我们介绍了多种网络IO模型,其中包括IO多路复用模型,在此基础上,本文将介绍两种高性能IO模型:Reactor和Proactor,Reactor应用于同步IO,Proactor应用于异步IO。
Reactor模型
普通函数调用流程如下:
reactor_normal
Reactor模型一种事件驱动(回调)机制:程序不主动调用某个API处理,而是相应事件发生,Reactor主动调用应用程序注册的接口进行处理。
中心思想:将所有要处理的IO事件注册到一个IO多路复用器上,同时主线程/进程阻塞在多路复用器上。一旦有IO事件到来或准备就绪,多路复用器返回并调用相应的事件处理函数。
my_reactor
接下来将具体介绍三种Reactor模型;
Reactor单线程模型(Single threaded version)
Reactor模型三个重要组件:
- 多路复用器(Reactor):负责监听注册进来的IO事件(对应图中Reactor)
- 事件分离器(Event Demultiplexer):将多路复用器中返回的就绪事件分到对应的处理函数中;(对应图中dispatch)
- 事件处理器(Event Handler):负责处理特定事件的处理函数(对应上图read,decode,compute,encode,send等)
该模型中,Reactor线程既要负责监听新连接的到来,又要dispatch请求到handler中;
消息处理流程如下:
- Reactor通过epoll监听连接事件,收到事件后通过dispatch转发
- 如果是连接建立的事件,则有acceptor接受连接,并创建handler处理后续事件
- 如果不是建立连接事件,Reactor会分发调用handler来响应
- handler完成read->业务处理->send的完整业务流程
以下为一个简化版的Reactor单线程模型
// reactor初始化 创建多路复用器
int reactor_init(struct reactor *r) {
r->epfd = epoll_create(1);
}
// reactor驱动
int reactor_run(struct reactor *r) {
if (r == NULL || r->epfd < 0 || r->events == NULL)
return -1;
struct epoll_event events[1024 + 1];
while (1) { // 死循环
// 有事件到来时通过epoll_wait返回
int nready = epoll_wait(r->epfd, events, 1024, 1000);
if (nready < 0){
continue;
}
for (i = 0; i < nready; i++) {
struct event_data *ev = (struct event_data*)events[i].data.ptr;
if (events[i].events & EPOLLIN) // available for read
ev->callback(ev->fd, events[i].events, ev->arg); // 调用读事件的回调函数处理
if (events[i].events & EPOLLOUT) //available for write
ev->callback(ev->fd, events[i].events, ev->arg); // 调用写事件的回到函数处理
}
}
return 0;
}
Reactor单线程模型只适用于小容量应用场景,但对于高负载,高并发场景并不适用,主要原因:
- 无法充分利用CPU核心(只能利用一个CPU核心),无法满足海量数据处理
- 高并发时,Reactor线程过载后处理速度会变慢,导致大量客户端连接超时,导致大量挤压信息和处理超时,成为性能瓶颈
- 一旦Reactor线程意外中断或者进入死循环,会导致整个系统通信模块不可用,造成节点故障
为了解决上述问题,演进出单Reactor多线程模型(线程池)
Reactor多线程模型(Multithreaded Designs)
消息处理流程如下:
- Reactor通过epoll监听连接事件,收到事件后通过dispatch转发
- 如果是连接建立的事件,则有Acceptor接受连接,并创建Handler处理后续事件;
- 如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应。
- Handler只负责响应事件,不做具体业务处理,通过Read读取数据后,会分发给后面的Worker线程池进行业务处理。
- Worker线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给Handler进行处理。
- Handler收到响应结果后通过send将响应结果返回给Client。
相对于第一种模型来说,在处理业务逻辑,也就是获取到IO的读写事件之后,交由线程池来处理,handler收到响应后通过send将响应结果返回给客户端。这样可以降低Reactor的性能开销,从而更专注的做事件分发工作了,提升整个应用的吞吐量。
但是这个模型存在问题如下:
- 多线程数据共享和访问比较复杂。如果子线程完成业务处理后,把结果传递给主线程Reactor进行发送,就会涉及共享数据的互斥和保护机制。
- Reactor承担所有事件的监听和响应,只在主线程中运行,可能会存在性能问题。例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。
为了解决性能问题,产生了第三种主从Reactor多线程模型。
主从Reactor多线程模型(Multiple Reactors)
将Reactor分为两部分:
- mainReactor负责监听server socket,用来处理网络IO连接建立操作,将建立的socketChannel指定注册给subReactor。
- subReactor主要做和建立起来的socket做数据交互和事件业务处理操作。通常,subReactor个数上可与CPU个数等同。
Nginx、Swoole、Memcached和Netty都是采用这种实现。
消息处理流程,如下:
- 从主线程池中随机选择一个Reactor线程作为acceptor线程,用于绑定监听端口,接收客户端连接
- acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作
- 步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,并创建一个Handler用于处理各种连接事件
- 当有新的事件发生时,SubReactor会调用连接对应的Handler进行响应
- Handler通过Read读取数据后,会分发给后面的Worker线程池进行业务处理
- Worker线程池会分配独立的线程完成真正的业务处理,如何将响应结果发给Handler进行处理
- Handler收到响应结果后通过Send将响应结果返回给Client
Reactor模式是编写高性能网络服务器的必备技术之一,具有以下优点:
- 响应快,不必为单个同步事件所阻塞,虽然reactor本身依然同步
- 编程简单,最大程度避免复杂的多线程及同步问题,避免多线程/进程的切换开销
- 可扩展性,可以方便的通过增加reactor实例个数来充分利用cpu资源(一个核心对应一个reactor,nginx)
- 可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性
Proactor模型
事件分离器只负责发起异步读写操作,IO操作本身由操作系统来完成,需要将用户定义的数据缓冲区地址和数据大小传给操作系统,其才能从中得到写出操作所需数据,或写入socket读到的数据。
事件分离器捕获IO操作完成事件并传递给对应事件处理器,由事件处理器获取IO操作结果,并对数据进行处理。
Proactor工作流程如下:
- 处理器发起异步操作,并关注IO完成事件
- 事件分离器等待操作完成事件
- 分离器等待过程中,操作系统利用并行的内核现场执行实际的IO操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成
- IO完成后,通过事件分离器呼唤处理器
- 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器
Proactor模型最大的特点是使用异步IO,所有IO操作都交给系统提供的异步IO接口执行。工作线程仅负责业务逻辑。
优点:给工作线程带来了更高的效率;
缺点:
- 编程复杂性:异步操作流程的事件的初始化和事件完成在时间和空间上都是相互分离的,异步编程更加复杂;
- 内存使用:缓冲区在读写操作的时间段内必须保持住,可能造成不确定性,并且每个并发操作都要求有独立的缓存;(Reactor模型在读写准备好前不需要开辟缓存)
- 操作系统支持:Windows通过IOCP实现真正的异步IO;linux2.6后也有aio接口,但是效果不理想,不如epoll性能。linux5.1后引入io_uring(一套全新的syscall,一套全新的async API,更高的性能,更好的兼容性,来迎接高IOPS,高吞吐量的未来)
Reactor和Proactor的相同点都是通过回调方式通知有IO事件到来,并由handler进行处理,
不同点在于Proactor回调handler时IO操作已经完成(异步),Reactor回调handler时需要先进行IO操作(同步)。
例如:Reactor中注册读事件,当fd可读时,调用者需要自己调用read读取数据,而Proactor在注册读事件时,同时会提供一个buffer用于存储读取的数据,那么Proactor通过回调函数通知用户时,用户无需再调用read读取数据,数据已经再buffer中了。
参考
nio
Reactor 和 Proactor
相关推荐
- Qt编程进阶(99):使用OpenGL绘制三维图形
-
一、Qt中的OpenGL支持...
- OpenGL基础图形编程(七)建模(opengl教程48讲)
-
七、OpenGL建模 OpenGL基本库提供了大量绘制各种类型图元的方法,辅助库也提供了不少描述复杂三维图形的函数。这一章主要介绍基本图元,如点、线、多边形,有了这些图元,就可以建立比较复杂的模型了...
- ffmpeg cv:Mat编码成H265数据流(ffmpeg编码mp4视频)
-
流程下面附一张使用FFmpeg编码视频的流程图。使用该流程,不仅可以编码H.264的视频,而且可以编码MPEG4/MPEG2/VP8等等各种...
- 986g超轻酷睿本,联想ThinkPad X1 Carbon 2025 Aura评测
-
今年3月份,联想首发了搭载Intel酷睿Ultra移动平台的ThinkPadX1CarbonGen12轻薄本,其续航表现令人惊喜。时隔9个月,IT之家收到了ThinkPad...
- 拆解五六年前的国产平板,这做工!
-
之前在论坛有幸运得被抽到奖,就是猎奇手机镜头,到手的时候玩了下鱼眼和广角微距,效果见图,用手机拍的那么就进入正题来说下拆鸡过程,外壳我就不拍出来了,免得打广告之嫌,拆出背面外壳就出现了一个裸板。第...
- 什么是闭合GOP和开放GOP?(闭合式和开放式区分)
-
翻译|Alex技术审校|李忠本文来自OTTVerse,作者为KrishnaRaoVijayanagar。...
- 拆解五六年前的国产平板(国产平板怎么拆开)
-
之前在论坛有幸运得被抽到奖,就是猎奇手机镜头,到手的时候玩了下鱼眼和广角微距,效果见图,用手机拍的那么就进入正题来说下拆鸡过程,外壳我就不拍出来了,免得打广告之嫌,拆出背面外壳就出现了一个裸板。第...
- 如何使用PSV播放MP4 视频自动退出怎么办
-
作者:iamwin来源:巴士论坛(点此进入)看到有很多同学在为psv无法播放视频而困扰,自己研究了下,发一个可以解决PSV出现播放视频播放到一半就跳出的问题。就是这个问题:首先,请大家先升级到版本≥1...
- 2023-03-21:音视频解混合(demuxer)为MP3和H264...
-
2023-03-21:音视频解混合(demuxer)为MP3和H264,用go语言编写。答案2023-03-21:...
- FFmpeg解码H264及swscale缩放详解
-
本文概要:...
- CasaOS保姆级喂饭教程!网心云OEC-Turbo安装CasaOS系统固件!
-
本内容来源于@什么值得买APP,观点仅代表作者本人|作者:柒叶君...
- Firefox 33将整合思科开源编解码器OpenH264
-
思科去年在BSD许可证下开源了支持H.264编解码的OpenH264,Mozilla则在当时宣布将在Firefox中整合思科的二进制模块。现在,最新的FirefoxNightly(Firefox3...
- 为什么传输视频流的时候需要将YUV编码成H.264?
-
首先开始的时候我们借用一张雷神的图帮助大家理解一下从上图可以看出我们要做的,就是将像素层的YUV格式,编码出编码层的h264数据。...
- FFmpeg学习(1)开篇(ffmpeg开发教程)
-
FFmpeg学习(1)开篇...
- 喜欢看视频必须了解 AV1编码那点事
-
喜欢看视频的小伙伴大概都有点感觉,AV1这个不太熟悉的视频格式,最近闹出的事情可不少,比如视频网站为了节约带宽偷偷默认使用AV1格式,让电脑狂转;比如Intel专门给旧CPU发布了相关工具;再比如GP...
你 发表评论:
欢迎- 一周热门
-
-
Linux:Ubuntu22.04上安装python3.11,简单易上手
-
宝马阿布达比分公司推出独特M4升级套件,整套升级约在20万
-
MATLAB中图片保存的五种方法(一)(matlab中保存图片命令)
-
别再傻傻搞不清楚Workstation Player和Workstation Pro的区别了
-
Linux上使用tinyproxy快速搭建HTTP/HTTPS代理器
-
如何提取、修改、强刷A卡bios a卡刷bios工具
-
Element Plus 的 Dialog 组件实现点击遮罩层不关闭对话框
-
日本组合“岚”将于2020年12月31日停止团体活动
-
SpringCloud OpenFeign 使用 okhttp 发送 HTTP 请求与 HTTP/2 探索
-
tinymce 号称富文本编辑器世界第一,大家同意么?
-
- 最近发表
- 标签列表
-
- dialog.js (57)
- importnew (44)
- windows93网页版 (44)
- yii2框架的优缺点 (45)
- tinyeditor (45)
- qt5.5 (60)
- windowsserver2016镜像下载 (52)
- okhttputils (51)
- android-gif-drawable (53)
- 时间轴插件 (56)
- docker systemd (65)
- slider.js (47)
- android webview缓存 (46)
- pagination.js (59)
- loadjs (62)
- openssl1.0.2 (48)
- velocity模板引擎 (48)
- pcre library (47)
- zabbix微信报警脚本 (63)
- jnetpcap (49)
- pdfrenderer (43)
- fastutil (48)
- uinavigationcontroller (53)
- bitbucket.org (44)
- python websocket-client (47)