Nginx中解决惊群问题和负载均衡问题

       Nginx的惊群问题:master进程开始监听Web端口,fork出多个子进程,这些子进程会开始同时监听同一个Web端口。假如在没有用户连入服务器,某一时刻恰好所有的worker子进程都休眠且等待新连接的系统调用;这时有一个用户向服务器发起了连接,内核在收到TCP的SYN包时,会激活所有的休眠worker子进程。最先开始执行accept的子进程可以成功建立新连接,而其他worker子进程都会accept失败,转而继续休眠。这些accept失败的子进程被内核唤醒是不必要的,这一时刻它们占用了本不需要的系统资源,引发了不必要的进程上下文切换,增加了系统开销。

 

       Nginx解决惊群问题的方式是:规定了同一时刻只能有唯一一个worker子进程监听Web端口。因此新连接事件只能唤醒唯一正在监听端口的worker子进程。

       要实现这个方案要打开accept_mutex锁,然后调用ngx_trylock_accept_mutex()函数尝试获取accept_mutex锁,如果加锁成功,才能够监听Web端口。

ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
	//使用进程间的同步锁,试图获取ngx_accept_mutex锁
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) 
	{

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "accept mutex locked");
		//如果获得ngx_accept_mutex锁,但ngx_accept_mutex_held为1,则
		//立刻返回。ngx_accept_mutex_held为1时表示当前进程已经获取到锁了。
        if (ngx_accept_mutex_held
            && ngx_accept_events == 0
            && !(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); //添加失败,则要释放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);

    //ngx_accept_mutex函数获取ngx_accept_mutex锁失败
    //ngx_accept_mutex为1表示当前进程还在获取到锁的状态,需要处理
    if (ngx_accept_mutex)
	{
		//将监听连接的读时间从事件驱动模块中移除
        if (ngx_disable_accept_events(cycle) == NGX_ERROR)
		{
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 0;
    }

    return NGX_OK;
}


        如果ngx_trylock_accept_mutex()函数没有获得锁,接下来调用事件驱动模块的process_events方法时只能处理已有连接上的事件; 如果获得了锁,调用process_events方法时既处理已有连接上的事件,也会处理新连接的事件。

       获得ngx_accept_mutex锁的进程会优先处理ngx_posted_accept_events队列中的事件,处理完后要立刻释放该锁,这样减少了锁的占用时间,其他进程可以获取该锁处理新的连接事件。

 

 

 

    Nginx的负载均衡问题的方式:

       利用一个负载均衡阈值ngx_accept_disabled来管理。这个阈值是进程允许的总连接数的1/8减去空闲连接数。就是当阈值为正整数时当前进程将不再处理新连接事件,取而代之的仅仅是阈值值减1。
       因为阈值的初始值为负值:N(总连接数)/8 - N(初始时,空闲值等于进程总允许连接数) = -(7/8)N。所以当Nginx各worker子进程间的负载均衡仅在某个worker子进程处理的连接数达到它最大处理总数的7/8时才会触发,这时该worker进程就会减少处理新连接的机会,其他空闲的worker进程就有机会去处理更多的新连接。

       要利用锁机制来做互斥与同步,既避免监听套接口被同时加入到多个进程的事件监控机制里,又避免监听套接口在某一时刻没有被任何一个进程监控。
        如果进程并没有处于过载状态,那么就会去争用锁,争锁成功就会把所有监听套接口加入到自身的事件监控机制里;争锁失败就会把监听套接口从自身的事件监控机制里删除。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值