前言
本小节主要讨论事件模块中如何接受新连接,其中涉及到了惊群以及负载均衡的处理。
如何建立新连接
之前在分析ngx_event_process_init
函数时,将所有空闲连接形成链表之后,它会遍历所有监听端口并将其读事件的回调函数设置为ngx_event_accept
接着会把监听连接的读事件添加到ngx_epoll_module
模块中。当执行ngx_epoll_process_events
时,如果有新连接事件出现,则会调用ngx_event_accept
来建立新的连接。
建立新连接的回调函数
建立新连接的回调函数是ngx_event_accept
,每个监听连接的读事件的handler
都在ngx_event_process_init
中设置为ngx_event_accept
。
下面直接看它的源码(省略了不必要的部分):
void
ngx_event_accept(ngx_event_t *ev)
{
socklen_t socklen;
ngx_err_t err;
ngx_log_t *log;
ngx_socket_t s;
ngx_event_t *rev, *wev;
ngx_listening_t *ls;
ngx_connection_t *c, *lc;
ngx_event_conf_t *ecf;
u_char sa[NGX_SOCKADDRLEN];
......
//获取ngx_event_core_module模块的配置项结构体指针
ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
ev->available = 1;
} else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
ev->available = ecf->multi_accept;
}
lc = ev->data;
ls = lc->listening;
ev->ready = 0;
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
"accept on %V, ready: %d", &ls->addr_text, ev->available);
/* 这个循环以available标志位作为判断条件
* 当该标志位被置1,则代表每次尽可能的多建立新连接
* 否则每次调用ngx_event_accept只建立一个新连接
*/
do {
socklen = NGX_SOCKADDRLEN;
......
//调用accept接受新连接
s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
if (s == -1) {
err = ngx_socket_errno;
if (err == NGX_EAGAIN) {
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
"accept() not ready");
return;
}
ngx_log_error((ngx_uint_t) ((err == NGX_ECONNABORTED) ?
NGX_LOG_ERR : NGX_LOG_ALERT),
ev->log, err, "accept() failed");
if (err == NGX_ECONNABORTED) {
if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
ev->available--;
}
if (ev->available) {
continue;
}
}
return;
}
.....
/* 这一步操作是设置负载均衡的阀值
* ngx_accept_disabled的初值是负数,为总连接的7/8
* 当ngx_accept_disabled为负数时,
* 则不会触发负载均衡的操作
* 当ngx_accept_disabled是正数时,就会触发负载均衡的操作了(当前进程不会处理新连接,而是将ngx_accept_disabled减1)
*/
ngx_accept_disabled = ngx_cycle->connection_n / 8
- ngx_cycle->free_connection_n;
//获取一个空闲连接
c = ngx_get_connection(s, ev->log);
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
ngx