Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nginx keepalive_requests 踩坑总结 #37

Open
jinhailang opened this issue Sep 12, 2018 · 0 comments
Open

Nginx keepalive_requests 踩坑总结 #37

jinhailang opened this issue Sep 12, 2018 · 0 comments

Comments

@jinhailang
Copy link
Owner

jinhailang commented Sep 12, 2018

Nginx keepalive_requests 踩坑总结

问题起因

Waf(基于 Nginx 实现的七层防护系统)上线上后,QA 同事对系统进行大流量压测(十万级 QPS),发现部署机器处于 TIME_WAIT 状态的 TCP 连接数异常,高峰期达到几千。按理 waf 这边使用的是长连接,连接数不应该这么多的。

问题定位

首先确认下 TCP 连接处于 TIME_WAIT 原因:

TCP 连接主动关闭方会处于较长的 TIME_WAIT 状态, 以确保数据传输完毕, 时间可长达 2 * MSL, 即就是一个数据包在网络中往返一次的最长时间。
其目的是避免连接串用导致无法区分新旧连接。

这就说明确实是服务端这边主动关闭了连接。我重新 review 了一下 Nginx 配置,确实使用了长连接,并且,我上线前在本地使用 ab 工具压测过, 没有观察到连接数量异常情况

只能祭出 google 大法了,发现可能是 keepalive_requests 设置太小引起的。

keepalive_requests 参数限制了一个 HTTP 长连接最多可以处理完成的最大请求数, 默认是 100。当连接处理完成的请求数达到最大请求数后,将关闭连接。

而我并没有配置 keepalive_requests,所以,就是使用的默认数 100,即一个长连接只能处理一百个请求,然后 Nginx 就就会主动关闭连接,使大量连接处于 TIME_WAIT 状态。

我猜测很大可能就是这个原因了,为了在本地验证,我将 keepalive_requests 分别设为 10, 50, 100, 1000,分四组分别进行压测,对比结果。

压测命令:

ab -n100000 -c10 -k http://127.0.0.1:8086/test

然后使用 netstat -nat |awk '{print $6}'|sort|uniq -c 查看连接数,结果出乎意料的清晰,TIME_WAIT 数量基本等于 总请求数/keepalive_requests,这就基本证实了猜测。

问题解决

其实大部分情况下,使用默认值是没有问题的,但是对于 QPS 较大的情况,默认数值就不够了,综合考虑,在 Nginx conf 增加配置项:

keepalive_requests      1024;

可能很多人更习惯不做限制,可以设置一个最大的数来实现这个效果 -- 2^32 - 1 = 4294967295(这是在运行32位和64位系统的计算机中是无符号长整形数所能表示的最大值,亦是运行32位系统的计算机中所能表示的最大自然数),

疑问

虽然问题解决了,但是,为什么 Nginx 要对单个长连接的处理请求数进行最大限制呢?一般我们理解的,长连接是在连接池里维护的,只要连接本身没有超时等异常问题,是可以一直复用的。

关于这个疑问,讨论的似乎不多,只在 Nginx 开发者论坛上看[nginx] Upstream keepalive: keepalive_requests directive.有所讨论,开发者有模糊的表述:

Much like keepalive_requests for client connections, this is mostly
a safeguard to make sure connections are closed periodically and the
memory allocated from the connection pool is freed.

但是,为什么需要定时关闭连接,释放内存?难道这只是一种保护策略,怕极端情况下,用户配置不当,导致连接一直不断开,引起内存问题?很奇怪,从 Nginx 开发日志也没有找到特别说明。

另外,上面的讨论贴其实是讨论 Upstream keepalive_requests 设置的,最新版 Nginx (version 1.15.3.) 在 Upstream 内增加了 keepalive_requests 配置项,与 keepalive_requests client connections 类似,默认值也是 100。也就是说,之前的版本,连接上游的连接是没有次数限制的,完全由上游的服务端的 keepalive_requests 设置,但是,如果上游服务端不使用 Nginx 的话,别的系统一般也是没有这个限制的。

在大流量的场景下,Nginx 代理升级到这个版本,就可能会引起连接异常情况。关于默认值和兼容性,作者的回复也比较主观,可能这块确实很难面面俱到吧,只能满足大多数场景了。

综上,在 QPS 较高的场景下,服务端要注意将 keepalive_requests 设置的大一些,如果 Nginx 升级到最新版,还需要注意设置 upstream 的keepalive_requests,这两个数量可以不一致,一般服务端要设置的大些,因为一般来说,一个服务端可能对应多个代理。

@jinhailang jinhailang changed the title Nginx keepalive_requests 踩坑总结 Nginx keepalive_requests 踩坑总结 Sep 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant