epoll 惊群

起因:

最近,一直在学习nginx。对于事件模块所解决的”惊群“现象完全不明白,遂产生了此文。


套接字:

可以将服务器端的套接字分为 监听套接字 与 连接套接字。 监听套接字负责等待用户连接,而通过accept,获得一个新的套接字,

该套接字为连接套接字,通过连接套接字与用户传送数据。


何谓惊群:

nginx 分为 master 进程 与 worker 进程,worker 进程其实就是一些子进程。当每个子进程都持有监听套接字时,假如某一时刻,

没有任何连接请求,所有子进程都处于休眠状态等待用户连接。突然,来了一个请求,此时,操作系统不知道该唤醒哪一个进程

来处理这个请求,索性全部唤醒。然后,只有一个进程获得处理权,取得连接套接字,其它进程都将失败,这导致资源的浪费。


现象重现:

测试版本

Linux controller 3.2.0-20-generic #33-Ubuntu SMP Tue Mar 27 16:42:26 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

测试结果


开启了8个工作进程,当到来一个请求时,惊醒了4个进程,最终,只有一个进程获得连接。


代码:

这里:http://download.csdn.net/detail/spch2008/6850183


centos: centos 环境下不是很明显。

[root@localhost alarmall]# uname -a
Linux localhost.localdomain 2.6.32-358.6.2.el6.x86_64 #1 SMP Thu May 16 20:59:36 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux


 可能需要连续运行几次client程序。    


总结-》并不是将所有工作进程全部唤醒,而只是唤醒了一部分。   

2016-02-01 更正:

进程没有全部唤醒,因为某个进程已经处理完这个accept,内核队列上已经没有这个事件,无需唤醒其他进程。(多谢 开心小熊

修改代码,进程不要accept,这样,全部进程唤醒。

memset(&event, 0, sizeof(struct epoll_event));
event.events |= EPOLLIN;
event.events |= EPOLLET; // 边缘
event.data.fd = sfd;

/* add listening sfd */
res = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
if (res == -1) {
    perror("ERROR : epoll_ctl EPOLL_CTL_ADD\n");
    return -1;
}

for ( ; ; ) {
    nfds = epoll_wait(efd, events, MAX_EVENTS, -1);

    for ( i = 0; i < nfds; i++) {
        if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)) {
            close(events[i].data.fd);
            printf("socket error, fd = %d\n", events[i].data.fd);
            continue;

        } else if (events[i].data.fd == sfd) {
            printf("process wake up, pid = %d\n", getpid());

            continue;  //直接跳出
            while (1) {
                addrlen = sizeof(struct sockaddr);


nginx解决办法:

nginx每次只将监听套接字放于一个进程中,这就避免了”惊群“现象,同一时刻,只有一个进程处于等待用户连接的状态,

其它进程都处理已经建立的连接。监听套接字被几个工作进程轮流持有,采用负载均衡,当一个进程过载后,放弃监听

套接字,由其它进程持有,而放弃的进程只能处理已经建立的连接。这样,就很好的解决了”惊群“现象。

ngx_int_t
ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
{
    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
            ngx_shmtx_unlock(&ngx_accept_mutex);
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 1;
        return NGX_OK;
    }

    if (ngx_accept_mutex_held) {
        if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
            return NGX_ERROR;
        }

        ngx_accept_mutex_held = 0;
    }
    return NGX_OK;
}

首先获得互斥锁,即只有一个进程可以得到监听套接字,当获取成功后,进入ngx_enable_accept_events,将监听

套接字加入epoll(采用epoll)中。没有获得锁,而上一次获得过,还没有释放,则进行释放ngx_disable_accept_events。

void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{

    if (ngx_accept_disabled > 0) {
        ngx_accept_disabled--;

    } else {
        if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
            return;
        }   
    }

}
只有在ngx_accept_disabled 小于0 的时候,才去获取互斥锁,也就是说,当进程处理连接的数量达到上限,不在去尝试

获取监听套接字,而是处理已经获取的连接。

ngx_event.c->ngx_event_core_module->ngx_event_process_init->ngx_event_accept:

ngx_accept_disabled = ngx_cycle->connection_n / 8
                      - ngx_cycle->free_connection_n;
阈值为连接总数的7/8.


参考:

惊群:http://blog.163.com/pandalove@126/blog/static/9800324520122633515612/

套接字:http://www.cnblogs.com/newlist/archive/2012/02/19/2358469.html

nginx:《深入理解nginx模块开发与架构解析》




  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值