Nginx访问间歇性卡顿的解决办法

近期有客户反馈,上线的系统在使用的过程中会出现间歇性卡顿的问题,每次持续几秒钟,然后又正常了,过一段时间又再次出现这种问题。

检查DNS服务器

因为客户反馈都集中在电信的网络上面,因此首先排查了DNS服务器,将客户方路由器的DNS服务器设置为公共DNS服务器:114.114.114.144和114.114.115.115。同时检查了域名的DNS解析,发现有一条记录的TTL设置的过小,将其修改成600。但客户反馈依然有问题。

降低Nginx的error级别并排除错误

然后打开nginx的error.log文件,发现没有特别的错误信息,打开nginx.conf文件,将 error_log的级别设置为notice(默认是error),运行了一会儿发现出现下面的warn错误:

an upstream response is buffered to a temporary file /tmp/xxx while reading upstream

主要的原因在于upstream请求HTTP头内容太大,大小超过了Nginx所设置的反向代理(proxy)的buffer值,只能写入到磁盘,所造成的结果就是某个请求会比较缓慢或者中断,比如图片经常无法显示,刷新之后又正常了等等。
关于Nginx的proxy buffer在proxy_buffering中有详细的说明:

When buffering is enabled, nginx receives a response from the proxied server as soon as possible, saving it into the buffers set by the proxy_buffer_size and proxy_buffers directives. If the whole response does not fit into memory, a part of it can be saved to a temporary file on the disk. Writing to temporary files is controlled by the proxy_max_temp_file_size and proxy_temp_file_write_size directives.

When buffering is disabled, the response is passed to a client synchronously, immediately as it is received. nginx will not try to read the whole response from the proxied server. The maximum size of the data that nginx can receive from the server at a time is set by the proxy_buffer_size directive.

也就是说proxy buffer由proxy_buffering 控制是否开启(默认开启),然后buffer的值由proxy_buffer_sizeproxy_buffers进行设置,如果整个请求超过buffer,超出的部分会写入到temporary file设置的文件中,写入临时文件的操作由proxy_max_temp_file_sizeproxy_temp_file_write_size进行设置。

proxy_buffer_size用来设置后端HTTP头响应的缓冲区的大小,proxy_buffers用来设置后端数据缓冲区大小,后面包含两个参数:number和size,开辟指定number个长度为size大小read_buf用来存储body,并非连接初始化的时候开辟number个,而是当buf不够存响应body时新申请,最多申请number个。除此之外,还有一个proxy_busy_buffers_size参数,该参数用于设置高负荷下缓冲大小,即当后端响应没有完全读取完毕之前,可以优先发送给客户端的缓存大小,其值是proxy_buffers和proxy_buffer_size的一部分,通常设置为proxy_buffer_size的两倍。
综合以上的说明并根据服务器实际的情况,将上述参数的值设置如下:

proxy_buffering on;
proxy_buffer_size        512k;
proxy_buffers            64 512k;
proxy_busy_buffers_size 1m;

经过以上设置之后,error.log中不再出现上述错误,也没有发现其他的错误。虽然客户方感觉有一些改变,但偶尔依然会有卡顿的情况。

修改Linux内核设置

记得在Resin服务器出现大量的ESTABLISHED和TIME_WAIT连接造成响应缓慢这篇文章中,为了减少TIME_WAIT的数量,将net.ipv4.tcp_tw_reusenet.ipv4.tcp_tw_recycle参数都设置成了1,这两个参数的含义如下:

  • tcp_tw_recycle:该参数用于回收TIME_WAIT连接,但是存在一个陷阱:当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。正式因如此,Linux从4.12内核之后移除了这个参数
  • tcp_tw_reuse:顾名思义就是复用TIME_WAIT连接。当创建新连接的时候,如果可能的话会考虑复用相应的TIME_WAIT连接。通常认为tcp_tw_reusetcp_tw_recycle安全一些,这是因为一来TIME_WAIT创建时间必须超过一秒才可能会被复用;二来只有连接的时间戳是递增的时候才会被复用。

通常情况下,客户方都是使用NAT方式联网访问系统的,如果将这两个参数都打开的话会出现误判的情况,因此需要打开/etc/sysctl.conf文件,将以下三个参数都设置为0:

net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_timestamps = 0

保存退出,然后执行sysctl -p命令生效。注意:一定要将上面三个参数的值设置为0,而不是将原来的设置注释掉,那样是无法生效的。

这样设置之后,问题得到完美解决。

参考资料:

nginx+tomcat 报错:『an upstream response is buffered to a temporary file 』
an upstream response is buffered to a temporary file
Nginx an upstream response is buffered to a temporary file
Nginx做前端Proxy时TIME_WAIT过多的问题
再叙TIME_WAIT
不要开启tcp_tw_recycle
tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项
【经验总结】tcp_tw_recycle参数引发的故障
记一次TIME_WAIT网络故障
被抛弃的tcp_recycle
记一次生产环境Nginx间歇性502的事故分析过程
使用 nginx 作反向代理,启用 keepalive 时,遇到 502 错误的调查过程

发表评论

电子邮件地址不会被公开。 必填项已用*标注