Proactor 网络模型

Proactor 网络模型

本文内容主要参考 asio文档

Proactor 和其他网络模型最大的区别就是 Proactor 中,执行 I/O 的职责由操作系统代为我们承担,因此使用 Proactor 网络库的应用程序代码里,是不会有 read、write 等 I/O 接口调用的

Proactor

我们以 boost asio 为例,讲解 Proactor 网络库中会出现的模块

  • Asynchronous Operation
    • 定义了异步执行的操作
    • 比如异步读写一个套接字
  • Asynchronous Operation Processor
    • 执行 Asynchronous Operation 并在操作完成后将事件放入 Completion Event Queue
    • 可以认为类似于 reactive_socket_service 的内部服务是 Asynchronous Operation Processor
  • Completion Event Queue
    • 缓存操作完成的事件直到它们被 Asynchronous Event Demultiplexer 取走
  • Completion Handler
    • 处理异步操作的结果
    • Asio 中 Completion Handler 必须是形式正确的可调用对象,一般通过 boost::bind 创建
    • 必须是可移动构造的,异步操作会通过移动构造函数接管它的所有权
  • Asynchronous Event Demultiplexer
    • 阻塞等待 Completion Event Queue 中有事件产生,然后返回一个事件给调用者
  • Proactor
    • 调用 Asynchronous Event Demultiplexer 以获取事件,并分发事件给相关联的 Completion Handler
    • 这个抽象实体的代表为 io_context
  • Initiator
    • 应用发起 Asynchronous Operation 的代码
    • Initiator 通过高级 API 比如 basic_stream_socket 与 Asynchronous Operation Processor 交互,再由该接口委托给诸如 reactive_socket_service 的服务

通过 Reactor 实现

在许多平台上,Asio 以 select、epoll、kqueue 等 Reactor API 来模拟实现 Proactor

  • Asynchronous Operation Processor
    • 当 Reactor 指出某个资源已经准备好执行操作,Asynchronous Operation Processor 执行异步操作并将相应的 Completion Handler 放入 Completion Event Queue
  • Completion Event Queue
    • 一系列 Completion Handler 构成的链表
  • Asynchronous Event Demultiplexer
    • 等待相应的事件或条件变量直到 Completion Event Queue 中有事件可用

通过Windows Overlapped I/O实现

在 Windows NT、2000 和 XP 上,Asio 利用 Overlapped I/O 的优势提供了一个高效的 Proactor 实现:

  • Asynchronous Operation Processor
    • 由操作系统实现,通过调用 AcceptEx 等 overlapped 函数以发起操作
  • Completion Event Queue
    • 由操作系统实现,与某个 I/O 完成端口关联起来,每个 io_context 实例都对应一个 I/O 完成端口
  • Asynchronous Event Demultiplexer
    • 被 Asio 调用来从队列中取出事件和它关联的 Completion Handler

优势

  • 可移植性
  • 将线程与并发解耦
  • 性能和可拓展性
  • 简化应用同步
    • Asio 中一般通过 strand 来实现应用间的同步,很少会去显式地使用锁
  • 很容易实现复合函数(Function composition)
    • 复合函数是指通过多次调用低层次读写 API 来提供高层次的操作,比如以特定协议格式发送消息
    • 在一个操作的 Completion Handler 中启动下一个操作,就可以将异步操作串联在一起,实现复合函数
    • 通过封装异步调用链中的第一个操作,用户就感知不到高层次的操作是通过级联实现的

劣势

  • 程序复杂
    • 特别明显的一点就是在 Proactor 模型中,应用逻辑往往是散落在各种回调函数里,这样我们的程序变得很难懂
  • 内存占用
    • 缓冲区空间必须在读写期间被保持,而这段时长往往是不确定的,而且每个并发的操作都需要独立的缓冲区
      • Asio 中往往是通过 RAII 来管理缓冲区,因为程序员没法判断这段缓冲区到什么时候才会被释放,通过将缓冲区智能指针绑定到函数对象中,让他们的生命周期产生关联
    • 对比 Reactor 模式,它在套接字准备好读写前都是不需要缓冲区空间的
posted @ 2022-02-23 23:40  路过的摸鱼侠  阅读(153)  评论(0编辑  收藏  举报