Event-driven programming library

来源:互联网 发布:淘宝管控下架什么意思 编辑:程序博客网 时间:2024/05/18 13:09
源码版本:redis 2.4.4
Event-driven programming  libray,提到这个,很容易想到如雷贯耳的libevent库(libeven封装了以下三种事件的响应:IO事件,定时器事件,信号事件)。
redis的没有采用庞大的libevent库,而是自己写了一个,牺牲了一些平台通用性,但是性能非常强劲。memcache采用了libevent,有人认为这是redis的优于性能比memcache性能。没有测试过,保留意见。

x相关源码:ae.h ae.c networking.c   anet.c  net.h ae_epoll.c ae_select.c ae_kqueue.c
ae.h、ae.c :event library具体实现
networking.c : 与客户端的交互 
anet.h anet.c : 网络通信
ae_epoll.c ae_select.c ae_kqueue.c  : 不同系统多路IO封装
ae_epoll.c : linux平台
ae_select.c :unix平台
ae_kqueue.c : BSD、APPLE

ae.c多路IO选择:

[cpp] view plaincopy
  1. #ifdef __linux__  
  2. #define HAVE_EPOLL 1  
  3. #endif  
  4.   
  5. #if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)  
  6. #define HAVE_KQUEUE 1  
  7. #endif  
  8.   
  9. #ifdef HAVE_EPOLL  
  10. #include "ae_epoll.c"  
  11. #else  
  12.     #ifdef HAVE_KQUEUE  
  13.     #include "ae_kqueue.c"  
  14.     #else  
  15.     #include "ae_select.c"  
  16.     #endif  
  17. #endif  

多路IO封装(以ae_epoll.c为例):
aeApiCreate:创建句柄(epoll_create)
aeApiFree:关闭句柄(close)
aeApiAddEvent:事件添加(epoll_ctl)
aeApiDelEvent:事件删除(epoll_ctl)
aeApiPoll:等待事件发生(epoll_wait)

网络简要说明:    
1. 初始化server, 等待客户端连接,并注册事件,回调函数acceptTcpHandler/acceptUnixHandler
[cpp] view plaincopy
  1. if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,  
  2.        acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");  
  3.    if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,  
  4.        acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");  
2. 回调函数acceptTcpHandler/acceptUnixHandler
在监听到新连接请求时,接收连接,创建redisClient对象,并注册事件(回调函数readQueryFromClient)
[cpp] view plaincopy
  1. void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {  
  2.     redisClient *c = (redisClient*) privdata;  
  3.     char buf[REDIS_IOBUF_LEN];  
  4.     int nread;  
  5.     REDIS_NOTUSED(el);  
  6.     REDIS_NOTUSED(mask);  
  7.   
  8.     nread = read(fd, buf, REDIS_IOBUF_LEN);  
  9.     if (nread == -1) {  
  10.         if (errno == EAGAIN) {  
  11.             nread = 0;  
  12.         } else {  
  13.             redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));  
  14.             freeClient(c);  
  15.             return;  
  16.         }  
  17.     } else if (nread == 0) {  
  18.         redisLog(REDIS_VERBOSE, "Client closed connection");  
  19.         freeClient(c);  
  20.         return;  
  21.     }  
  22.     if (nread) {  
  23.         c->querybuf = sdscatlen(c->querybuf,buf,nread);  
  24.         c->lastinteraction = time(NULL);  
  25.     } else {  
  26.         return;  
  27.     }  
  28.     if (sdslen(c->querybuf) > server.client_max_querybuf_len) {  
  29.         sds ci = getClientInfoString(c), bytes = sdsempty();  
  30.   
  31.         bytes = sdscatrepr(bytes,c->querybuf,64);  
  32.         redisLog(REDIS_WARNING,"Closing client that reached max query buffer length: %s (qbuf initial bytes: %s)", ci, bytes);  
  33.         sdsfree(ci);  
  34.         sdsfree(bytes);  
  35.         freeClient(c);  
  36.         return;  
  37.     }  
  38.     processInputBuffer(c);  
  39. }  
