【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队 ...
随机推荐
- C语言常用关键字及运算符操作---关键字
每个知识点4问: 1. 是什么? 2. 什么时间用? 3. 怎么用? 4.为什么这么用? 1. 32个关键字 //(1)sizeof 的用法 //sizeof 是关键字,让编译器帮我们查看内存空间存储 ...
- Bug的分类和管理流程
1.按照严重程度划分 定义:是指Bug对软件质量的破坏程度,即BUG的存在将对软件的功能和性能产生怎样的影响 分类:系统崩溃.严重.一般.次要.建议 2.按优先级划分 定义:表示处理和修正软件缺陷的现 ...
- k8s 核心功能[转]
部署应用 执行命令: kubectl run kubernetes-bootcamp \ --image=docker.io/jocatalin/kubernetes-bootcamp:v1 \ -- ...
- 2018 CCPC 女生赛 hdoj6287 口算训练
题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6287 Summarize: 1.分解质因数: 2.二分查找函数lower_bound与upper_bo ...
- docker的网络(基础)
Docker的网络子系统是可插拔的,使用驱动程序.默认情况下存在多个驱动程序,并提供核心网络功能: bridge:docker默认的网络驱动.如果未指定驱动程序,则这是需要创建的网络类型.当应用程序在 ...
- React深入 - 手写redux api
简介: 手写实现redux基础api createStore( )和store相关方法 api回顾: createStore(reducer, [preloadedState], enhancer) ...
- day16-python之函数式编程匿名函数
1.复习 #!/usr/bin/env python # -*- coding:utf-8 -*- name = 'alex' #name=‘lhf’ def change_name(): name= ...
- MD5散列算法的示例
在很多地方,都用到了数据加密,比较多的就是MD5了,也比较安全,下面就贴上个示例,输入一串字符串,通过MD5加密 加密算法如下 public static string MD5_Encrypt(str ...
- 『NYIST』第八届河南省ACM竞赛训练赛[正式赛一]-CodeForces 236A,虽然很水,但有一个很简单的函数用起来方便
A. Boy or Girl time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- Go 方法和接收者
package main import ( "fmt" ) //面向对象 //go仅支持封装,不支持继承和多态 //go语言中没有class,只要struct //不论地址还是结构 ...