并发之巅:事件驱动Reactor在高性能服务器的应用

一、什么是 Reactor:

Reactor 模式是一种事件驱动的设计模式,它通过管理事件而非管理 IO 操作来响应多个输入/输出请求。在这种模式中,应用程序不需要持续监控或直接处理 IO 操作,而是关注于发生的事件及其响应。这种方式使得应用程序能够高效地管理多个并发的输入/输出源,通过非阻塞的方式提高了系统整体的性能和响应能力。

二、Reactor 模式的核心原则和操作:

  1. 不同的 IO 事件对应不同回调:

    • 在 Reactor 模式中,每种类型的 IO 操作(如读、写、连接等)都关联到特定的事件。每个事件都有相应的回调函数与之对应,当事件发生时,对应的回调函数被触发执行。
  2. 事件注册与回调机制:

    • Register (注册):应用程序向 Reactor 注册感兴趣的事件及其对应的事件处理器(回调函数)。例如,应用程序可能对监听套接字(listenfd)上的输入事件(EPOLLIN)感兴趣,并注册一个接受新连接的回调函数(accept_cb)。
    • Callback (回调):当对应的事件被触发时,Reactor 会自动调用之前注册的回调函数来处理该事件。
  3. IO 事件转换为回调执行的示例:

    • listenfd->EPOLLIN->accept_cb:当监听套接字(listenfd)准备好接收新的连接时(EPOLLIN),执行接受连接的回调函数(accept_cb)。
    • clientfd->EPOLLIN->recv_cb:当客户端套接字(clientfd)有数据可读时(EPOLLIN),执行接收数据的回调函数(recv_cb)。
    • clientfd->EPOLLOUT->send_cb:当客户端套接字(clientfd)准备好发送数据时(EPOLLOUT),执行发送数据的回调函数(send_cb)。

三、Reactor 的核心功能:

  1. 事件与回调的匹配:

    • Reactor 维护一个或多个事件处理器(Event Handlers)和事件(Event)的映射表。当事件发生时,Reactor 查找这个映射表,找到相应的事件处理器并执行。
  2. 每一个 IO 与之对应的参数:

    • 每个注册的事件处理器(回调)不仅与特定的事件类型相关联,还可以与特定的 IO 资源(如文件描述符)和附加参数(如用户数据)关联。这使得处理程序能够更灵活地根据上下文执行适当的操作。

Reactor 模式通过将 IO 操作转换为事件驱动的回调执行,极大地提高了应用程序处理并发连接和请求的能力,特别适用于高性能的网络服务和应用程序。

四、代码实践

#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>
#include <errno.h>
#include <sys/time.h>

#define BUFFER_LENGTH 1024
#define CONNECTION_SIZE 1048567
#define MAX_PORTS 20

#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

typedef int (*RCALLBACK)(int fd);
int epfd = 0;
struct timeval begin;

struct conn
{
    int fd;

    char rbuffer[BUFFER_LENGTH];
    int rlength;

    char wbuffer[BUFFER_LENGTH];
    int wlength;

    RCALLBACK send_callback;

    union
    {
        RCALLBACK recv_callback;
        RCALLBACK accept_callback;
    } raction;
};

struct conn conn_list[CONNECTION_SIZE] = {0};
// fd

int set_event(int fd, int event, int flag)
{
    if (flag) // non-zero add
    {
        struct epoll_event ev;
        ev.events = event;
        ev.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    }
    else // zero mod
    {
        struct epoll_event ev;
        ev.events = event;
        ev.data.fd = fd;
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
    }
}

int recv_cb(int fd)
{
    int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LENGTH, 0);
    if (count == 0)
    {
        // 客户端关闭了连接
        printf("Client closed the connection: %d\n", fd);
        close(fd);
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);

        return 0;
    }
    conn_list[fd].rlength = count;

    // printf("RECV: %s\n", conn_list[fd].rbuffer);

#if 1 // echo
    conn_list[fd].wlength = conn_list[fd].rlength;
    memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, conn_list[fd].wlength);
#endif

    set_event(fd, EPOLLOUT, 0);
}

int send_cb(int fd)
{
    int count = send(fd, conn_list[fd].wbuffer, count, 0);
    set_event(fd, EPOLLIN, 0);
    return count;
}

int event_register(int fd, int event)
{
    if (fd < 0)
        return -1;
    // 新的连接设置不同的回调函数
    conn_list[fd].fd = fd;
    conn_list[fd].raction.recv_callback = recv_cb;
    conn_list[fd].send_callback = send_cb;

    memset(conn_list[fd].rbuffer, 0, BUFFER_LENGTH);
    conn_list[fd].rlength = 0;

    memset(conn_list[fd].wbuffer, 0, BUFFER_LENGTH);
    conn_list[fd].wlength = 0;

    set_event(fd, event, 1);
}

// listenfd(sockfd) -->EPOLLIN --> accept_cb
int accept_cb(int fd)
{
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);

    int clientfd = accept(fd, (struct sockaddr *)&clientaddr, &len);
    // printf("accept finshed: %d\n", clientfd);
    if (clientfd < 0)
    {
        printf("accept error: %d --> %s\n", errno, strerror(errno));
        return -1;
    }
    event_register(clientfd, EPOLLIN);

    if ((clientfd % 1000) == 0)
    {
        struct timeval current;
        gettimeofday(&current, NULL);

        int time_used = TIME_SUB_MS(current, begin);

        memcpy(&begin, &current, sizeof(struct timeval));
        printf("accept finshed: %d\n, time_used: %d\n", clientfd, time_used);
    }

    return 0;
}

int init_sever(unsigned short port)
{

    int socketfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 0.0.0.0
    servaddr.sin_port = htons(port);              // 0-1023

    if (-1 == bind(socketfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr)))
    {
        printf("bind failed: %s\n", strerror(errno));
    }

    listen(socketfd, 10);
    printf("listen finshed: %d\n", socketfd);

    return socketfd;
}

int main()
{
    unsigned short port = 2000;

    epfd = epoll_create(1);
    int i = 0;
    for (i = 0; i < MAX_PORTS; i++)
    {
        int socketfd = init_sever(port + i);

        conn_list[socketfd].fd = socketfd;
        conn_list[socketfd].raction.recv_callback = accept_cb;
        set_event(socketfd, EPOLLIN, 1);
    }

    gettimeofday(&begin, NULL);

    while (1)
    { // mainloop
        struct epoll_event events[1024] = {0};
        int nready = epoll_wait(epfd, events, 1024, -1);

        int i = 0;
        for (i = 0; i < nready; i++)
        {
            int connfd = events[i].data.fd;
#if 0
            if (events[i].events & EPOLLIN)
            {
                conn_list[connfd].raction.recv_callback(connfd);
            }
            else if (events[i].events & EPOLLOUT)
            {
                conn_list[connfd].send_callback(connfd);
            }
#else // io读写事件并存
            if (events[i].events & EPOLLIN)
            {
                conn_list[connfd].raction.recv_callback(connfd);
            }

            if (events[i].events & EPOLLOUT)
            {
                conn_list[connfd].send_callback(connfd);
            }
#endif
        }
    }
}

1、不同的io事件,做不同的action

2、io做到独立 

https://xxetb.xetslk.com/s/2sff5t

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值