3. 客户端请求处理:
在接收到客户端请求数据后,首先对请求进行解析,解析完成后反馈请求
[cpp] view plaincopy
  1. void processInputBuffer(redisClient *c) {  
  2.     /* Keep processing while there is something in the input buffer */  
  3.     while(sdslen(c->querybuf)) {  
  4.         /* Immediately abort if the client is in the middle of something. */  
  5.         if (c->flags & REDIS_BLOCKED || c->flags & REDIS_IO_WAIT) return;  
  6.   
  7.         /* REDIS_CLOSE_AFTER_REPLY closes the connection once the reply is 
  8.          * written to the client. Make sure to not let the reply grow after 
  9.          * this flag has been set (i.e. don't process more commands). */  
  10.         if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;  
  11.   
  12.         /* Determine request type when unknown. */  
  13.         if (!c->reqtype) {  
  14.             if (c->querybuf[0] == '*') {  
  15.                 c->reqtype = REDIS_REQ_MULTIBULK;  
  16.             } else {  
  17.                 c->reqtype = REDIS_REQ_INLINE;  
  18.             }  
  19.         }  
  20.   
  21.         if (c->reqtype == REDIS_REQ_INLINE) {  
  22.             if (processInlineBuffer(c) != REDIS_OK) break;  
  23.         } else if (c->reqtype == REDIS_REQ_MULTIBULK) {  
  24.             if (processMultibulkBuffer(c) != REDIS_OK) break;  
  25.         } else {  
  26.             redisPanic("Unknown request type");  
  27.         }  
  28.   
  29.         /* Multibulk processing could see a <= 0 length. */  
  30.         if (c->argc == 0) {  
  31.             resetClient(c);  
  32.         } else {  
  33.             /* Only reset the client when the command was executed. */  
  34.             if (processCommand(c) == REDIS_OK)  
  35.                 resetClient(c);  
  36.         }  
  37.     }  
  38. }  
在请求处理完成后,反馈结果.
[cpp] view plaincopy
  1. int processCommand(redisClient *c) {  
  2.     .......  
  3.     /* Exec the command */  
  4.     if (c->flags & REDIS_MULTI &&  
  5.         c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&  
  6.         c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)  
  7.     {  
  8.         queueMultiCommand(c);  
  9.         addReply(c,shared.queued);  
  10.     } else {  
  11.         if (server.vm_enabled && server.vm_max_threads > 0 &&  
  12.             blockClientOnSwappedKeys(c)) return REDIS_ERR;  
  13.         call(c);  
  14.     }  
  15.     return REDIS_OK;  
  16. }  
[cpp] view plaincopy
  1. void addReply(redisClient *c, robj *obj) {  
  2.     if (_installWriteEvent(c) != REDIS_OK) return;  
  3.     redisAssert(!server.vm_enabled || obj->storage == REDIS_VM_MEMORY);  
  4.   
  5.     /* This is an important place where we can avoid copy-on-write 
  6.      * when there is a saving child running, avoiding touching the 
  7.      * refcount field of the object if it's not needed. 
  8.      * 
  9.      * If the encoding is RAW and there is room in the static buffer 
  10.      * we'll be able to send the object to the client without 
  11.      * messing with its page. */  
  12.     if (obj->encoding == REDIS_ENCODING_RAW) {  
  13.         if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)  
  14.             _addReplyObjectToList(c,obj);  
  15.     } else {  
  16.         /* FIXME: convert the long into string and use _addReplyToBuffer() 
  17.          * instead of calling getDecodedObject. As this place in the 
  18.          * code is too performance critical. */  
  19.         obj = getDecodedObject(obj);  
  20.         if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != REDIS_OK)  
  21.             _addReplyObjectToList(c,obj);  
  22.         decrRefCount(obj);  
  23.     }  
  24. }  
[cpp] view plaincopy
  1. int _installWriteEvent(redisClient *c) {  
  2.     if (c->fd <= 0) return REDIS_ERR;  
  3.     if (c->bufpos == 0 && listLength(c->reply) == 0 &&  
  4.         (c->replstate == REDIS_REPL_NONE ||  
  5.          c->replstate == REDIS_REPL_ONLINE) &&  
  6.         aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,  
  7.         sendReplyToClient, c) == AE_ERR) return REDIS_ERR;  
  8.     return REDIS_OK;  
  9. }  
