几种高性能网络模型

本文首发于:https://mp.weixin.qq.com/s/b3NSHCTB8Ji7kqipWK6IZA

微信公众号:  后端技术指南针

欢迎关注 获得最佳阅读体验

如何实现高并发是个热门的话题,不同的语言和平台有不同的处理手段,

本文主要讲述在Linux平台下C/C++语言实现的几种高性能网络模型。
通过本文可以了解到以下内容:

  1. IO复用技术
  2. 事件处理模型
  3. 几种并发模型
  4. Memcache的网络模型


1.IO复用技术
IO复用是一种机制,简单说就是IO事件的到来、读写、异常等情况都有内核帮你监控了,应用程序只需要处理那些内核通知你的IO句柄即可,

这种机制的前提是即使建立数万连接,某时刻可读写的fd数量只占总连接量的很小的比例,

因此借助于内核事件驱动机制,就可以实现单线程的数万Socket的管理。

举个栗子:
一个大工厂很多车间,之前必须每个车间有个值班人员,per house per woker的模式,大部分情况下车间是没有异常情况的,

但是为了保证万无一失,仍然需要一名工作人员。后来引入了监控报警系统,视频音频等信息都实时传输到中控室,

从而1名或数名工作人员就可以完成整个厂区的监控,大大提高效率。
IO复用就不多说了,并没有什么太多复杂之处,

目前热门的epoll就是其中的悍将,至于epoll的一些详细的机制可以自行查询不再赘述了。


2.事件处理模型
网络设计模式中,如何处理各种I/O事件是其非常重要的一部分,

Reactor 和Proactor两种事件处理模型应运而生,

可以使用同步I/O实现Reactor模型,使用异步I/O实现Proactor模型。

  • Reactor事件处理模型

Reactor模型是同步I/O事件处理的一种常见模型,其核心思想:将关注的I/O事件注册到多路复用器上,

一旦有I/O事件触发,将事件分发到事件处理器中,执行就绪I/O事件对应的处理函数中。模型中有三个重要的组件:

  1. 多路复用器:由操作系统提供接口,Linux提供的I/O复用接口有select、poll、epoll
  2. 事件分离器:将多路复用器返回的就绪事件分发到事件处理器中
  3. 事件处理器:处理就绪事件处理函数
    典型的Reactor模型类图结构:


Reactor 类结构中包含有的主要角色:

  1. Handle:标示文件描述符
  2. Event Demultiplexer:对操作系统内核实现I/O复用接口的封装等待发生事件发生
  3. Event Handler:事件处理接口
  4. Event Handler A/B:实现应用程序所提供的特定事件处理逻辑
  5. Reactor:反应器定义一个接口,注册和删除关注的事件句柄、运行事件处理循环、等待就绪事件触发,分发事件到注册的回调函数。
    Reactor模型工作的简化流程:

  • Proactor事件处理模型

与Reactor不同的是,Proactor使用异步I/O系统接口将I/O操作托管给操作系统,

Proactor模型中分发处理异步I/O完成事件,并调用相应的事件处理接口来处理业务逻辑。

Proactor类结构如图所示:


Proactor类结构中包含如下角色:

  1. Handle:socket句柄;
  2. Async Operation Processor:异步操作处理器执行异步操作,一般由内核实现
  3. Async Operation:异步操作
  4. Completion Event Queue:完成事件队列
  5. Proactor:主动器 为应用程序进程提供事件循环,从完成事件队列中取出异步操作的结果,分发调用相应的后续处理逻辑
  6. Completion Handler:完成事件接口,一般由回调函数组成
  7. Completion Handler A/B:实现接口定义特定的应用处理逻辑。

Proactor模型的简化的工作流程:


Proactor利用异步I/O并行能力带来更高的效率,但同时增加了编程复杂度。windows对异步I/O提供了很好的支持,

而Linux对异步I/O操作支持并不是特别理想,因此Linux平台上还是以Reactor模型为主,Boost asio采用的是Proactor模型。


3.并发模式
在I/O密集型的程序,采用并发方式可以提高CPU的使用率,可采用多进程和多线程两种方式实现并发。

其中包括半同步/半异步模式、半同步/半反应堆模式、半同步/半反应堆模式改进版、Follower/Leader模式。

  • 同步和异步概念

