c++应用网络编程之六事件驱动的模式

一、网络编程的处理方式

在网络编程中,一般会遇到几个重要的环节:
1、网络IO处理
这个是最基础的,即如何快速准确的响应相关的网络IO操作。这也是网络编程中很关键的一环,所以非常多的技术涌现了出来,包括前面分析的XDP等。其中可能又根据不同的层次又划分了多个情况。
2、数据分发
这是一个承上启下的环节。数据从IO处理获取后,如何从内核态向用户态分发复制。当然,如果把数据复制分发泛化,还可以理解成全链路的数据复制。
3、数据处理
这是用户层真切的能感知的一层。数据在拷贝到用户层后,用户层面对巨大的数据量,如何进行计算和处理(二次分发、转发或保存等等)。

为了更好的处理上述的问题,一般现在的网络编程比较常用的是使用两大数类处理方式,一种是基于线程的处理模式,一种是基于事件驱动的模式。本文先分析事件驱动模式。

二、事件驱动模型

事件驱动模型(Event Drive Model)是一个非常通用的模型,做为一种编程范式,它通过外部事件来驱动程序的执行。即程序中存在一个事件循环,当有外部事件触发相关动作时,便触发相应的回调处理函数进行操作。

三、事件驱动模式

事件驱动模型可以分为两种模式,即前摄器模式(Proactor)和反应器模式(Reactor)。当然这两种模式的名字可能不同的资料上有所不同,比如叫反应堆模式,大家自己明白即可。下就这两种模式进行一下分析:
1、前摄器模式
Proactor模式也可以称为主动器模式,它应用于异步IO操作。所有的事件一般要从一个多路事件分离器(Event Demultiplexer,由CPU或者其它来处理)来处理,用来得到事件触发中的具体的IO事件,并将其分发到指定的读写操作事件处理器(一般为回调函数或中断函数);反之亦然(处理事件请求)。包括下面Reactor模式基本也是这样。
前摄器模式的麻烦之处在于,纯的异步IO动作是需要底层OS支持的。但目前主流的OS基本就是类Linux和闭源的Windows。而前者的IO操作由于开源是清晰可知的,它只是单纯的IO多路利用,并不是纯异步。所以基于Linux内核的网络通信基本都是Reactor模式。而IOCP据说是前摄器模式,但由于Windows闭源,所以其实也无法完全分析。
Proactor模式一般的操作流程是:
1)用户层发起异步IO操作
2)将相关操作和参数写入完成事件队列(这和IOCP基本一致)
3)Proactor检测异步事件完成与否并取出完成的相关数据进行回调处理。
前摄器的优点:
1)更适合处理耗时长或者耗时参差不齐的异步任务
2)支持更高的并发
3)隔离IO操作和用户层操作,用户层不主动干预IO
4)实现了IO操作和用户操作的异步自动关联通知
5)性能有显著提高
缺点:
1)大大增加了编程的复杂性,无论是整体逻辑还是实现调度,错误调试以及后期维护等,都对开发者的水平提出了更高的要求
2)调试困难,特别是内存错误很难定位
3)调度控制以及完成状态控制难于掌握
4)需要底层OS的支持

2、反应器模式
Reactor模式相对前摄器模式就比较简单了。所谓简单就是其底层的IO事件虽然使用IO多路复用,但机制仍然是同步机制,即同步IO。也就是说,在上面的事件分离器中,Reactor模式下是一个同步的事件分离器。
它的基本流程是:
1)提供事件句柄注册和删除的接口
2)运行事件循环
3)事件触发后分发相关事件到注册的回调函数(句柄)
在Linux中,这种基于事件驱动的模型非常常见,比如epoll。
它一般分成三种类型:
1)单Reactor单线程
这个比较好理解,搞一个Reactor,在一个线程中完成所有的事件处理(监听、连接、接收、读写等等)。他的优势就是简单,但未必性能低。比如常用的Redis。
2)单Reactor多线程(线程池)
一般来说,并发会导致上层应用的处理速度无法匹配IO动作,所以很简单的方式是就是增加多线程来处理数据。即单Reactor只完成相对简单的非读写事件,而读写事件则发送到多线程(线程池)处理。
3)多Reactor多线程(线程池)
上面的操作一般来说就会中等或稍微偏高的操作基本都可以了,但如果有高并发的情况还是会有捉襟见肘的情况。继续大搞分治法,把Reactor也搞成多个线程来处理不就可以了。也就是从一对一,到一对多再到多对多。回头想一想线程池中的HS/HA就明白了。
Reactor模式的优点:
1)编程相对简单,即使是多对多的情况,也基本容易把握流程
2)扩展相对容易,只是多增加相关的线程或Reactor即可。
3)移植性较好,一般不强关联OS
4)高可复用
缺点:
1)回调句柄处理需要控制同步阻塞的时间
2)很多事件驱动都是单线程轮询,有瓶颈限制
3)最后就是同步事件的限制

需要强调的是前前摄器的实现Boost.aio是需要底层OS的支持的,而ACE则是一个非常不受人推荐的异步网络库。其它常见的如libevent等基本都反应堆模式。

四、二者对比分析

Reactor模式与Proactor模式一个典型的不同在于,前者的事件和事件处理句柄是对应的,不存在一个事件处理队列。每当有事件触发后,理论上一个事件处理handler就应该被触发。这里很关键的在于,事件的分发与事件的监听是隔离的。
而异步IO与同步IO最终的体现是,前摄器的模型上更简单,但实现上更复杂;反应器模型上相对复杂,但实现要相对简单。前摄器的弹性更好,但受限于底层OS。反应器的弹性较小但一般不受底层OS的限制反而更有可移植性和复用性。
总体上看,前摄器的应用是小众的,受限的;反应器的应用是较普遍的,基本很少受限的。

五、总结

很多学习网络编程的都是从SOCKET开发开始,一点点的扩展,然后才考虑各种机制、模型等。这就无法从整体上进行网络编程的把控。一开始就陷入了细节的泥潭。本系列文章一开始先从宏观上对网络编程进行分析说明,然后再开始一个个细节的网络知识的学习,这样就更好的知道了各种编程模式的适应性。更容易理解对网络编程的细节的处理的不同导致的适应场景的不同。
一个是无目的的学习,一个是有放矢的学习。见仁见智吧。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值