“惊群”,看看nginx是怎么nginx解决跨域它的

Nginx如何解决“惊群”现象 - 推酷
Nginx如何解决“惊群”现象
首先解释下什么是“惊群”现象:如果多个工作进程同时拥有某个监听套接口,那么一旦该套接口出现某客户端请求,此时就将引发所有拥有该套接口的工作进程去争抢这个请求,能争抢到的肯定只有某一个工作进程,而其他工作进程注定要无功而返,这种现象即为
Nginx解决这种“惊群”现象使用的是负载均衡的策略,接下来先结合Nginx的源码详细介绍下Nginx的这种负载均衡策略。
首先是Nginx如何开启负载均衡策略:当然运行的Nginx要是多进程模型,并且工作进程数目大于1。这很好理解,只有在拥有多个工作进程争抢一个套接口时才会出现惊群现象,也才会需要负载均衡的策略。
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_
ngx_use_accept_mutex = 0;
其中变量ngx_use_accept_mutex就是用于标识是否打开负载均衡策略。这里的负载均衡策略又叫做“
前端负载均衡
”,因为它用于将客户端请求合理地分配给工作进程的方法;而“
后端负载均衡
”是用于为处理客户端请求,来合理选择一个后端服务器的策略。
接下来介绍负载均衡策略如何“合理的”将请求分配给工作进程,ngx_process_events_and_timers()有如下代码:
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled & 0) {
ngx_accept_disabled--;
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
if (timer == NGX_TIMER_INFINITE
|| timer & ngx_accept_mutex_delay)
timer = ngx_accept_mutex_
可以看到这段代码只有在开启负载均衡(即ngx_use_accept_mutex = 1)后才生效。在该逻辑内,首先通过检测变量ngx_accept_diabled值是够大于0来判断当前进程是否已经过载,为什么可以这样判断需要理解变量ngx_accept_diabled值的含义,这在accept()接受新连接请求的处理函数ngx_event_accept()内可以看到。
ngx_accept_disabled = ngx_cycle-&connection_n / 8
- ngx_cycle-&free_connection_n;
其中ngx_cycle-&connection_n表示一个工作进程中的最大可承受连接数,可以通过worker_connections指令配置,其默认值为512.另外一个变量ngx_cycle-&free_connection_n则表示当前的可用连接数,假设当前活动连接数为X,那么该值为ngx_cycle-&connection_n - X;故此ngx_accept_diabled的值为:
ngx_accept_diabled = X - ngx_cycle-&connection_n * 7 / 8;
也就是说如果当前活动连接数(X)超过最大可承受连接数的7/8,则表示发生过载,变量ngx_accept_diabled的值将大于0,并且该值越大表示过载越大,当前进程的负载越重。
回过去再看函数ngx_process_events_and_timers()内的代码,当进程处于过载状态时,所做的工作仅仅只是对变量ngx_accept_diabled自减1,这表示既然经过了一轮事件处理,那么负载肯定有所减小,所以也相应的调整变量ngx_accept_diabled的值。经过一段时间,ngx_accept_diabled又会降到0以下,便可争取锁获取新的请求连接。所以
可以看出最大可承受连接数的7/8是一个负载均衡点,当某工作进程的负载达到这个临界点时,它就不会去尝试获取互斥锁,从而让新增负载均衡到其他工作进程上
如果进程并没有处于过载状态,那么就会去争用锁,当然,实际上是争用监听套接口的监控权,争锁成功就会把
监听套接口
加入到自身的事件监控机制里(如果原本不在);争锁失败就会把监听套接口从自身的事件监控机制里删除(如果原本在)。
变量ngx_accept_mutex_held的值用于标识当前是否拥有锁,注意这一点很重要,因为如果当前拥有锁,则个flags变量打个NGX_POST_EVENTS标记,这表示所有发生的事件都将延后处理。
这是任何架构设计都必须遵守的一个约定,即持锁者必须尽量缩短自身持锁的时间
。所以把大部分事件延迟到释放锁之后再去处理,把锁尽快释放,缩短自身持锁的时间让其他进程尽可能的有机会获取锁。如果当前进程没有拥有锁,那么就把事件监控机制阻塞点(比如epoll_wait())的超时时间限制在一个比较短的范围内,超时越快,那么也就更频繁地从阻塞中跳出来,也就有更多的机会去争抢到互斥锁。
前面说到当事件到来时,不会立即处理,而是会设置一个延迟处理的标识(NGX_POST_EVENTS)。延迟处理的原理就是当一个事件发生时,会将事件以链表的形式缓存起来。
讲了这么多好像和解决惊群现在没有关系一样,其实上面所讲的内容大体上已经解释了Nginx如何避免惊群现象的原理了。总结下面两点:
第一,如果在处理新建连接事件的过程中,在监听套接字接口上又来了新的请求会怎么样?这没有关系,当前进程只处理已缓存的事件,新的请求将被阻塞在监听套接字接口上,并且监听套接字接口是以水平方式加入到事件监控机制里的,所以等到下一轮被哪个进程争取到锁并加在事件监控机制里时才会触发而被抓取出来。
第二,在进程处理事件时,只是把锁释放了,而没有将监听套接字接口从事件监控机制里删除,所以当处理缓存事件过程中,互斥锁可能会被另外一个进程争抢到并把所有监听套接字接口加入到它的事件监控机制里。因此严格来说,在同一时刻,监听套接字可能被多个进程拥有,但是,在同一时刻,监听套接口只可能被一个进程监控,因此在进程处理完缓存事件之后去争抢锁,发现锁被其他进程占用而争抢失败,会把所有监听套接口从自身的事件监控机制删除,然后才进行事件监控。
在同一时刻,监听套接口只可能被一个进程监控,这也就意味着Nginx根本不会受到惊群的影响。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致博客访问: 98160
博文数量: 19
博客积分: 413
博客等级: 一等列兵
技术积分: 294
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: 系统运维
在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很低下,许多进程被内核重新调度唤醒,同时去响应这一个事件,当然只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠(也有其他选择)。这种性能浪费现象就是惊群。惊群通常发生在server 上,当父进程绑定一个端口监听socket,然后fork出多个子进程,子进程们开始循环处理(比如accept)这个socket。每当用户发起一个TCP连接时,多个子进程同时被唤醒,然后其中一个子进程accept新连接成功,余者皆失败,重新休眠。那么,我们不能只用一个进程去accept新连接么?然后通过消息队列等同步方式使其他子进程处理这些新建的连接,这样惊群不就避免了?没错,惊群是避免了,但是效率低下,因为这个进程只能用来accept连接。对多核机器来说,仅有一个进程去accept,这也是程序员在自己创造accept瓶颈。所以,我仍然坚持需要多进程处理accept事件。其实,在linux2.6内核上,accept系统调用已经不存在惊群了(至少我在2.6.18内核版本上已经不存在)。大家可以写个简单的程序试下,在父进程中bind,listen,然后fork出子进程,所有的子进程都accept这个监听句柄。这样,当新连接过来时,大家会发现,仅有一个子进程返回新建的连接,其他子进程继续休眠在accept调用上,没有被唤醒。但是很不幸,通常我们的程序没那么简单,不会愿意阻塞在accept调用上,我们还有许多其他网络读写事件要处理,linux下我们爱用epoll解决非阻塞socket。所以,即使accept调用没有惊群了,我们也还得处理惊群这事,因为epoll有这问题。上面说的测试程序,如果我们在子进程内不是阻塞调用accept,而是用epoll_wait,就会发现,新连接过来时,多个子进程都会在epoll_wait后被唤醒!nginx就是这样,master进程监听端口号(例如80),所有的nginx worker进程开始用epoll_wait来处理新事件(linux下),如果不加任何保护,一个新连接来临时,会有多个worker进程在epoll_wait后被唤醒,然后发现自己accept失败。现在,我们可以看看nginx是怎么处理这个惊群问题了。nginx的每个worker进程在函数ngx_process_events_and_timers中处理事件,(void) ngx_process_events(cycle, timer, flags);封装了不同的事件处理机制,在linux上默认就封装了epoll_wait调用。我们来看看ngx_process_events_and_timers为解决惊群做了什么:[cpp]&从上面的注释可以看到,无论有多少个nginx worker进程,同一时刻只能有一个worker进程在自己的epoll中加入监听的句柄。这个处理accept的nginx worker进程置flag为NGX_POST_EVENTS,这样它在接下来的ngx_process_events函数(在linux中就是ngx_epoll_process_events函数)中不会立刻处理事件,延后,先处理完所有的accept事件后,释放锁,然后再处理正常的读写socket事件。我们来看下ngx_epoll_process_events是怎么做的:[cpp]&看看ngx_use_accept_mutex在何种情况下会被打开:[cpp]&当nginx worker数量大于1时,也就是多个进程可能accept同一个监听的句柄,这时如果配置文件中accept_mutex开关打开了,就将ngx_use_accept_mutex置为1。再看看有些负载均衡作用的ngx_accept_disabled是怎么维护的,在ngx_event_accept函数中:[cpp]&表明,当已使用的连接数占到在nginx.conf里配置的worker_connections总数的7/8以上时,ngx_accept_disabled为正,这时本worker将ngx_accept_disabled减1,而且本次不再处理新连接。最后,我们看下ngx_trylock_accept_mutex函数是怎么玩的:[cpp]&OK,关于锁的细节是如何实现的,这篇限于篇幅就不说了,下篇帖子再来讲。现在大家清楚nginx是怎么处理惊群了吧?简单了说,就是同一时刻只允许一个nginx worker在自己的epoll中处理监听句柄。它的负载均衡也很简单,当达到最大connection的7/8时,本worker不会去试图拿accept锁,也不会去处理新连接,这样其他nginx worker进程就更有机会去处理监听句柄,建立新连接了。而且,由于timeout的设定,使得没有拿到锁的worker进程,去拿锁的频繁更高。
阅读(2389) | 评论(0) | 转发(1) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。nginx如何避免惊群现象 - CSDN博客
nginx如何避免惊群现象
nginx使用锁ngx_accept_mutex来避免惊群现象。锁是自己来实现的,这里锁的实现分为两种情况,一种是支持原子操作的情况,也就是由NGX_HAVE_ATOMIC_OPS这个宏来进行控制的,一种是不支持原子操作,这是是使用文件锁来实现。
用户空间进程间锁实现的原理,就是能弄一个让所有进程共享的东西,比如mmap的内存,比如文件,然后通过这个东西来控制进程的互斥。参考之前的一篇文档《锁的本质》,其实锁就是一种共享的资源而已。——共享一个变量,然后通过设置这个变量来控制进程的行为。
typedef struct {
#if (NGX_HAVE_ATOMIC_OPS)
ngx_atomic_t
//原子操作
//fd表示进程间共享的文件句柄
//name表示文件名
} ngx_shmtx_t;
上锁:ngx_trylock_accept_mutex-----------------&ngx_shmtx_trylock---------------&ngx_trylock_fd
删除锁:ngx_shmtx_unlock-------------------------&ngx_unlock_fd
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
/*ngx_shmtx_trylock通过fcntl对ngx_accept_mutex中fd成员变量上锁,锁定整个文件*/
if (ngx_shmtx_trylock(&ngx_accept_mutex)) {/*锁定成功*/
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle-&log, 0,
&accept mutex locked&);
/*ngx_accept_mutex_held 获得锁的标志,如果本来已经获得锁,则直接返回OK*/
if (ngx_accept_mutex_held
&& ngx_accept_events == 0
&& !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
return NGX_OK;
/*ngx_enable_accept_events将cycle-&listening中的端口号都加到epoll事件中。把进程对应的监听socket 放入到epoll中进行监听,这样只有该进程能监听到accept操作。*/
if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
ngx_shmtx_unlock(&ngx_accept_mutex);
return NGX_ERROR;
ngx_accept_events = 0;
ngx_accept_mutex_held = 1;/*获得锁的标识*/
return NGX_OK;
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle-&log, 0,
&accept mutex lock failed: %ui&, ngx_accept_mutex_held);
/*//如果我们前面已经获得了锁,然后这次获得锁失败,则说明当前的listen句柄已经被其他的进程锁监听,因此此时需要从epoll中移出调已经注册的listen句柄。这样就很好的控制了子进程的负载均衡 */
if (ngx_accept_mutex_held) {
/*ngx_disable_accept_events将cycle-&listening中的端口号从epoll事件中删除*/
if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
return NGX_ERROR;
ngx_accept_mutex_held = 0;
return NGX_OK;
ngx_shmtx_trylock(ngx_shmtx_t *mtx){
err = ngx_trylock_fd(mtx-&fd);
if (err == 0) {
ngx_trylock_fd(ngx_fd_t fd)
ngx_memzero(&fl, sizeof(struct flock));
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &fl) == -1) {
return ngx_
ngx_shmtx_unlock(ngx_shmtx_t *mtx)
err = ngx_unlock_fd(mtx-&fd);
if (err == 0) {
ngx_log_abort(err, ngx_unlock_fd_n & %s failed&, mtx-&name);
ngx_unlock_fd(ngx_fd_t fd)
ngx_memzero(&fl, sizeof(struct flock));
fl.l_type = F_UNLCK;
fl.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &fl) == -1) {
支持原子操作的锁
/* cl should be equal or bigger than cache line size */
// 创建共享内存,用于accept mutex,connection counter和ngx_temp_number
/* ngx_accept_mutex */
/* ngx_connection_counter */
/* ngx_temp_number */
通过代码可以看到这里将会有3个区域被所有进程共享,其中我们的锁将会用到的是第一个。下面这段代码是初始化对应的共享内存区域。然后保存对应的互斥体指针。 shm.size =
shm.name.len = sizeof(&nginx_shared_zone&);
shm.name.data = (u_char *) &nginx_shared_zone&;
shm.log = cycle-&
// 创建共享内存,共享内存的起始地址保存在shm.addr
if (ngx_shm_alloc(&shm) != NGX_OK) {
return NGX_ERROR;
// 获得共享内存的起始地址
shared = shm.
// accept互斥体取得共享内存的第一段cl大小内存
ngx_accept_mutex_ptr = (ngx_atomic_t *)
ngx_accept_mutex.spin = (ngx_uint_t) -1;
// 系统支持原子数据则使用原子数据实现accept mutex,否则使用文件上锁实现
if (ngx_shmtx_create(&ngx_accept_mutex, shared, cycle-&lock_file.data)
!= NGX_OK)
return NGX_ERROR;
ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name)
mtx-&lock =
if (mtx-&spin == (ngx_uint_t) -1) {
return NGX_OK;
mtx-&spin = 2048;
#if (NGX_HAVE_POSIX_SEM)
if (sem_init(&mtx-&sem, 1, 0) == -1) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle-&log, ngx_errno,
&sem_init() failed&);
mtx-&semaphore = 1;
我们先来看获得锁。 这里nginx分为两个函数,一个是trylock,它是非阻塞的,也就是说它会尝试的获得锁,如果没有获得的话,它会直接返回错误。 而第二个是lock,它也会尝试获得锁,而当没有获得他不会立即返回,而是开始进入循环然后不停的去获得锁,知道获得。不过nginx这里还有用到一个技巧,就是每次都会让当前的进程放到cpu的运行队列的最后一位,也就是自动放弃cpu。 先来看trylock 这个很简单,首先判断lock是否为0,为0的话表示可以获得锁,因此我们就调用ngx_atomic_cmp_set去获得锁,如果获得成功就会返回1,负责为0.
ngx_uint_t
ngx_shmtx_trylock(ngx_shmtx_t *mtx)
ngx_atomic_uint_
val = *mtx-&
return ((val & 0x) == 0
&& ngx_atomic_cmp_set(mtx-&lock, val, val | 0x));
ngx_atomic_cmp_set(mtx-&lock, 0, ngx_pid)
的意思就是如果lock的值是0的话,就把lock的值修改为当前的进程id,否则返回失败。
接下来来看lock的实现,lock最终会调用ngx_spinlock,因此下面我要主要来分析这个函数。#define ngx_shmtx_lock(mtx)
ngx_spinlock((mtx)-&lock, ngx_pid, 1024)
我们来看spinklock,必须支持原子指令,才会有这个函数,这里nginx采用宏来控制的. 这里和trylock的处理差不多,都是利用原子指令来实现的,只不过这里如果无法获得锁,则会继续等待。 我们来看代码的实现:void
ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
#if (NGX_HAVE_ATOMIC_OPS)
ngx_uint_t
for ( ;; ) {
//如果lock为0,则说明没有进程持有锁,因此设置lock为value(为当前进程id),然后返回。
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
//如果cpu个数大于1(也就是多核),则进入spin-wait loop阶段。
if (ngx_ncpu & 1) {
//开始进入循环。
for (n = 1; n & n &&= 1) {
//下面这段就是纯粹的spin-loop wait。
for (i = 0; i & i++) {
//这个函数其实就是执行&PAUSE&指令,接下来会解释这个指令。
ngx_cpu_pause();
//然后重新获取锁,如果获得则直接返回。
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
//这个函数调用的是sched_yield,它会强迫当前运行的进程放弃占有处理器。
ngx_sched_yield();
#if (NGX_THREADS)
#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
通过上面的代码可以看到spin lock实现的很简单,就是一个如果无法获得锁,就进入忙等的过程,不过这里nginx还多加了一个处理,就是如果忙等太长,就放弃cpu,直到下次任务再次占有cpu。 接下来来看下PAUSE指令,这条指令主要的功能就是告诉cpu,我现在是一个spin-wait loop,然后cpu就不会因为害怕循环退出时,内存的乱序而需要处理,所引起的效率损失问题。
##参考http://lonelyc./3799
& &/557899.html
本文已收录于以下专栏:
相关文章推荐
*什么是惊群现象?Nginx中用了什么方法来避免这种问题的发生?本篇就解决这两个问题。。。→_→*
首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是当一个缓存数据失效之后会导致同时有多个并发线程去向后端数据库发起请求去获取同...
[1].http://blog.chinaunix.net/uid--id-3075103.html
1.进程A在n端口上监听,即调用listen(listenfd,backlog);
2.之后A调用fork产生子进程B,此时B拷贝了A的listenfd,该描述符使用的是相同的“文件表项”(具体参考 ...
java类打包成.jar可执行文件
发现在windows中运行好好的java程序,一迁移到linux环境下就发现程序运行时,内存会不断增加,直到被系统killed.所以我排除出内存...
在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很...
在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很...
文章目录1、nginx 原子锁实现介绍
2、处理惊群实现原理
3、gcc 原子操作
4、nginx 原子锁自实现程序
1、nginx 原子锁实现介绍
Nginx中的锁是自己实现的,分为两种,一种...
      简单来说,多线程/多进程(linux下线程进程也没有多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很低下,许多进程...
转载 http://blog.csdn.net/russell_tao/article/details/7204260
在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多...
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)“惊群”,看看nginx是怎么解决它的
我的图书馆
“惊群”,看看nginx是怎么解决它的
转自:http://blog.csdn.net/russell_tao/article/details/7204260在说nginx前,先来看看什么是“惊群”?简单说来,多线程/多进程(linux下线程进程也没多大区别)等待同一个socket事件,当这个事件发生时,这些线程/进程被同时唤醒,就是惊群。可以想见,效率很低下,许多进程被内核重新调度唤醒,同时去响应这一个事件,当然只有一个进程能处理事件成功,其他的进程在处理该事件失败后重新休眠(也有其他选择)。这种性能浪费现象就是惊群。惊群通常发生在server 上,当父进程绑定一个端口监听socket,然后fork出多个子进程,子进程们开始循环处理(比如accept)这个socket。每当用户发起一个TCP连接时,多个子进程同时被唤醒,然后其中一个子进程accept新连接成功,余者皆失败,重新休眠。那么,我们不能只用一个进程去accept新连接么?然后通过消息队列等同步方式使其他子进程处理这些新建的连接,这样惊群不就避免了?没错,惊群是避免了,但是效率低下,因为这个进程只能用来accept连接。对多核机器来说,仅有一个进程去accept,这也是程序员在自己创造accept瓶颈。所以,我仍然坚持需要多进程处理accept事件。其实,在linux2.6内核上,accept系统调用已经不存在惊群了(至少我在2.6.18内核版本上已经不存在)。大家可以写个简单的程序试下,在父进程中bind,listen,然后fork出子进程,所有的子进程都accept这个监听句柄。这样,当新连接过来时,大家会发现,仅有一个子进程返回新建的连接,其他子进程继续休眠在accept调用上,没有被唤醒。但是很不幸,通常我们的程序没那么简单,不会愿意阻塞在accept调用上,我们还有许多其他网络读写事件要处理,linux下我们爱用epoll解决非阻塞socket。所以,即使accept调用没有惊群了,我们也还得处理惊群这事,因为epoll有这问题。上面说的测试程序,如果我们在子进程内不是阻塞调用accept,而是用epoll_wait,就会发现,新连接过来时,多个子进程都会在epoll_wait后被唤醒!nginx就是这样,master进程监听端口号(例如80),所有的nginx worker进程开始用epoll_wait来处理新事件(linux下),如果不加任何保护,一个新连接来临时,会有多个worker进程在epoll_wait后被唤醒,然后发现自己accept失败。现在,我们可以看看nginx是怎么处理这个惊群问题了。nginx的每个worker进程在函数ngx_process_events_and_timers中处理事件,(void) ngx_process_events(cycle, timer, flags);封装了不同的事件处理机制,在linux上默认就封装了epoll_wait调用。我们来看看ngx_process_events_and_timers为解决惊群做了什么:[cpp]&从上面的注释可以看到,无论有多少个nginx worker进程,同一时刻只能有一个worker进程在自己的epoll中加入监听的句柄。这个处理accept的nginx worker进程置flag为NGX_POST_EVENTS,这样它在接下来的ngx_process_events函数(在linux中就是ngx_epoll_process_events函数)中不会立刻处理事件,延后,先处理完所有的accept事件后,释放锁,然后再处理正常的读写socket事件。我们来看下ngx_epoll_process_events是怎么做的:[cpp]&看看ngx_use_accept_mutex在何种情况下会被打开:[cpp]&当nginx worker数量大于1时,也就是多个进程可能accept同一个监听的句柄,这时如果配置文件中accept_mutex开关打开了,就将ngx_use_accept_mutex置为1。再看看有些负载均衡作用的ngx_accept_disabled是怎么维护的,在ngx_event_accept函数中:[cpp]&表明,当已使用的连接数占到在nginx.conf里配置的worker_connections总数的7/8以上时,ngx_accept_disabled为正,这时本worker将ngx_accept_disabled减1,而且本次不再处理新连接。最后,我们看下ngx_trylock_accept_mutex函数是怎么玩的:[cpp]&OK,关于锁的细节是如何实现的,这篇限于篇幅就不说了,下篇帖子再来讲。现在大家清楚nginx是怎么处理惊群了吧?简单了说,就是同一时刻只允许一个nginx worker在自己的epoll中处理监听句柄。它的负载均衡也很简单,当达到最大connection的7/8时,本worker不会去试图拿accept锁,也不会去处理新连接,这样其他nginx worker进程就更有机会去处理监听句柄,建立新连接了。而且,由于timeout的设定,使得没有拿到锁的worker进程,去拿锁的频繁更高。
TA的最新馆藏
喜欢该文的人也喜欢}

我要回帖

更多关于 nginx 惊群 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信