ngx ------ngx_cache_manager_process_cycle
阅读原文时间:2023年07月09日阅读:2

static void
ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
{-----------------------------------
/*
* Set correct process type since closing listening Unix domain socket
* in a master process also removes the Unix domain socket file.
*/
ngx_process = NGX_PROCESS_HELPER;

ngx\_close\_listening\_sockets(cycle);

/\* Set a moderate number of connections for a helper process. \*/  
cycle->connection\_n = 512;  

//work 进程 初始化
ngx_worker_process_init(cycle, -1);

ngx\_memzero(&ev, sizeof(ngx\_event\_t));  
ev.handler = ctx->handler; //ngx\_cache\_manager\_process\_handler  ngx\_cache\_loader\_process\_handler  
ev.data = ident;  
ev.log = cycle->log;  
ident\[3\] = (void \*) -1;

ngx\_use\_accept\_mutex = 0;

ngx\_setproctitle(ctx->name);

ngx\_add\_timer(&ev, ctx->delay, NGX\_FUNC\_LINE); //ctx->dealy秒执行ctx->handler;

for ( ;; ) {

    if (ngx\_terminate || ngx\_quit) {  
        ngx\_log\_error(NGX\_LOG\_NOTICE, cycle->log, 0, "exiting");  
        exit(0);  
    }

    if (ngx\_reopen) {  
        ngx\_reopen = 0;  
        ngx\_log\_error(NGX\_LOG\_NOTICE, cycle->log, 0, "reopening logs");  
        ngx\_reopen\_files(cycle, -1);  
    }  

------------//work 进程 循环处理 fd io 以及 timer------------------------------------------------------
ngx_process_events_and_timers(cycle);
}
}

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta;

---------------------------------------  
ngx\_use\_accept\_mutex = 1;  

//ngx_use_accept_mutex表示是否需要通过对accept加锁来解决惊群问题。当nginx worker进程数>1时且配置文件中打开accept_mutex时,这个标志置为1
if (ngx_use_accept_mutex) {
/*
ngx_accept_disabled表示此时满负荷,没必要再处理新连接了,我们在nginx.conf曾经配置了每一个nginx worker进程能够处理的最大连接数,
当达到最大数的7/8时,ngx_accept_disabled为正,说明本nginx worker进程非常繁忙,将不再去处理新连接,这也是个简单的负载均衡
在当前使用的连接到达总连接数的7/8时,就不会再处理新连接了,同时,在每次调用process_events时都会将ngx_accept_disabled减1,
直到ngx_accept_disabled降到总连接数的7/8以下时,才会调用ngx_trylock_accept_mutex试图去处理新连接事件 如果ngx_accept_disabled大于了0,就表示该进程接受的
连接过多,因此就放弃一次争抢accept mutex的机会,同时将 自己减1。然后,继续处理已有连接上的事件。Nginx就借用 此变量实现了进程关于连接的基本负载均衡。

*/
if (ngx_accept_disabled > 0) { //为正说明可用连接用了超过八分之七,则让其他的进程在下面的else中来accept
ngx_accept_disabled--;

    } else {  
        /\*  
             如果ngx\_trylock\_accept\_mutex方法没有获取到锁,接下来调用事件驱动模块的process\_events方法时只能处理已有的连接上的事件;  
             如果获取到了锁,调用process\_events方法时就会既处理已有连接上的事件,也处理新连接的事件。

            如何用锁来避免惊群?  
               尝试锁accept mutex,只有成功获取锁的进程,才会将listen  
               套接字放入epoll中。因此,这就保证了只有一个进程拥有  
               监听套接口,故所有进程阻塞在epoll\_wait时,不会出现惊群现象。  
               这里的ngx\_trylock\_accept\_mutex函数中,如果顺利的获取了锁,那么它会将监听端口注册到当前worker进程的epoll当中   

           获得accept锁,多个worker仅有一个可以得到这把锁。获得锁不是阻塞过程,都是立刻返回,获取成功的话ngx\_accept\_mutex\_held被置为1。  
           拿到锁,意味着监听句柄被放到本进程的epoll中了,如果没有拿到锁,则监听句柄会被从epoll中取出。  
          \*/  
    /\*  
       如果ngx\_use\_accept\_mutex为0也就是未开启accept\_mutex锁,则在ngx\_worker\_process\_init->ngx\_event\_process\_init 中把accept连接读事件统计到epoll中  
       否则在ngx\_process\_events\_and\_timers->ngx\_process\_events\_and\_timers->ngx\_trylock\_accept\_mutex中把accept连接读事件统计到epoll中;  

---------------------------尝试锁accept mutex,只有成功获取锁的进程,才会将listen 套接字放入epoll中。因此,这就保证了只有一个进程拥有  监听套接口,故所有进程阻塞在epoll_wait时,不会出现惊群现象。 

*/
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) { //不管是获取到锁还是没获取到锁都是返回NGX_OK
return;
}

        /\*  
            拿到锁的话,置flag为NGX\_POST\_EVENTS,这意味着ngx\_process\_events函数中,任何事件都将延后处理,会把accept事件都放到  
            ngx\_posted\_accept\_events链表中,epollin|epollout事件都放到ngx\_posted\_events链表中 ---  

 获取锁的进程,将添加一个NGX_POST_EVENTS标志, 此标志的作用是将所有产生的事件放入一个队列中, 等释放锁后,再慢慢来处理事件。
因为,处理事件可能 会很耗时,如果不先释放锁再处理的话,该进程就长 时间霸占了锁,导致其他进程无法获取锁,这样accept 的效率就低了。            
*/
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;

        } else {  
            /\*  
                拿不到锁,也就不会处理监听的句柄,这个timer实际是传给epoll\_wait的超时时间,修改为最大ngx\_accept\_mutex\_delay意味  
                着epoll\_wait更短的超时返回,以免新连接长时间没有得到处理  
                \*/  
            if (timer == NGX\_TIMER\_INFINITE  
                || timer > ngx\_accept\_mutex\_delay)  
            {   //如果没获取到锁,则延迟这么多ms重新获取说,继续循环,也就是技术锁被其他进程获得,本进程最多在epoll\_wait中睡眠0.5s,然后返回  
                timer = ngx\_accept\_mutex\_delay; //保证这么多时间超时的时候出发epoll\_wait返回,从而可以更新内存时间  
            }  
        }  
    }  
}

