高性能的事件併發框架的實現

引言:lighttpd與nginx這些新興的webserver框架都以支撐大規模的併發而聞名,如下介紹一個我本身實現的並使用的併發事件框架,這個框架不止包括網絡事件,也包括其餘耗時事件好比IO等均可以在事件框架中運行。node


1,監聽事件的初始化。
如下是一個事件結構的大體成員,咱們以Epoll模型爲例子,包括描述符的數組,和事件的回調函數。
初始化要分配以最大值爲限制的fdnode的數組。
typedef struct fdevents {
struct server *srv;
fdevent_handler_t type;
fdnode **fdarray;
size_t maxfds;
#ifdef EPLOLL
int epoll_fd;
struct epoll_event *epoll_events;
#endif
int (*reset)(struct fdevents *ev);
void (*free)(struct fdevents *ev);
int (*event_set)(struct fdevents *ev, int fde_ndx, int fd, int events);
int (*event_del)(struct fdevents *ev, int fde_ndx, int fd);
int (*event_get_revent)(struct fdevents *ev, size_t ndx);
int (*event_get_fd)(struct fdevents *ev, size_t ndx);
int (*event_next_fdndx)(struct fdevents *ev, int ndx);
int (*poll)(struct fdevents *ev, int timeout_ms);
int (*fcntl_set)(struct fdevents *ev, int fd);
} fdevents;
2,事件的註冊
int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) 
將事件新增進數組fdarray中,這樣在事件知足時,就會再相應的條件觸發回調函數handler,
這個註冊會初始化一個fdnode 
3,事件的設置
int fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events) 
將對應的fdnode 指定爲那種IO事件的響應,IO事件有以下種類
#define FDEVENT_IN     BV(0)
#define FDEVENT_PRI    BV(1)
#define FDEVENT_OUT    BV(2)
#define FDEVENT_ERR    BV(3)
#define FDEVENT_HUP    BV(4)
#define FDEVENT_NVAL   BV(5)
 
4,事件Poll動做,將全部觸發的事件返回到fdndx的鏈表上,處理邏輯能夠對相應的事件作邏輯處理
int fdevent_poll(fdevents *ev, int timeout_ms)
 
5,fd的回調函數
typedef handler_t (*fdevent_handler)(struct server *srv, void *ctx, int revents);
當有事件發生是,全部邏輯都包括在回調函數中,如下代碼是poll後的典型處理
do {
fdevent_handler handler;
void *context;
handler_t r;
fd_ndx  = fdevent_event_next_fdndx (srv->ev, fd_ndx);
if (-1 == fd_ndx) break; 
                                //這裏的3種回調函數是事件模型對應的真實實現,好比epoll的實現
                                //則對應的是epoll實現代碼中的
                                //fdevent_event_get_revent獲取對應事件
                                //fdevent_event_get_fd 獲取文件描述符
                                // fdevent_get_handler 獲取事件回調函數
                                //  fdevent_get_context 獲取事件邏輯數據
revents = fdevent_event_get_revent (srv->ev, fd_ndx);
fd      = fdevent_event_get_fd     (srv->ev, fd_ndx);
handler = fdevent_get_handler(srv->ev, fd);
context = fdevent_get_context(srv->ev, fd);
 
switch (r = (*handler)(srv, context, revents)) {
case HANDLER_FINISHED:                    //針對處理的返回值作不一樣的處理
case HANDLER_GO_ON:                        
case HANDLER_WAIT_FOR_EVENT:
case HANDLER_WAIT_FOR_FD:
break;
case HANDLER_ERROR:
SEGFAULT();
break;
default:
log_error_write(srv, __FILE__, __LINE__, "d", r);
break;
}
} while (--n > 0);
 
5,事件框架,多路複用模型(epoll,select,kquque,FIFO等),具體的數據邏輯相互分離
 
 
6,網絡事件註冊進事件框架(創建鏈接)
網絡事件也做爲普通事件之一註冊進事件框架
fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket);
fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN);
 
network_server_handle_fdevent 實際上作了accpet的實現,當網絡事件被Poll函數觸發是,由network_server_handle_fdevent 來執行創建鏈接的動做。
7,網絡事件註冊進事件框架(讀寫事件)
在network_server_handle_fdevent 中註冊讀寫事件,針對讀寫來實現對於的操做在connection_handle_fdevent實現
fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con);
 
8,讀寫事件的邏輯抽象化實現
業務邏輯跟進本身邏輯須要獨立開來,對於網絡事件來講,只須要關心是否可讀寫就能夠,爲此每一個鏈接都要維護一個緩衝區鏈表,將業務與網絡進行隔離。
typedef struct {
chunk *first;
chunk *last;
chunk *unused;
size_t unused_chunks;
array *tempdirs;
off_t  bytes_in, bytes_out;
} chunkqueue;
相關文章
相關標籤/搜索