Nginx 使用 reuseport 导致偶发性卡顿:问题探究与解决方案**
2024-01-28 03:09:07
背景:
从 2018 年开始,我们有个业务陆续接到反馈,Nginx 线上集群经常出现不响应或偶发性的“超慢”请求。这种卡顿每天都有少量出现。奇怪的是,只有多个集群中的一个出现,其他压力更大的集群皆未出现。业务结构比较简单,前端通过 Nginx 转发请求至后端应用。
经过仔细排查,我们发现问题出在 Nginx 使用的 reuseport 选项上。reuseport 是一个 Linux 内核选项,允许在一个端口上绑定多个套接字,从而提高网络性能。我们本来是为了提高 Nginx 的性能才开启了这个选项,但没想到却导致了偶发性卡顿。
原理分析:
为了理解 reuseport 如何导致卡顿,我们需要先了解一下它的工作原理。
在 Linux 内核中,每个套接字都有一个接收队列和一个发送队列。当数据包到达时,内核会将数据包放入接收队列,应用程序可以通过调用 recv() 函数从接收队列中读取数据包。当应用程序向网络发送数据时,内核会将数据包放入发送队列,然后由网络设备将数据包发送出去。
当 reuseport 选项开启时,多个套接字可以绑定到同一个端口上。这意味着内核会为每个套接字创建一个接收队列和一个发送队列。当数据包到达时,内核会将数据包放入任意一个套接字的接收队列中。应用程序可以通过调用 recv() 函数从任何一个套接字的接收队列中读取数据包。
问题根源:
在我们的案例中,Nginx 使用了 reuseport 选项,并且后端应用使用了多进程模型。这意味着 Nginx 会为每个后端进程创建一个套接字,并绑定到同一个端口上。当数据包到达时,内核会将数据包放入任意一个套接字的接收队列中。
如果某个后端进程崩溃或重启,内核会关闭该进程对应的套接字,并将其从接收队列中移除。此时,其他后端进程仍然可以通过自己的套接字接收数据包。但是,如果内核将数据包放入该进程对应的套接字的接收队列中,那么数据包就会丢失。
解决方案:
为了解决这个问题,我们有两种选择:
- 禁用 reuseport 选项。
- 在 Nginx 配置中设置 backlog 参数。
我们选择了第二种方案,因为禁用 reuseport 选项会降低 Nginx 的性能。在 Nginx 配置中设置 backlog 参数可以限制内核为每个套接字接收队列保存的数据包数量。当数据包数量达到 backlog 参数的值时,内核会将新到达的数据包丢弃。这样可以防止数据包丢失。
优化建议:
除了设置 backlog 参数之外,我们还对 Nginx 的配置进行了其他优化,包括:
- 调整 worker_processes 参数的值。
- 调整 worker_connections 参数的值。
- 调整 keepalive_timeout 参数的值。
- 启用 gzip 压缩。
- 使用 CDN 缓存静态资源。
这些优化措施使 Nginx 的性能得到了显著提升,并且偶发性卡顿的问题也得到了解决。
总结:
通过对 Nginx 使用 reuseport 导致的偶发性卡顿问题的分析,我们了解了 reuseport 的工作原理,并找到了问题的根源。我们通过设置 backlog 参数来解决这个问题,并对 Nginx 的配置进行了其他优化,使 Nginx 的性能得到了显著提升。希望这篇文章能够帮助您解决类似的问题,并对 Nginx 的网络优化和性能提升有所启发。