Redis执行完客户端请求后,会调用addReply,在addReply中调用installWriteEvent来注册一个事件,并绑定事件处理函数sendReplyToClient,用来把响应发送到client。
4.主循环
处理定时事件和注册事件
[cpp] view plaincopy
  1. void aeMain(aeEventLoop *eventLoop) {  
  2.     eventLoop->stop = 0;  
  3.     while (!eventLoop->stop) {  
  4.         if (eventLoop->beforesleep != NULL)  
  5.             eventLoop->beforesleep(eventLoop);  
  6.         aeProcessEvents(eventLoop, AE_ALL_EVENTS);  
  7.     }  
  8. }  
beforesleep通过aeSetBeforeSleepProc定义,主要是特殊处理vm和aof相关的请求
[cpp] view plaincopy
  1. int aeProcessEvents(aeEventLoop *eventLoop, int flags)  
  2. {  
  3.     int processed = 0, numevents;  
  4.   
  5.     /* Nothing to do? return ASAP */  
  6.     if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;  
  7.   
  8.     /* Note that we want call select() even if there are no 
  9.      * file events to process as long as we want to process time 
  10.      * events, in order to sleep until the next time event is ready 
  11.      * to fire. */  
  12.     if (eventLoop->maxfd != -1 ||  
  13.         ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {  
  14.         int j;  
  15.         aeTimeEvent *shortest = NULL;  
  16.         struct timeval tv, *tvp;  
  17.   
  18.         if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))  
  19.             shortest = aeSearchNearestTimer(eventLoop);  
  20.         if (shortest) {  
  21.             long now_sec, now_ms;  
  22.   
  23.             /* Calculate the time missing for the nearest 
  24.              * timer to fire. */  
  25.             aeGetTime(&now_sec, &now_ms);  
  26.             tvp = &tv;  
  27.             tvp->tv_sec = shortest->when_sec - now_sec;  
  28.             if (shortest->when_ms < now_ms) {  
  29.                 tvp->tv_usec = ((shortest->when_ms+1000) - now_ms)*1000;  
  30.                 tvp->tv_sec --;  
  31.             } else {  
  32.                 tvp->tv_usec = (shortest->when_ms - now_ms)*1000;  
  33.             }  
  34.             if (tvp->tv_sec < 0) tvp->tv_sec = 0;  
  35.             if (tvp->tv_usec < 0) tvp->tv_usec = 0;  
  36.         } else {  
  37.             /* If we have to check for events but need to return 
  38.              * ASAP because of AE_DONT_WAIT we need to se the timeout 
  39.              * to zero */  
  40.             if (flags & AE_DONT_WAIT) {  
  41.                 tv.tv_sec = tv.tv_usec = 0;  
  42.                 tvp = &tv;  
  43.             } else {  
  44.                 /* Otherwise we can block */  
  45.                 tvp = NULL; /* wait forever */  
  46.             }  
  47.         }  
  48.   
  49.         numevents = aeApiPoll(eventLoop, tvp);  
  50.         for (j = 0; j < numevents; j++) {  
  51.             aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];  
  52.             int mask = eventLoop->fired[j].mask;  
  53.             int fd = eventLoop->fired[j].fd;  
  54.             int rfired = 0;  
  55.   
  56.         /* note the fe->mask & mask & ... code: maybe an already processed 
  57.              * event removed an element that fired and we still didn't 
  58.              * processed, so we check if the event is still valid. */  
  59.             if (fe->mask & mask & AE_READABLE) {  
  60.                 rfired = 1;  
  61.                 fe->rfileProc(eventLoop,fd,fe->clientData,mask);  
  62.             }  
  63.             if (fe->mask & mask & AE_WRITABLE) {  
  64.                 if (!rfired || fe->wfileProc != fe->rfileProc)  
  65.                     fe->wfileProc(eventLoop,fd,fe->clientData,mask);  
  66.             }  
  67.             processed++;  
  68.         }  
  69.     }  
  70.     /* Check time events */  
  71.     if (flags & AE_TIME_EVENTS)  
  72.         processed += processTimeEvents(eventLoop);  
  73.   
  74.     return processed; /* return the number of processed file/time events */  
  75. }  
rfileProc和wfileProc即注册事件时定义的回调函数
原创粉丝点击