为什么不同的nginx进程CPU使用率差别很大

本文是Why does one NGINX worker take all the load? 的阅读笔记。

nginx是多进程程序,每个进程称为一个worker,这些worker是对等的,理论上每个进程CPU使用率应该相同,但事实并非如此。我以前也很好奇,为啥有的worker cpu使用率很高,有的很低,非常不均衡。

1 单accept队列,多worker进程

这种情况,有两种accept方法,一种是 blocking-accept,一种是 epoll-accept。

  1. blocking-accept,进程阻塞在accept(此时不能做别的事情),直到新请求来到,这种方法新请求在多个进程建均匀分布;
  2. epoll-accept,把 listen 的fd加入epoll,epoll报告可读时,调用accept(之前不用阻塞在accept,程序可以做其它事情),这种方法新请求总是集中在最忙的进程。

nginx采用的是 epoll-accept 方法,负载在多个worker间分布不均,忙的总是很忙。(类似LIFO算法,因为最忙的进程总是最新加入event loop,最新加入的总是得到新的请求)

2 多accept队列,多worker进程

通常一个port在bind后,无法再次bind(多进程共享accept队列)。后来Linux 支持了 SO_REUSEPORT ,多个进程可以bind到同一个port(每个进程一个accept队列)。使用 SO_REUSEPORT 后,负载在多个worker间均匀分布,多个worker差不多忙。

3 孰优孰劣

从延迟的角度看,两种方法均值差不多,但是:

  1. 单accept队列,延迟波动很小,标准差只有1.58,延迟更稳定;
  2. 多accept队列,延迟波动很大,标准差有25.27, 稳定性较差。

这其实是两种排队方法的对决:

  1. 单accept队列,只要有worker空闲,连接总是会立即得到处理;
  2. 多accept队列,只有队列对应的worker空闲(其它worker即使空闲也不行),连接才能得到立即处理。

总体看单accept队列更好点,可以使用FIFO、或者Round-robin的方法代替LIFO。但是也说不准,因为忙碌的CPU(LIFO)可能比空闲的CPU处理请求更快,因为它的缓存更热。

Author: zhengzhiyong

Created: 2023-09-03 日 23:20

Validate