深入了解 Reactor 网络编程架构模型

Reactor网络编程架构模型

常见的网络编程模型有:Reactor Proactor Asynchronous Completion Token and Acceptor_Connector
这里介绍最主流的reactor模型:
通常网络编程模型处理的主要流程如下: initiate => receive => demultiplex => dispatch => process events

I/O多路复用可以用作并发事件驱动(event-driven)程序的基础,即整个事件驱动模型是一个状态机,包含了:状态(state), 输入事件(input-event), 状态转移(transition), 状态转移即状态到输入事件的一组映射。通过I/O多路复用的技术检测事件的发生,并根据具体的事件(通常为读写),进行不同的操作,即状态转移。


Reactor是什么?

关于reactor 是什么,我们先从wiki上看下:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.

从上述文字中我们可以看出以下关键点 :

事件驱动(event handling
可以处理一个或多个输入源(one or more inputs)
通过Service Handler同步的将输入事件(Event)采用多路复用分发给相应的Request Handler(多个)处理

在这里插入图片描述
自POSA2 中的关于Reactor Pattern 介绍中,我们了解了Reactor 的处理方式:

同步的等待多个事件源到达(采用select()实现
将事件多路分解以及分配相应的事件服务进行处理,这个分派采用server集中处理(dispatch
分解的事件以及对应的事件服务应用从分派服务中分离出去(handler

在reactor pattern处理模式中定义以下三种角色:
Reactor 将I/O事件分派给对应的Handler
Acceptor 处理客户端新连接,并分派请求到处理器链中
Handlers 执行非阻塞读/写 任务

常见的Reactor模型分为三种:

1、单reactor单线程模型
在这里插入图片描述
这是最基本的单Reactor单线程模型。其中Reactor线程,负责多路分离套接字,有新连接到来触发connect 事件之后,交由Acceptor进行处理,有IO读写事件之后交给hanlder 处理。

Acceptor主要任务就是构建handler ,在获取到和client相关的SocketChannel之后 ,绑定到相应的hanlder上,对应的SocketChannel有读写事件之后,基于reactor 分发,hanlder就可以处理了(所有的IO事件都绑定到selector上,有Reactor分发)。

该模型 适用于处理器链中业务处理组件能快速完成的场景。不过,这种单线程模型不能充分利用多核资源,所以实际使用的不多。

2、单reactor多线程模型
在这里插入图片描述
相对于第一种单线程的模式来说,在处理业务逻辑,也就是获取到IO的读写事件之后,交由线程池 thread pool来处理,这样可以减小主reactor的性能开销,从而更专注的做事件分发工作了,从而提升整个应用的吞吐。

3、多reactor多线程模型
在这里插入图片描述
第三种模型比起第二种模型,是将Reactor分成两部分,

mainReactor负责监听server socket,用来处理新连接的建立,将建立的socketChannel指定注册给subReactor
subReactor维护自己的selector, 基于mainReactor 注册的socketChannel多路分离IO读写事件,读写网络数据,对业务处理的功能,另其扔给worker线程池来完成。


Reactor模式是一种典型的事件驱动的编程模型,Reactor逆置了程序处理的流程,其基本的思想即为Hollywood Principle— 'Don't call us, we'll call you'.

Reactor事件处理机制为:主程序将事件以及对应事件处理的方法在Reactor上进行注册, 如果相应的事件发生,Reactor将会主动调用事件注册的接口,即 回调函数. libevent即为封装了epoll并注册相应的事件(I/O读写,时间事件,信号事件)以及回调函数,实现的事件驱动的框架。

Reactor事件处理机制的编程模型,在Redis中也得到了很好的运用,Redis中基于I/O多路复用(mutiplexing) 开发Reactor事件处理机制,监听多个套接字的AE_READABLE读,AE_WRITABLE写事件。读事件绑定读操作和具体执行命令的操作函数,写事件绑定命令回复的操作函数。


架构

The Reactor architectural pattern allows event-driven applications to demultiplex and dispatch service requests that are delivered to an application from one or more clients.
Reactor架构模式允许事件驱动的应用通过多路分发的机制去处理来自不同客户端的多个请求。
在这里插入图片描述
上图是Reactor核心的事件处理流程,有如下几个关键组件

事件(事件源)
Linux上为文件描述符,handler即为注册在特定事件上的程序,事件发生通常在linux下为IO事件,由操作系统触发

Reactor(反应器)
事件管理的接口,内部使用event demultiplexer注册,注销事件;并运行时间循环,当有事件进入"就绪“状态时,调用注册事件的回调函数处理事件;

class Reactor {
public:
    int register_handler(EventHandler *pHandler, int event);

    int remove_handler(EventHandler *pHandler, int event);

    void handle_events(timeval *ptv);
}

Event demultiplexer(事件多路分发机制)

通常是由操作系统提供的I/O多路复用的机制,例如select, epoll. 程序首先将handler(事件源)以及对应的事件注册到event demultiplexer上;当有事件到达时,event demultiplexer就会发出通知,通知Reactor调用事件处理程序进行处理;

Event Handler(事件处理程序)
事件处理程序提供了一组接口,在reactor相应的事件发生时调用,执行相应的事件处理,通常会绑定一个有效的handler

class Event_Handler {
public:
    // events maybe read/write/timeout/close .etc
    virtual void handle_events(int events) = 0;
    virtual HANDLE get_handle() = 0;
}

用一个简单的例子说明这个模型:
下图描述了一个简单的日志服务器,即一个或者多个客户端通过不同的请求获得不同设备的日志,例如打印机的运行情况,数据库的TPS等等。对于传统的线程池模型来说只能每个对于每个请求使用一个单独的线程去处理,这就导致了当请求增加时过多了线程上下文切换,出现了性能上的瓶颈。
在这里插入图片描述
模型的实现:
如下是一种Reactor的简单实现,监听STDIN,并注册不同的事件。处理网络请求也类似,具体可参考 reactor-server源码

#include <sys/epoll.h>
#include <unistd.h>
#include <iostream>
#include <array>
#include <unordered_map>

typedef int EventType;

class Epoll {
    // 封装了epoll I/O 多路复用的机制, Event demultiplexer
public:
    static const int NO_FLAGS = 0;
    static const int BLOCK_INDEFINITELY = -1;
    static const int MAX_EVENTS = 5;

    Epoll() {
        fileDescriptor = epoll_create1(NO_FLAGS);
        event.data.fd = STDIN_FILENO;
        // 设置epoll event 为EPOLLIN(对应文件描述符可读), EPOLLPRI(对应文件描述符有紧急事件可读)
        event.events = EPOLLIN | EPOLLPRI;
    }

    int wait() {
        return epoll_wait(fileDescriptor, events.data(), MAX_EVENTS, BLOCK_INDEFINITELY);
    }

    int control() {
        return epoll_ctl(fileDescriptor, EPOLL_CTL_ADD, STDIN_FILENO, &event);
    }

    ~Epoll() {
        close(fileDescriptor);
    }

private:
    int fileDescriptor;
    struct epoll_event
            event;
    std::array<epoll_event, MAX_EVENTS> events{};

};

class EventHandler {
    // Event Handler

public:
    int handle_event(EventType et) {
        std::cout << "Event Handler: " << et << std::endl;
        return 0;
    }

};

class Reactor {
    // Dispatcher
public:
    Reactor() {
        epoll.control();
    }

    //注册对应的回调函数到handlers中
    void addHandler(std::string event, EventHandler callback) {
        handlers.emplace(std::move(event), std::move(callback));
    }

    void run() {
        while (true) {
            int numberOfEvents = wait();

            for (int i = 0; i < numberOfEvents; ++i) {
                std::string input;
                std::getline(std::cin, input);

                try {
                    // 根据的具体的事件去找对应的handler,并执行相应的操作
                    handlers.at(input).handle_event(EventType(i));
                } catch (const std::out_of_range &e) {
                    std::cout << "no  handler for " << input << std::endl;
                }
            }
        }
    }


private:
    // handlers Table, 存储事件以及其对应的handlers
    std::unordered_map<std::string, EventHandler> handlers{};
    Epoll epoll;

    int wait() {
        int numberOfEvents = epoll.wait();
        return numberOfEvents;
    }
};
int main() {
    Reactor reactor;

    reactor.addHandler("a", EventHandler{});
    reactor.addHandler("b", EventHandler{});
    reactor.run();
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Reactor多线程模型是一种在单个Reactor架构下引入线程池的模式,用于处理一些非IO操作的事件,如计算、编解码任务等。虽然这种模式在效率上有一定提升,但在面对瞬间的高并发连接场景时,性能仍然不佳。\[1\] 主从Reactor多线程模型是一种相对复杂的模式,需要封装多个模块,如主反应器(MainReactor)、子反应器(SubReactor)、接受者(Acceptor)、处理者(Handler)和线程池(Worker)。这种模型的编程实现较为复杂。\[2\] 单个主Reactor多个从Reactor多线程模型是主从Reactor模型的一种变体,它包含了主Reactor和多个从Reactor,以及多个线程。这种模型的元素较为复杂,但可以提高并发处理能力。\[3\] 综上所述,单Reactor多线程模型和主从Reactor多线程模型都是用于提高并发处理能力的模式,但在实现上有一定的差异。 #### 引用[.reference_title] - *1* [IO多路复用和Reactor模型](https://blog.csdn.net/qq_42290561/article/details/125859442)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【Netty】主从反应器 ( Reactor ) 多线程模型](https://blog.csdn.net/han1202012/article/details/106489252)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.liang呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值