为什么不同的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。
- blocking-accept,进程阻塞在accept(此时不能做别的事情),直到新请求来到,这种方法新请求在多个进程建均匀分布;
- 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 孰优孰劣
从延迟的角度看,两种方法均值差不多,但是:
- 单accept队列,延迟波动很小,标准差只有1.58,延迟更稳定;
- 多accept队列,延迟波动很大,标准差有25.27, 稳定性较差。
这其实是两种排队方法的对决:
- 单accept队列,只要有worker空闲,连接总是会立即得到处理;
- 多accept队列,只有队列对应的worker空闲(其它worker即使空闲也不行),连接才能得到立即处理。
总体看单accept队列更好点,可以使用FIFO、或者Round-robin的方法代替LIFO。但是也说不准,因为忙碌的CPU(LIFO)可能比空闲的CPU处理请求更快,因为它的缓存更热。