2.2.1服务器百万并发实现

Reactor模型的几个重要组件:Event事件、Reactor反应堆、Demultiplex事件分发器、Evanthandler事件处理器

接上节课,上节课中,我们使用了epoll实现了同时监听多个文件描述符,是对IO的管理,也提到了reactor是对事件的管理,那具体来说是怎样的呢?reactor是事件驱动模型,也就是EPOLLIN/EPOLLOUT,同时,我们应该维护一种结构,对于每个fd,都应该有这样一种记录该fd相关的结构。
在这里插入图片描述
对于其中的fd小块,存储的都是该fd相关的内容,比如该fd的读写缓存区,对应的回调函数等等:
在这里插入图片描述
结构可以用代码这样表示:

typedef int (*ZVCALLBACK)(int fd, int events, void *arg);

typedef struct zv_connect_s {
    int fd;

    ZVCALLBACK cb;

    char rbuffer[BUFFER_LENGTH];
    int rc;
    char wbuffer[BUFFER_LENGTH];
    int wc;

    int count;
} zv_connect_t;

typedef struct zv_connblock_s {
    zv_connect_t *block;
    struct zv_connblock_s *next;
} zv_connblock_t;

typedef struct zv_reactor_s {
    int epfd;
    zv_connblock_t *blockheader;

} zv_reactor_t;

这样,对于listenfd,我们设置它的accept_cb,对于clientfd,设置它的recv_cbsend_cb

具体来说,我们以fd为下标,唯一标识该fd相关的块,对于listenfd,通过listenfd设置相关的accept_cb回调:

int accept_cb(int fd, int events, void *arg)
{
   	...
    int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
    if (clientfd < 0) {
        printf("accept errno: %d\n", errno);
        return -1;
    }

    zv_reactor_t *reactor = (zv_reactor_t*)arg;
    zv_connect_t *conn = &reactor->blockheader->block[clientfd];

    conn->fd = clientfd;
    conn->cb = recv_cb;
    ...
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = clientfd;
    epoll_ctl(reactor->epfd, EPOLL_CTL_ADD, clientfd, &ev);
}

reactor->blockheader->block[listenfd].fd = listenfd;
reactor->blockheader->block[listenfd].cb = accpet_cb;

接收和发送回调同理。
并且,当我们使用上节课提到的epoll将IO管理起来后,整体代码就会显得非常简洁,因为当有EPOLLIN/EPOLLOUT事件来临时,我们只需要根据fd找到对应的回调函数执行即可:

while (1) {//mainloop,  event driver

        int nready = epoll_wait(reactor.epfd, events, 1024, -1);
        if (nready < 0) continue;
        int i = 0; 
        for (i = 0; i < nready; i++) {
            int connfd = events[i].data.fd;
            zv_connect_t *conn = &reactor.blockheader->block[connfd];
            if (events[i].events & EPOLLIN) {
                conn->cb(connfd, events[i].events, &reactor);
            }
            if (events[i].events & EPOLLOUT) {
                conn->cb(connfd, events[i].events, &reactor);
            }
        }
    }

这便是reactor对事件管理的方式,这样一来,我们可以将网络与业务分离,业务人员只需要在回调函数中读写相应的buffer即可,无需关注网络细节!

再往后,为了提高处理能力,我们可以将clientfd相关的读写操作放到子线程去做,而主线程只负责appect等等,这就涉及到不同的网络编程模型,之后再谈。


1.并发:服务器同时承载的客户端数量
2.qps:每一秒处理的请求数量
3.最大时延:
4.新建(建链):每秒建立的链接数
5.吞吐量

ulimit -a查看每个进程可以打开的文件描述符个数
在这里插入图片描述
可以看到这里open files最大是1024,有两种方式可以修改:
1.命令:ulimit -n 1048576(这是临时修改)
在这里插入图片描述
2.修改文件/etc/security/limits.conf(注意中间是tab键)
在这里插入图片描述

gaoyuelong@ubuntu:~/23040voice/06$ sudo sysctl -p
gaoyuelong@ubuntu:~/23040voice/06$ sudo modprobe nf_conntrack

(目前虚拟机网络没有调试好,导致网络通信有些问题,后续再写百万并发部分,先挖个坑)

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:https://ke.qq.com/course/417774?flowToken=1020253

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

高二的笔记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值