accept静群的前提是多进程下共享服务端socket的fd,因此可以先看nginx-0x11-进程模型。
1 nginx服务要监听哪些端口
1.1 怎么配置
在nginx.conf
配置文件中指定要监听的端口,告诉nginx服务监听在哪些端口上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server { listen 80; server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / { root html; index index.html index.htm; }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; }
|
1.2 socket插口初始化监听
在启动过程中会初始化一个重要的变量cycle
,在ngx_init_cycle
这个方法中涉及到开启socket套接字的监听。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
if (ngx_open_listening_sockets(cycle) != NGX_OK) { goto failed; }
|
最后master进程把准备好的socket信息存储在cycle的listening数组中,将来大家共享。
1 2 3 4 5 6 7 8 9 10
|
ngx_array_t listening;
|
2 单进程下怎么注册监听连接的
虽然单进程下肯定不存在所谓的惊群,但是为了理解后面worker进程向内核多路复用器注册连接事件监听,有必要先看下在单进程下的注册连接事件的时机。
ngx_process_cycle.c中ngx_single_process_cycle
方法会回调各模块的init_process
方法,在ngx_event.c中的函数ngx_event_process_init
中。
1 2 3 4 5 6 7 8 9 10 11
|
if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; }
|
3 多进程下监听连接的时机
3.1 工作进程的初始化
在master进程创建worker进程成功后,每个worker进程会执行到ngx_worker_process_init
方法里面,在这个方法里面,又会回调模块的初始化,跟上面单进程一样,会执行到事件模块的初始化方法,在ngx_event.c中的函数ngx_event_process_init
中。只是在多进程下,会标记需要accept锁,并且此时不进行连接事件监听。
3.1.1 事件模块初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1;
ngx_accept_mutex_held = 0; ngx_accept_mutex_delay = ecf->accept_mutex_delay;
} else { ngx_use_accept_mutex = 0; }
|
3.1.2 不注册连接事件
1 2 3 4
| if (ngx_use_accept_mutex) {
continue; }
|
3.2 worker进程的事件循环
worker进程初始化好后就开启了事件循环,在ngx_worker_process_cycle
方法调用ngx_process_events_and_timers
方法。
4 注册监听连接事件
在调用内核获取就绪事件之前,会尝试抢accept锁。
- 如果没有参与抢锁,就不会发生注册连接事件监听的动作
- 如果抢锁失败,就要把自己曾经注册过监听连接的事件全部移除掉
只有抢到锁的worker进程才有资格监听连接。
4.1 抢锁成功注册事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_listening_t *ls; ngx_connection_t *c;
ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) {
c = ls[i].connection;
if (c == NULL || c->read->active) { continue; } if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } }
return NGX_OK; }
|
4.2 抢锁失败移除事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT) == NGX_ERROR) { return NGX_ERROR; }
|