【Nginx】惊群问题
转自:江南烟雨
惊群问题的产生
ngx_thread_volatile ngx_event_t *ngx_posted_accept_events;
ngx_thread_volatile ngx_event_t *ngx_posted_events;
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
ngx_uint_t flags;
ngx_msec_t timer, delta; if (ngx_timer_resolution) {
timer = NGX_TIMER_INFINITE;
flags = ; } else {
timer = ngx_event_find_timer();
flags = NGX_UPDATE_TIME; #if (NGX_THREADS) if (timer == NGX_TIMER_INFINITE || timer > ) {
timer = ;
} #endif
} /*ngx_use_accept_mutex表示是否需要通过对accept加锁来解决惊群问题。当使用了master模式,nginx worker进程数>1时且配置文件中打开accept_mutex时,这个标志置为1
它在函数ngx_event_process_int中被设置,源代码为:
if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
ngx_use_accept_mutex = 1;
ngx_accept_mutex_held = 0;
ngx_accept_mutex_delay = ecf->accept_mutex_delay; } else {
ngx_use_accept_mutex = 0;
}*/
if (ngx_use_accept_mutex) {
//负载均衡处理
if (ngx_accept_disabled > ) {
ngx_accept_disabled--; } else {
//调用ngx_trylock_accept_mutex方法,尝试获取accept锁
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
} //拿到锁
if (ngx_accept_mutex_held) {
/*给flags增加标记NGX_POST_EVENTS,这个标记作为处理时间核心函数ngx_process_events的一个参数,这个函数中所有事件将延后处理。会把accept事件都放到ngx_posted_accept_events链表中,epollin|epollout普通事件都放到ngx_posted_events链表中 */
flags |= NGX_POST_EVENTS;
} else {
/*获取锁失败,意味着既不能让当前worker进程频繁的试图抢锁,也不能让它经过太长事件再去抢锁
下面的代码:即使开启了timer_resolution时间精度,牙需要让ngx_process_change方法在没有新事件的时候至少等待ngx_accept_mutex_delay毫秒之后再去试图抢锁
而没有开启时间精度时,如果最近一个定时器事件的超时时间距离现在超过了ngx_accept_mutex_delay毫秒,也要把timer设置为ngx_accept_mutex_delay毫秒,这是因为当前进程虽然没有抢到accept_mutex锁,但也不能让ngx_process_change方法在没有新事件的时候等待的时间超过ngx_accept_mutex_delay,这会影响整个负载均衡机制*/
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay)
{
timer = ngx_accept_mutex_delay;
}
}
}
} //计算ngx_process_events消耗的时间
delta = ngx_current_msec; //事件处理核心函数
(void) ngx_process_events(cycle, timer, flags); delta = ngx_current_msec - delta; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"timer delta: %M", delta); //ngx_posted_accept_events链表有数据,开始accept新连接
if (ngx_posted_accept_events) {
ngx_event_process_posted(cycle, &ngx_posted_accept_events);
} //释放锁后再处理ngx_posted_events链表中的普通事件
if (ngx_accept_mutex_held) {
ngx_shmtx_unlock(&ngx_accept_mutex);
} //如果ngx_process_events消耗的时间大于0,那么这是可能有新的定时器事件触发
if (delta) {
//处理定时器事件
ngx_event_expire_timers();
} ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"posted events %p", ngx_posted_events); //ngx_posted_events链表中有数据,进行处理
if (ngx_posted_events) {
if (ngx_threaded) {
ngx_wakeup_worker_thread(cycle); } else {
ngx_event_process_posted(cycle, &ngx_posted_events);
}
}
}
上面代码中要进行说明的是,flags被设置后作为函数ngx_process_events方法的一个参数,在epoll模块中这个接口的实现方法是ngx_epoll_process_events(其具体代码见http://blog.csdn.net/xiajun07061225/article/details/9250341)。当falgs标志位含有nGX_POST_EVENTS时是不会立即调用事件的handler回调方法的,代码如下所示:
//事件需要延后处理
if (flags & NGX_POST_EVENTS) {
/*如果要在post队列中延后处理该事件,首先要判断它是新连接时间还是普通事件
以确定是把它加入到ngx_posted_accept_events队列或者ngx_posted_events队列中。*/
queue = (ngx_event_t **) (rev->accept ?
&ngx_posted_accept_events : &ngx_posted_events);
//将该事件添加到相应的延后队列中
ngx_locked_post_event(rev, queue); } else {
//立即调用事件回调方法来处理这个事件
rev->handler(rev);
}
ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
//尝试获取accept_mutex锁。注意是非阻塞的。返回1表示成功,返回0表示失败。
//ngx_accept_mutex 定义:ngx_shmtx_t ngx_accept_mutex;(ngx_shmtx_t是Nginx封装的互斥锁,用于经常间同步)
if (ngx_shmtx_trylock(&ngx_accept_mutex)) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"accept mutex locked"); //获取到锁,但是标志位ngx_accept_mutex_held为1,表示当前进程已经获取到锁了,立即返回。
if (ngx_accept_mutex_held
&& ngx_accept_events ==
&& !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
{
return NGX_OK;
} //将所有监听事件添加到当前的epoll等事件驱动模块中
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
//添加失败,必须释放互斥锁
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
}
//标志位设置
ngx_accept_events = ;
//当前进程已经获取到锁
ngx_accept_mutex_held = ; return NGX_OK;
} ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, ,
"accept mutex lock failed: %ui", ngx_accept_mutex_held); //获取锁失败,但是标志位ngx_accept_mutex_held仍然为1,即当前进程还处在获取到锁的状态,这是不正确的
if (ngx_accept_mutex_held) {
//将所有监听事件从事件驱动模块中移除
if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
return NGX_ERROR;
}
//没有获取到锁,设置标志位
ngx_accept_mutex_held = ;
} return NGX_OK;
}
调用这个方法的结果是,要么唯一获取到锁且其epoll等事件驱动模块开始监控web端口上的新连接事件。这种情况下调用process_events方 法时就会既处理已有连接上的事件,也处理新连接的事件。要么没有获取到锁,当前进程不会收到新连接事件。这种情况下process_events只处理已 有连接上的事件。
【Nginx】惊群问题的更多相关文章
- Nginx惊群问题
Nginx惊群问题 "惊群"概念 所谓惊群,可以用一个简单的比喻来说明: 一群等待食物的鸽子,当饲养员扔下一粒谷物时,所有鸽子都会去争抢,但只有少数的鸽子能够抢到食物, 大部分鸽子 ...
- Nginx惊群处理
惊群:是指在多线程/多进程中,当有一个客户端发生链接请求时,多线程/多进程都被唤醒,然后只仅仅有一个进程/线程处理成功,其他进程/线程还是回到睡眠状态,这种现象就是惊群. 惊群是经常发生现在serve ...
- NGINX怎样处理惊群的
写在前面 写NGINX系列的随笔,一来总结学到的东西,二来记录下疑惑的地方,在接下来的学习过程中去解决疑惑. 也希望同样对NGINX感兴趣的朋友能够解答我的疑惑,或者共同探讨研究. 整个NGINX系列 ...
- “惊群”,看看nginx是怎么解决它的
在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群.可以想见,效率很 ...
- Nginx学习之一-惊群现象
惊群问题(thundering herd)的产生 在建立连接的时候,Nginx处于充分发挥多核CPU架构性能的考虑,使用了多个worker子进程监听相同端口的设计,这样多个子进程在accept建立新连 ...
- 【转载】“惊群”,看看nginx是怎么解决它的
原文:http://blog.csdn.net/russell_tao/article/details/7204260 在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下 ...
- Nginx中的惊群现象解决方法
*什么是惊群现象?Nginx中用了什么方法来避免这种问题的发生?本篇就解决这两个问题...→_→* 惊群现象的定义与危害 在Nginx中,每一个worker进程都是由master进程fork出来的.m ...
- Nginx模型 & 惊群问题
这篇写的不错 http://www.cnblogs.com/linguoguo/p/5511293.html Nginx为啥性能高-多进程异步IO模型 1. 对于每个worker进程来说,独立的进程, ...
- nginx&http 第三章 惊群
惊群:概念就不解释了. 直接说正题:惊群问题一般出现在那些web服务器上,Linux系统有个经典的accept惊群问题,这个问题现在已经在内核曾经得以解决,具体来讲就是当有新的连接进入到accept队 ...
随机推荐
- dropdb - 删除一个现有 PostgreSQL 数据库
SYNOPSIS dropdb [ option...] dbname DESCRIPTION 描述 dropdb 删除一个现有 PostgreSQL 数据库. 执行这条命令的人必须是数据库超级用户, ...
- vue 组件名和方法名 重名了,报function错误
vue 组件名和方法名 重名了,报function错误
- django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module
pip3 install mysqlclient try again python manage.py makemigrations python manage.py migrate
- 从零开始--系统深入学习Android
http://www.cnblogs.com/tianjian/category/354587.html
- java缓存的使用
缓存1,缓存的定义与作用2,缓存的使用范围(命中率高.高访问量)3,缓存策略(命中率,最大元素,清空策略);4,缓存介质(内存缓存,硬盘缓存,数据库缓存)(本地缓存(ehcache,oscache)与 ...
- BUG笔记 1.0
似乎只要coding,这些代码就要跟我过不去似的 今天在linux上安装了mysql-server,想不到竟然被一个及其简单的问题给难住了. 是的,我竟然无法登陆!!! 在论坛,百度,google上苦 ...
- 6. 将单独表空间(File-Per-Table Tablespaces)复制到另一个实例
6. 将单独表空间复制到另一个实例 本节介绍如何将单独表空间从一个MySQL实例复制 到另一个MySQL实例,也称为可传输表空间功能. 将InnoDB单独表空间复制到其他实例的原因有很多: - 在不对 ...
- redis:哨兵集群配置
最少配置1主2从3哨兵 一.引言 上一篇文章我们详细的讲解了Redis的主从集群模式,其实这个集群模式配置很简单,只需要在Slave的节点上进行配置,Master主节点的配置不需要做任何更改,但是有一 ...
- POJ 3258 River Hopscotch (二分法)
Description Every year the cows hold an event featuring a peculiar version of hopscotch that involve ...
- 大数据学习——linux常用命令(二)
二.目录操作 1 查看目录信息 ls / 查看根目录下的文件信息 ls . 或者 ls ./查看当前目录下的文件信息 ls ../查看根目录下 ls /home/hadoop ls -l . 查看当前 ...