nginx惊群问题解决原理和负载均衡实现

nginx惊群问题的解决原理和负载均衡实现

nginx会将监听连接的读事件设为ngx_event_accept。如果有新连接事件,则会调用该方法来建立新连接。
nginx惊群问题解决原理和负载均衡实现_第1张图片

​ Nginx处于充分发挥多喝CPU架构性能的考虑,使用了多个worker子进程监听相同端口的设计。这样多个子进程在accept建立时就会争抢——惊群。子进程数量越多问题越明显。

​ 为了解决上述问题,Nginx设计了两个post队列。一个是由被触发的监听连接的读事件构成的ngx_posted_accept_events,另一个由普通读写事件构成的ngx_posted_events队列。两个队列用处如下:

  • 将epoll_wait产生的一批事件分到这两个队列中,让存放新连接的事件ngx_posted_accept_events优先执行,存放普通事件的ngx_posted_events队列最后执行。这是解决惊群和负载均衡问题的关键。
  • 如果在处理一个事件的过程中产生了另一个事件,而我们希望这个事件随后执行(不立刻执行)。那么可以把它放到Post队列中。
如何解决惊群问题

nginx惊群问题的本质是多个worker在监听同一个端口的时候,某一时刻恰好所有worker都休眠且等待新连接的系统调用,这时有一个用户向服务器发起了连接,会激活所有休眠的worker,当然只有一个worker可以成功accept。这些accept失败的子进程被内核唤醒是 不必要的,增加了没必要的系统开销。

​ nginx在应用层面上解决了该问题。其实就是加了个锁。

nginx惊群问题解决原理和负载均衡实现_第2张图片

​ 在上面关于ngx_accept_mutex实际上是nginx进程间的同步锁。只有获取到了锁的进程才能处理新连接事件。监听web端口。如果ngx_trylock_accept_mutex方法没获取到锁,接下来的调用事件模块的Process_events方法时只处理已有连接上的事件。如果获取到了锁,则即处理新连接事件和已有连接事件。

​ 那么问题来了:什么时候释放ngx_accept_mutex锁
nginx惊群问题解决原理和负载均衡实现_第3张图片

看上述代码,如果worker持有锁了,则会打上NGX_POST_EVENTS标签。在ngx_epoll_process_evevnts的时候,flags含有NGX_POST_EVENTS标记的时候时 不会立刻调用事件的handler回调的。

nginx惊群问题解决原理和负载均衡实现_第4张图片

其实就是处理完posted_accept_events队列的事件后就马上释放锁。

负载均衡实现

首先需要开启accept_mutex锁。在事件模块初始化时会初始化全局变量整形值ngx_accept_disabled。它为负载均衡机制实现的关键阈值。

在nginx启动时,其为负数,为连接总数的7/8。ngx_accept_disabled用法也很简单,当为负数的时候,不启用负载均衡,当其为正数的时候就触发负载均衡。
nginx惊群问题解决原理和负载均衡实现_第5张图片

该代码表面,当当前使用的连接到达总连接的7/8时,就不会再处理新连接了。同时,在每次调用Process_events时都会把ngx_accept_disabled减1.只有当其降到连接总数7/8以下时才会调用ngx_trylock_accept_mutex尝试处理新连接。

所以,Nginx各worker子进程间的负载均衡仅在某个worker进程处理的连接数达到它最大处理总数的7/8才会触发。

你可能感兴趣的:(Linux,服务器,nginx,负载均衡,运维)