Nginx Insight: Event Loop
Nginx 区别于它之前的主流 Web 服务器的地方实际上是 Event Driven。
Event Loop
Event Loop 的结构实质上就是一个无限循环。
for(;;) {
do_one_event();
}
Nginx Worker Process Event Loop
for(;;) {
ngx_process_events_and_timers(cycle);
}
Nginx 的事件处理
proc ngx_process_events_and_timers {cycle} {
set timeout [find_most_recent_timer]
move_posted_events -from $ngx_posted_next_events -to $ngx_posted_events
process_events -timeout $timeout
process_posted_events $ngx_posted_accept_events
expire_timers
process_posted_events $ngx_posted_events
}
- Event 主要是三种
accept
- Socket IO
- Timer
accept
事件交由$ngx_posted_accept_events
队列处理- 模块可以通过
$ngx_posted_events
队列介入 Nginx 的事件循环
Nginx 的事件队列
Nginx 的 event 模块实现了三个事件队列:
ngx_posted_events
ngx_posted_next_events
ngx_posted_accept_events
事件队列操作:
ngx_post_event(ev, &ngx_posted_events)
ngx_delete_posted_event(ev)
每次事件循环最后,会调用并清空 ngx_posted_events
里的所有事件。
ngx_posted_events
队列里的事件允许添加新的事件到当前队列。理论上可以在这里形成死循环。
如果希望一个事件重复触发,可以将其加入 ngx_posted_next_events
队列。事件循环会在下一个循环里把 ngx_posted_next_events
里的第一个事件移入 ngx_posted_events
队列。
用 Tcl 代码表示即 push ngx_posted_events [shift ngx_posted_next_events]
Nginx 事件循环里的 Socket
对于客户端网络连接,基本思路就是监听 IO 事件。监听方式可以是
select
pool
- epoll
- kqueue
对于监听链接 (Listening Socket / Aassive Socket),Nginx 也一并通过异步监听的方式处理了。这样可以异步地监听多个端口。
思路用 Tcl 的方式表示,大致如下:
foreach sock $socket_list {
fileevent $sock readable|writable {
ev->ready = 1;
ev->available = -1;
ngx_post_event(ev, &ngx_posted_events);
}
}
foreach sock $server_socket_list {
fileevent $sock readable|writable {
ev->ready = 1;
ev->available = -1;
ngx_post_event(ev, &ngx_posted_accept_events);
}
}
可以看到,accept
事件有单独的事件队列,并且优先级高于普通的 IO 事件。
Nginx 事件循环里的 Timer
File: event/ngx_event_timer.c
定时器添加和删除:
#define ngx_add_timer ngx_event_add_timer
#define ngx_del_timer ngx_event_del_timer
void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer){
ev->timer_set = 1;
insert_timer_to_rbtree $ev
}
void ngx_event_del_timer(ngx_event_t *ev){
ev->timer_set = 0;
delete_timer_from_rbtree $ev
}
定时器触发时:
void ngx_event_expire_timers(){
foreach ev [expired_timers_list] {
ev->timer_set = 0;
ev->timedout = 1;
ev->handler(ev);
}
}
一些和定时器有关的常数:
#define NGX_TIMER_INFINITE (ngx_msec_t) -1
#define NGX_TIMER_LAZY_DELAY 300
Nginx Master Process Event Loop
for(;;){
sigsuspend(&set);
}