推荐一个零声学院免费教程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:
第三章 :reactor事件处理模式
reactor的英文翻译叫反应堆,这个反应堆是指对事件的反应,反应堆里面包含了一堆的监听事件集合,当一个事件就绪后,reactor马上对它做出反应和处理。这个反应和处理可以交给特定的函数或线程进程处理。
reactor模式要求主线程(IO处理单元),只负责监听文件描述符是否有时间发生,有的话就立马将该事件通知给工作线程(逻辑单元)处理。除此之外主线程不做其它任何实质性的工作。读写数据,接收新的连接,处理客户请求都在工作线程中处理
以epoll为例,实现reactor的流程是:
工作线程从请求队列中取出事件后,将更具事件的类型来处理它,对于可读事件执行,执行读数据和处理请求的操作;对于可写事件执行写数据操作;对于连接事件处理连接操作。所以没必要区分读工作线程和写工作线程。
下面的这个代码实列,为了简便易读,没有创建工作线程,而是用对应的回调函数取而代之。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_LENGTH 4096
#define MAX_EPOLL_EVENTS 1024
int ReadCallback(int fd, int event, void *arg);
int WriteCallback(int fd, int event, void *arg);
int AcceptCallback(int fd, int event, void *arg);
typedef int NCALLBACK(int, int, void*);
enum TagFdType {
LISTENFD = 0,
CLIENTFD
};
struct TagEvent {
int fd;
int events;
void *arg;
NCALLBACK *ReadCallback;
NCALLBACK *WriteCallback;
NCALLBACK *AcceptCallback;
int status;
char buffer[BUFFER_LENGTH];
enum TagFdType type;
int length;
long lastActive;
};
struct TagReactor {
int epollFd;
struct TagEvent *events;
};
int SetFdNonblock(int fd)
{
int flag = fcntl(fd, F_GETFD, 0);
flag = fcntl(fd, F_SETFL, flag| O_NONBLOCK);
return flag;
}
int ListenFdInit(unsigned int port)
{
int ret = 0;
int listenFd = socket(AF_INET, SOCK_STREAM, 0);
if (listenFd < 0) {
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
ret = bind(listenFd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if ( ret == -1) {
printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
return -2;
}
ret = listen(listenFd, 10);
if (ret == -1) {
printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
return -3;
}
return listenFd;
}
int ReactorInit(struct TagReactor *reactor) {
if (reactor == NULL) {
return -1;
}
memset(reactor, 0, sizeof(struct TagReactor));
reactor->epollFd = epoll_create(1);
if (reactor->epollFd <= 0) {
printf("create epollFd in %s err %s\n", __func__, strerror(errno));
return -2;
}
// 使用线性表分配了MAX_EPOLL_EVENTS个事件的存储空间,
reactor->events = (struct TagEvent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct TagEvent));
if (reactor->events == NULL) {
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epollFd);
return -3;
}
return 0;
}
void EventSet(struct TagEvent *ev, int fd, NCALLBACK AcceptCb,NCALLBACK ReadCb,NCALLBACK WriteCb, enum TagFdType type ,void *arg)
{
ev->type = type;
ev->fd = fd;
ev->events = 0;
ev->arg = arg;
ev->lastActive = time(NULL);
ev->AcceptCallback = AcceptCb;
ev->ReadCallback = ReadCb;
ev->WriteCallback = WriteCb;
return ;
}
int EventDel(int epfd, struct TagEvent *ev) {
struct epoll_event event = {0, {0}};
if (ev->status != 1) {
return -1;
}
event.data.ptr = ev;
ev->status = 0;
epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &event);
return 0;
}
int EventAdd(int epfd, int events, struct TagEvent *ev) {
struct epoll_event event = {0, {0}};
event.data.ptr = ev;
event.events = ev->events = events;
int op;
if (ev->status == 1) {
op = EPOLL_CTL_MOD;
} else {
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if (epoll_ctl(epfd, op, ev->fd, &event) < 0) {
printf("event add failed [fd=%d], events[%d]\n", ev->fd, events);
return -1;
}
return 0;
}
int ReactorAddListener(struct TagReactor *reactor, int sockfd) {
if (reactor == NULL) {
return -1;
}
if (reactor->events == NULL) {
return -1;
}
enum TagFdType type = LISTENFD;
EventSet(&reactor->events[sockfd], sockfd, AcceptCallback, ReadCallback, WriteCallback, type, reactor);
EventAdd(reactor->epollFd, EPOLLIN, &reactor->events[sockfd]);
return 0;
}
int WriteCallback(int fd, int event, void *arg) {
struct TagReactor *reactor = (struct TagReactor*)arg;
struct TagEvent *ev = reactor->events + fd;
int len = send(fd, ev->buffer, ev->length, 0);
if (len > 0) {
printf("send[fd=%d], [%d]%s\n", fd, len, ev->buffer);
EventDel(reactor->epollFd, ev);
enum TagFdType type = CLIENTFD;
EventSet(ev, fd, AcceptCallback, ReadCallback, WriteCallback, type, reactor);
EventAdd(reactor->epollFd, EPOLLIN, ev);
} else {
close(ev->fd);
EventDel(reactor->epollFd, ev);
printf("send[fd=%d] error %s\n", fd, strerror(errno));
}
return len;
}
int ReadCallback(int fd, int event, void *arg) {
struct TagReactor *reactor = (struct TagReactor*)arg;
struct TagEvent *ev = reactor->events + fd;
int len = recv(fd, ev->buffer, BUFFER_LENGTH , 0);
EventDel(reactor->epollFd, ev);
if (len > 0) {
ev->length = len;
ev->buffer[len] = '\0';
printf("C[%d]:%s\n", fd, ev->buffer);
enum TagFdType type = CLIENTFD;
EventSet(ev, fd, AcceptCallback, ReadCallback, WriteCallback, type, reactor);
EventAdd(reactor->epollFd, EPOLLOUT, ev);
} else if (len == 0) {
close(ev->fd);
printf("[fd=%d] pos[%ld], closed\n", fd, ev-reactor->events);
} else {
close(ev->fd);
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
return len;
}
int AcceptCallback(int fd, int event, void *arg)
{
int ret = 0;
struct TagReactor *reactor = (struct TagReactor*)arg;
if (reactor == NULL) return -1;
struct sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
int clientFd = accept(fd, (struct sockaddr*)&clientAddr, &len);
if (clientFd == -1) {
printf("accept: %s\n", strerror(errno));
return -1;
}
if (clientFd > MAX_EPOLL_EVENTS) {
printf("%s: max connect limit[%d]\n", __func__, MAX_EPOLL_EVENTS);
return -1;
}
int flag = SetFdNonblock(clientFd);
if (flag < 0) {
printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
return -2;
}
enum TagFdType type = CLIENTFD;
EventSet(&reactor->events[clientFd], clientFd, AcceptCallback, ReadCallback, WriteCallback, type, reactor);
EventAdd(reactor->epollFd, EPOLLIN, &reactor->events[clientFd]);
printf("new connect [%s:%d][time:%ld], pos[%d]\n",
inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), reactor->events[clientFd].lastActive, clientFd);
return 0;
}
int ReactorRun(struct TagReactor *reactor) {
if (reactor == NULL) {
return -1;
}
if (reactor->epollFd < 0) {
return -1;
}
if (reactor->events == NULL) {
return -1;
}
struct epoll_event events[MAX_EPOLL_EVENTS + 1];
int checkpos = 0, i;
while (1) {
long now = time(NULL);
for (i = 0; i < 100; i++, checkpos++) {
if (checkpos == MAX_EPOLL_EVENTS) {
checkpos = 0;
}
if (reactor->events[checkpos].status != 1) {
continue;
}
long duration = now - reactor->events[checkpos].lastActive;
if (duration >= 60) {
close(reactor->events[checkpos].fd);
printf("[fd=%d] timeout\n", reactor->events[checkpos].fd);
EventDel(reactor->epollFd, &reactor->events[checkpos]);
}
}
int nready = epoll_wait(reactor->epollFd, events, MAX_EPOLL_EVENTS, 1000);
if (nready < 0) {
printf("epoll_wait error, exit\n");
continue;
}
for (i = 0;i < nready;i ++) {
struct TagEvent *ev = (struct TagEvent*)events[i].data.ptr;
if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {
if (ev->type == LISTENFD) { // 监听fd
ev->AcceptCallback(ev->fd, events[i].events, ev->arg);
} else { // 客户端连接fd
ev->ReadCallback(ev->fd, events[i].events, ev->arg);
}
}
if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {
ev->WriteCallback(ev->fd, events[i].events, ev->arg); // 写事件
}
}
}
}
int ReactorDestory(struct TagReactor *reactor)
{
close(reactor->epollFd);
free(reactor->events);
return 0;
}
int main(int argc, char const *argv[])
{
int listenFd = ListenFdInit(9999);
struct TagReactor *reactor = (struct TagReactor*)malloc(sizeof(struct TagReactor));
ReactorInit(reactor);
ReactorAddListener(reactor, listenFd);
ReactorRun(reactor);
ReactorDestory(reactor);
close(listenFd);
return 0;
}
这里主要介绍两种并发模式:
异步线程就是主线程,它监听所有socket上的事件。如果有新的连接请求过来,主线程就接受并往epoll上注册读写事件。如果监听的socket上有毒血事件过来,主线程则把该socket插入请求队列(一般会把和该socket相关的应用程序数据、任务类型等信息封装成一个对象),交由工作线程处理。工作线程通过竞争机制获取任务的接管权。
前面的简单模式有如下缺点:
本文章主要对基于reactor的高并发模式进行了一个介绍,并给出了一个reactor的简单实现。