delta = ngx\_current\_msec;

/\*  
1.如果进程获的锁,并获取到锁,则该进程在epoll事件发生后会触发返回,然后得到对应的事件handler,加入延迟队列中,然后释放锁,然  
后在执行对应handler,同时更新时间,判断该进程对应的红黑树中是否有定时器超时,  
2.如果没有获取到锁,则默认传给epoll\_wait的超时时间是0.5s,表示过0.5s继续获取锁,0.5s超时后,会跟新当前时间,同时判断是否有过期的  
  定时器,有则指向对应的定时器函数  
\*/

/*
1.ngx_event_s可以是普通的epoll读写事件(参考ngx_event_connect_peer->ngx_add_conn或者ngx_add_event),通过读写事件触发

2.也可以是普通定时器事件(参考ngx_cache_manager_process_handler->ngx_add_timer(ngx_event_add_timer)),通过ngx_process_events_and_timers中的
epoll_wait返回,可以是读写事件触发返回,也可能是因为没获取到共享锁,从而等待0.5s返回重新获取锁来跟新事件并执行超时事件来跟新事件并且判断定
时器链表中的超时事件,超时则执行从而指向event的handler,然后进一步指向对应r或者u的->write_event_handler read_event_handler

3.也可以是利用定时器expirt实现的读写事件(参考ngx_http_set_write_handler->ngx_add_timer(ngx_event_add_timer)),触发过程见2,只是在handler中不会执行write_event_handler read_event_handler
*/

//linux下,普通网络套接字调用ngx\_epoll\_process\_events函数开始处理,异步文件i/o设置事件的回调方法为ngx\_epoll\_eventfd\_handler  
(void) ngx\_process\_events(cycle, timer, flags);

delta = ngx\_current\_msec - delta; //(void) ngx\_process\_events(cycle, timer, flags)中epoll等待事件触发过程花费的时间

ngx\_log\_debug1(NGX\_LOG\_DEBUG\_EVENT, cycle->log, 0, "epoll\_wait timer range(delta): %M", delta);

//当感应到来自于客户端的accept事件,epoll\_wait返回后加入到post队列,执行完所有accpet连接事件后,立马释放ngx\_accept\_mutex锁,这样其他进程就可以立马获得锁accept客户端连接  

//* 这里完成对队列中的accept事件的处理,实际就是调用 ngx_event_accept函数来获取一个新的连接,然后放入 epoll中。 
ngx_event_process_posted(cycle, &ngx_posted_accept_events); //一般执行ngx_event_accept

//释放锁后再处理下面的EPOLLIN EPOLLOUT请求   --------------所有accept事件处理完成,如果拥有锁的话,就赶紧释放了。 其他进程还等着抢了。  
if (ngx\_accept\_mutex\_held) {  
    ngx\_shmtx\_unlock(&ngx\_accept\_mutex);  
}

if (delta) {  
    ngx\_event\_expire\_timers(); //处理红黑树队列中的超时事件handler  
}

/\*  
 然后再处理正常的数据读写请求。因为这些请求耗时久,所以在ngx\_process\_events里NGX\_POST\_EVENTS标志将事件都放入ngx\_posted\_events  
 链表中,延迟到锁释放了再处理。  
 \*/  
ngx\_event\_process\_posted(cycle, &ngx\_posted\_events); //普通读写事件放在释放ngx\_accept\_mutex锁后执行,提高客户端accept性能  

}

https://yikun.github.io/2014/03/26/nginxevent/