并发模式中的同步异步和 I/O模型中的同步异步并不一样:

  1. 并发模式中同步指程序按照代码顺序执行,异步指程序依赖事件驱动
  2. I/O模型中同步异步用来区分是主动读取数据结构,还是内核帮忙完成数据的读取再返回给线程
  • 半同步/半异步HAHS模式

半异步/半同步模式Half-Sync/Half-Async简称HSHA,是说这个网络模型中异步和同步都存在,IO层是异步处理,业务处理是同步,因此需要依赖于内核的异步IO机制。
HSHA模式工作流程如图:

 

  1. 异步线程监听到事件后,由内核完成读取数据,异步将其封装为请求对象插入到请求队列中
  2. 请求队列有新的请求对象,通知同步线程获取请求对象,这个对象是包含了数据的结构,并不是fd
  3. 同步线程处理请求对象,实现业务逻辑

综上可知当客户端发送请求时,服务端接收数据是异步的,工作线程从队列收到数据之后是同步的,所以称为半同步/半异步。

  • 半同步/半反应堆HSHR模式

由于HAHS模式依赖于内核的异步IO支持,Linux本身AIO并不理想,因此可以借助于epoll等IO多路复用来模拟这个过程,

但是本质上epoll仍然是同步的,因为连接的数据仍然需要自己来读取,因此epoll返回的只是活跃的fd。
半同步/半反应堆模式Half-Sync/Half-Reactor简称HSHR,和HAHS模式很类似,

区别在于IO层使用epoll多路复用并非纯异步IO实现,数据处理线程和IO线程之间仍然通过队列来实现数据传输,

但是相比HSHA来说,HSHR模式传输的是活跃fd而不是读取的数据。


虽然HSHR模式克服了对纯异步IO的依赖,但是基础版本的HSHR模式,IO线程完成连接的到达处理和响应处理,

也就是同时监听新连接和已建立的连接,并且基于队列与后面的工作线程传输数据,在非常高的并发场景可能存在瓶颈,因此后续有对此的改进版本。

  • 半同步/半反应堆模式的改进版

HSHR模式中单一IO线程既要处理新连接又要监控已连接fd的读写事件,因此改进版本增加从线程进行已连接fd的读写事件监听,

原来的IO主线程只负责处理新连接,然后将建立的fd扔给从线程进行监听读写事件。有的地方也称之为主从Reactor模式,如图:


其工作流程为:

  1. 主线程实现连接监听,只处理网络I/O连接事件
  2. 新的连接fd分发至工作线程中,这些fd上的I/O事件都由该工作线程处理,工作线程都可以处理多个fd的读写事件
  3. 工作线程独立维护自己的事件循环,监听不同连接fd的I/O事件
  4. 处理任务可以使用线程池或者从线程自己处理
  • 领导者/追随者LF模式

Follower/Leader模式是多个工作线程轮流进行事件监听、事件分发、处理事件的模式。

任何一个时间点,只有一个工作线程处理成为Leader负责I/O事件监听,而其他线程都是Follower,并等待成为Leader。Follower/Leader模式的工作流概述如下:

  • 当前Leader Thread监听到就绪事件后,从Follower 线程集中推选出 空闲Thread成为新的Leader
  • 新的Leader Thread继续I/O监听
  • 之后继续处理I/O就绪事件,执行完后加入到Follower 线程集中,等待成为Leader

从上描述,Leader/Follower模式的工作线程存在三种状态,工作线程同一时间只能处于一种状态,这三种状态为:

  • Leader:线程处于领导者状态,负责监听I/O事件;
  • Processing:线程处理就绪I/O事件;
  • Follower:等待成为新的领导者或者可能被当前Leader指定处理就绪事件。


4.Memcache网络模型分析
Memcached采用了很典型的Master-Worker模型,采用的是多线程而不是多进程. 主线程Master接收连接, 然后把连接分派给工作线程Worker,

工作线程处理业务逻辑。核心的共享数据是消息队列,主线程会把收到的事件请求放入队列,

随后调度程序会选择一个空闲的Worker线程来从队列中取出事件请求进行处理Memcached使用libevent实现事件循环,

如图:



5.参考资料

  1. https://tech.youzan.com/yi-bu-wang-luo-mo-xing/
  2. https://zhuanlan.zhihu.com/p/58860015
  3. http://xiaobaoqiu.github.io/blog/2014/11/03/memcachedwang-luo-mo-xing/
  4. 《Linux高性能服务器编程》游双

猜你喜欢

转载自www.cnblogs.com/backnullptr/p/11907506.html