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_cb
或send_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