nginx如何避免worker进程的惊群效应

首先看一下事件模块,其初始化工作主要由ngx_event_process_init完成;
Prefork-worker:调用ngx_event_core_module->ngx_event_module_init,只是初始化一些变量;
After-fork:调用ngx_worker_process_init --> ngx_event_process_init
 1 开启accept mutex
 2 初始化红黑树定时器ngx_event_timer_rbtree
 3 调用epoll模块的init方法ngx_epoll_init, 调用epoll_create
 4 设置时间精度timer_resolution
 5 预分配cycle->connections/read_events/write_events数组
 6 将监听套接字读事件处理方法设置为ngx_event_accept

监听套接字由master初始化,每个worker都继承一份,同时创建各自的epoll结构体和定时器;

1:如何确保同一时间只有一个worker操纵监听套接字?
答案: accept_mutex,同一时间只有一个worker能获取
ngx_worker_process_init --> ngx_event_process_init --> ngx_process_events_add_timers(位于for(;;)循环中)
 1 若当前worker负载已超阈值,则不去尝试获取mutex即接收客户端新连接;
 2 若当前worker成功获取mutex,则将事件flags设为NGX_POST_EVENTS,epoll_wait返回时将事件handler分别放入accepted队列和post队列,处理完前者后立即释放mutex,然后处理后者,  减少mutex占用时间;
 3 若当前worker没有获取mutex,则在epoll_wait返回后当场执行各事件对应的handler;
代码片段
   if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;
        } else {
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }
            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;  --获取到mutex的worker,将flag置为post,epoll_wait返回时不立即执行event->handler,而是将其放入accept和post队列;
            } else {
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }
.......

    (void) ngx_process_events(cycle, timer, flags);  --使用epoll时为ngx_epoll_process_events,epoll_wait返回时,根据rev->accept将返回事件分别加入accepted和posted队列
.......

    if (ngx_posted_accept_events) {  --先执行新建连接事件即accept队列,调用其ev->hander即ngx_event_accept(),然后释放mutex,再接着执行post队列;
        ngx_event_process_posted(cycle, &ngx_posted_accept_events);
    }

    if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }
.......
    if (ngx_posted_events) {
        if (ngx_threaded) {
            ngx_wakeup_worker_thread(cycle);
        } else {
            ngx_event_process_posted(cycle, &ngx_posted_events);
        }
    }


2 为什么获取accept_mutex就能确保有且只有当前worker能监听listening port
答案:ngx_try_lock_accept_mutex --> ngx_enable_accept_events,当worker获取mutex后,通过ngx_enable_accept_events将监听套接口的读事件加入epoll监控;
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
      ......
        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }
      ......
}
}

ngx_enable_accept_events(ngx_cycle_t *cycle)
{
   for (i = 0; i < cycle->listening.nelts; i++) {
        c = ls[i].connection;

        if (c->read->active) {
            continue;
        }

        if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
            if (ngx_add_conn(c) == NGX_ERROR) {
                return NGX_ERROR;
            }
        } else {
            if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }
    }
   ........
}

 

 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。