Nginx解决惊群现象
来源:互联网 发布:伊美服饰淘宝网 编辑:程序博客网 时间:2024/05/14 22:42
惊群现象:所有的工作进程都在等待一个socket,当socket客户端连接时,所有工作线程都被唤醒,但最终有且仅有一个工作线程去处理该连接,其他进程又要进入睡眠状态。
Nginx通过控制争抢处理socket的进程数量和抢占ngx_accept_mutex锁解决惊群现象。只有一个ngx_accept_mutex锁,谁拿到锁,谁处理该socket的请求。
如果当前进程的连接数>最大连接数*7/8,则该进程不参与本轮竞争。
//nginx的每个worker进程在函数ngx_process_events_and_timers中处理事件。下面代码是ngx_process_events_and_timers()函数的核心部分。void ngx_process_events_and_timers(ngx_cycle_t *cycle) { //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进程非常繁忙,将不再去处理新连接,这也是个简单的负载均衡 if (ngx_accept_disabled > 0) { ngx_accept_disabled--; } else { //工作进程抢占锁,抢占成功的进程将ngx_accept_mutex_held变量置为1。拿到锁,意味着socket被放到本进程的epoll中了,如果没有拿到锁,则socket会被从epoll中取出。 //此处trylock是非阻塞锁,如果没有抢占到锁,进程会立刻返回,处理自己监听的描述符上的读写事件。 if(pthread_mutex_trylock(&ngx_accept_mutex)) { ngx_accept_mutex_held = 1; } else { //设置time时间,500ms后就去争抢锁,使得没有拿到锁的worker进程,去拿锁的频繁更高,确保每个进程可以处理几乎相同数量的fd的读写。 timer = 500; ngx_accept_mutex_held = 0; } //拿到锁的话,置flag为NGX_POST_EVENTS,这意味着ngx_process_events函数中,任何事件都将延后处理,会把accept事件都放到ngx_posted_accept_events链表中,epollin|epollout事件都放到ngx_posted_events链表中 if (ngx_accept_mutex_held) { flags |= NGX_POST_EVENTS; } } //继续epoll_wait等待处理事件 int num = epoll_wait(epollfd, events, length, timer); for(int i=0; i<num; ++i) { ...... //如果是读事件 if (revents & EPOLLIN) { //有NGX_POST_EVENTS标志的话,就把accept事件放到ngx_posted_accept_events队列中,把正常的事件放到ngx_posted_events队列中延迟处理 //新连接事件队列ngx_posted_accept_events //用户读写事件队列ngx_posted_events if (flags & NGX_POST_EVENTS) { queue = rev->accept ? &ngx_posted_accept_events: &ngx_posted_events; ngx_post_event(rev, queue); } else//处理 { rev->handler(rev); } } //如果是写事件 if (revents & EPOLLOUT) { //同理,有NGX_POST_EVENTS标志的话,写事件延迟处理,放到ngx_posted_events队列中 if (flags & NGX_POST_EVENTS) { ngx_post_event(rev, &ngx_posted_events); } else//处理 { rev->handler(rev); } } } //先处理新用户的连接事件 ngx_event_process_posted(cycle, &ngx_posted_accept_events); //释放处理新连接的锁 if(ngx_accept_mutex_held) { pthread_mutex_unlock(&ngx_accept_mutex); } //再处理已建立连接的用户读写事件 ngx_event_process_posted(cycle, &ngx_posted_events);}
nginx从抢锁、释放锁到处理事件的整个过程,我已经结合代码做了注释,相信大家对整个过程应该已经不陌生了。至于pthread_mutex_trylock()中进程是如何抢占锁的,这就有赖于实现抢占的算法了,此处只是解释处理过程,并不关心抢占实现原理。感兴趣的同学可以自己搜索相关资料。
1.先处理新用户的连接事件,再释放处理新连接的锁,为什这么设计?
如果刚释放锁,就有新连接,刚获得锁的进程要给等待队列中添加sockfd时,此时原获得锁的进程也要从等待队列中删除sockfd,TCP的三次握手的连接是非线程安全的。为了避免产生错误,使得将sockfd从等待队列中删除后,再让新的进程抢占锁,处理新连接。
2.拿到锁,将任务放在任务队列中,不是立刻去处理,为什这么设计?
每个进程要处理新连接事件,必须拿到锁,当前进程将新连接事件的sokect添加到任务队列中,立即释放锁,让其他进程尽快获得锁,处理用户的连接。
你可能有个疑问,如果没有加锁,有新事件连接时,所有的进程都会被唤醒执行accept,有且仅有一个进程会accept返回成功,其他进程都重新进入睡眠状态。现在有了锁,在发生accept之前,进程们要去抢占锁,也是有且仅有一个进程会抢到锁,其他进程也是重新进入睡眠状态。即:不论是否有accept锁,都会有很多进程被唤醒再重新进入睡眠状态的过程,那惊群现象如何解释?
其实,锁不能解决惊群现象,惊群现象是没办法解决的,很多进程被同时唤醒是一个必然的过程。Nginx中通过检查当前进程的连接数是否>最大连接数*7/8来判断当前进程是否能处理新连接,减少被唤醒的进程数量,也实现了简单的负载均衡。锁只能保证不让所有的进程去调用accept函数,解决了很多进程调用accept返回错误,锁解决的是惊群现象的错误,并不是解决了惊群现象!
- Nginx解决惊群现象
- nginx如何解决惊群现象
- nginx 惊群现象
- nginx惊群现象
- Nginx如何解决“惊群”现象
- Nginx如何解决“惊群”现象
- nginx如何避免惊群现象
- Nginx惊群现象的处理
- Nginx中的惊群现象解决方法
- Linux 惊群现象
- 惊群现象
- 惊群现象
- Linux 惊群现象
- 惊群现象
- epoll 群惊现象
- 惊群现象
- 惊群现象
- 惊群现象
- 并发编程--线程池拒绝策略RejectedExecutionHandler(三)
- 个人编程心得
- J
- VS2013+opecv 2.4.9 配置
- Android OTA升级原理和流程分析(一)
- Nginx解决惊群现象
- 常见布局&控件——下拉按钮
- opencv输出中文字符,并控制字符大小
- request请求https时禁用安全请求警告
- java运算符
- Springboot 事务管理控制
- 文章标题
- Jersey全局异常处理器
- JQuery语法练习