高并发中的惊群问题

目录

1 惊群效应是什么?

2 惊群效应消耗了什么

3 惊群的几种情况

3.1 accept惊群(新版内核已解决) 

3.2 epoll_create 在 fork 之前创建

3.3 epoll_create 在 fork 之后创建

4 Linux与Nginx的解决方案

4.1 锁策略

4.2 SO_REUSEPORT

4.3 EPOLLEXCLUSIVE标志位


1 惊群效应是什么?

       惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。

惊群效应消耗了什么

  • Linux 内核对用户进程(线程)频繁地做无效的调度、上下文切换等使系统性能大打折扣。上下文切换(context switch)过高会导致 CPU 像个搬运工,频繁地在寄存器和运行队列之间奔波,更多的时间花在了进程(线程)切换,而不是在真正工作的进程(线程)上面。直接的消耗包括 CPU 寄存器要保存和加载(例如程序计数器)、系统调度器的代码需要执行。间接的消耗在于多核 cache 之间的共享数据。
  • 为了确保只有一个进程(线程)得到资源,需要对资源操作进行加锁保护,加大了系统的开销。目前一些常见的服务器软件有的是通过锁机制解决的,比如 Nginx(它的锁机制是默认开启的,可以关闭);还有些认为惊群对系统性能影响不大,没有去处理,比如 Lighttpd。

惊群的几种情况

3.1 accept惊群(新版内核已解决) 

        以多进程为例,在主进程创建监听描述符listenfd后,fork()多个子进程,多个进程共享listenfd,accept是在每个子进程中,当一个新连接来的时候,会发生惊群。

  • Linux 2.6 版本之前,监听同一个 socket 的进程会挂在同一个等待队列上,当请求到来时,会唤醒所有等待的进程。
  • Linux 2.6 版本之后,通过引入一个标记位 WQ_FLAG_EXCLUSIVE,解决掉了 accept 惊群效应。

       具体实现: 在Linux 2.6版本中,维护了一个等待队列,队列中的元素就是进程,非exclusive属性的元素会加在等待队列的前面,而exclusive属性的元素会加在等待队列的末尾,当子进程调用阻塞accept时,该进程会被打上WQ_FLAG_EXCLUSIVE标志位,从而成为exclusive属性的元素被加到等待队列中。当有TCP连接请求到达时,该等待队列会被遍历,非exclusive属性的进程会被不断地唤醒,直到出现第一个exclusive属性的进程,该进程会被唤醒,同时遍历结束。

3.2 epoll_create 在 fork 之前创建

      与 accept 惊群的原因类似,当有事件发生时,等待同一个文件描述符的所有进程(线程)都将被唤醒,而且解决思路和 accept 一致。为什么需要全部唤醒?因为内核不知道,你是否在等待文件描述符来调用 accept() 函数,还是做其他事情(信号处理,定时事件)。

       此种情况惊群效应已经被解决。、

3.3 epoll_create 在 fork 之后创建

  (1) 主进程创建listendfd

  (2) 主进程创建多个子进程

  (3)每个子进程创建自已的epollfd

  (4)每个子进程把listenfd加入到epollfd中

  (5) 当一个连接进来时,会触发epoll惊群,多个子进程epoll同时会触发

分析:

       因为每个子进程的epoll是不同的epoll, 虽然listenfd是同一个,但新连接过来时, accept会触发惊群,但内核不知道该发给哪个监听进程,因为不是同一个epoll。所以这种惊群内核并没有处理。惊群还是会出现。

       Nginx就是使用这种方式。


4 Linux与Nginx的解决方案

4.1 锁策略

基本思路是,子进程在进行epoll_ctl加入监听事件以及epoll_wait前,需要获得进程间的全局锁,获得锁的子进程才有资格通过监听获取连接请求并创建TCP连接,锁策略确保了只有1个子进程在处理TCP连接请求。

锁策略的伪代码如下:

semop(...); // lock
epoll_wait(...);
accept(...);
semop(...); // unlock
... // manage the request

Nginx在1.11.3版本(2016-07-26)之前通过配置accept_mutex默认为on支持该解决方案。

4.2 SO_REUSEPORT

       在Linux 3.9版本引入了socket套接字选项SO_REUSEPORT,Linux 3.9版本之前,一个进程通过bind一个三元组({<protocol>, <src_addr>, <src_port>})组合之后,其他进程不能再bind同样的三元组,Linux 3.9版本之后,凡是传入选项SO_REUSEPORT且为同一个用户下(安全考虑)的socket套接字都可以bind和监听同样的三元组。内核对这些监听相同三元组的socket套接字实行负载均衡,将TCP连接请求均匀地分配给这些socket套接字。

         这里的负载均衡基本原理为:当有TCP连接请求到来时,用数据包的({<src_addr>, <src_port>})作为一个hash函数的输入,将hash后的结果对SO_REUSEPORT套接字的数量取模,得到一个索引,该索引指示的数组位置对应的套接字便是要处理连接请求的套接字。

        Nginx在1.9.1版本(2015-05-26)时支持了reuseport特征,在Nginx配置中的listen指令的端口号之后增加reuseport参数后,Nginx的各个worker进程就有自己各自的监听套接字,这些监听套接字监听相同的源地址和端口号组合。

使用方式如下:

http {
     server {
          listen 80 reuseport;
          server_name  localhost;
          # ...
     }
}

stream {
     server {
          listen 12345 reuseport;
          # ...
     }
}

        由于reuseport特征负载均衡在内核中的实现原理是按照套接字数量的hash,所以当Nginx进行reload,从reuseport升级为非reuseport,或者从多worker进程升级为少worker进程,都会有大幅度的性能下降。

4.3 EPOLLEXCLUSIVE标志位

        在Linux 4.5版本引入EPOLLEXCLUSIVE标志位(Linux 4.5, glibc 2.24),子进程通过调用epoll_ctl将监听套接字与监听事件加入epfd时,会同时将EPOLLEXCLUSIVE标志位显式传入,这使得子进程带上了exclusive属性,也就是互斥属性,跟Linux 2.6版本解决accept惊群效应的解决方案类似,不同的地方在于,当有监听事件发生时,唤醒的可能不止一个进程(见如下对EPOLLEXCLUSIVE标志位的官方文档说明中的“one or more”),这一定程度上缓解了惊群效应。

# http://man7.org/linux/man-pages/man2/epoll_ctl.2.html
EPOLLEXCLUSIVE (since Linux 4.5)
              Sets an exclusive wakeup mode for the epoll file descriptor
              that is being attached to the target file descriptor, fd.
              When a wakeup event occurs and multiple epoll file descriptors
              are attached to the same target file using EPOLLEXCLUSIVE, one
              or more of the epoll file descriptors will receive an event
              with epoll_wait(2).  The default in this scenario (when
              EPOLLEXCLUSIVE is not set) is for all epoll file descriptors
              to receive an event.  EPOLLEXCLUSIVE is thus useful for avoid‐
              ing thundering herd problems in certain scenarios.

Nginx在1.11.3版本时采用了该解决方案,所以从该版本开始,配置accept_mutex默认为off。

参考:

1、[框架]高并发中的惊群效应

2、Liunx与Nginx中的惊群效应

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值