惊群:是指在多线程/多进程中,当有一个客户端发生链接请求时,多线程/多进程都被唤醒,然后只仅仅有一个进程/线程处理成功,其他进程/线程还是回到睡眠状态,这种现象就是惊群。
惊群是经常发生现在server端,父进程fork很多子进程,当有客户端有链接请求时,所有子进程都被唤醒,可是只有一个子进程处理请求,其他子进程就继续休眠。
Nginx 处理集群的方法:
ngx_postd_events:是监听套接口上的事件
ngx_posted_accept_events:是新建连接事件(也就是监听套接口上发生的可读事件)
ngx_accept_mutex_held:看是否拥有锁
Nginx中统一时刻,监听套接口只可能被一个进程监控,并且在建立新连接时,通过获得锁,来确定那个子进程进行监控。当当前工作进程负载较小时,即当前活动连接数 < 最大可承受连接数的*7/8,就可以将监听套接口加入到自身的时间监控机制中,从而带来新的请求,如果当前进程的负载很大,则将该监听套接口从自身的事件中删除,避免带来的新的客户端的请求而带来更大的负载。
关于锁,持锁者必须缩短自身拥有锁的事件,所以Nginx是将很多时间延迟到释放锁之后进行处理,把锁释放,尽量释放拥有锁的时间,让其他进程有机会获得锁。
1 if (ngx_use_accept_mutex) 2 { 3 // 4 当前的连接数是否超过最大可承受连接数的 7/8 5 if (ngx_accept_disabled > 0) 6 { 7 ngx_accept_disabled--; 8 } 9 else 10 { 11 //获得锁 12 if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) 13 { 14 return; 15 } 16 //获得锁,表明新建连接后,将所有事件延后处理,先释放锁,是的拥有锁的事件最小化 17 if (ngx_accept_mutex_held) 18 { 19 flags |= NGX_POST_EVENTS; 20 } 21 else 22 { 23 //没有获得锁传给epoll_wait使得epoll_wait 使得返回时间剪短防止长时间连接没有返 //回 24 if (timer == NGX_TIMER_INFINITE|| timer > ngx_accept_mutex_delay) 25 { 26 timer = ngx_accept_mutex_delay; 27 } 28 } 29 } 30 31 ngx_accept_disabled = ngx_cycle->connection_n / 8 32 - ngx_cycle->free_connection_n; 33 34 35 delta = ngx_current_msec; 36 37 (void) ngx_process_events(cycle, timer, flags); 38 39 delta = ngx_current_msec - delta; 40 41 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, 42 "timer delta: %M", delta); 43 44 //新建链接缓存 45 ngx_event_process_posted(cycle, &ngx_posted_accept_events); 46 47 //释放锁 48 if (ngx_accept_mutex_held) { 49 ngx_shmtx_unlock(&ngx_accept_mutex); 50 } 51 52 if (delta) { 53 ngx_event_expire_timers(); 54 } 55 ngx_event_process_posted(cycle, &ngx_posted_events); 56 /* 57 此处释放锁并没有将监听套接字从时间监控机制中删除,在接下来的ngx_posted_events缓存事件时,互斥锁被另一个进程争取,并且把监听套接字加入到事件监听机制中,而同一时间,一个监听套接字被多个程序占有,因此进程在处理完ngx_posted_events后去争取锁,发现锁被其他进程占用而失败,所以将该监听套接口从自身的事件监听机制中删除,然后才进行事件监控。 58 */