前些日听闻某线上环境的一台负载均衡服务器“挂掉”了,究其原因是服务器日志回滚脚本失效致使磁盘满,最终大部分请求均无法转发,也算是个不小的事故。

虽说线上环境磁盘满这种低级错误是不应该出现的,但是在Nginx的配置上做做优化不失为一个双保险的好方案。其实Nginx提供了详细的buffer配置方案,可供配置的关键参数如下:(顺路分析一下请求无法转发的原因)

上传相关(缓冲client端数据):
 client_header_buffer_size size用以缓冲client端请求的header,默认大小为1k。
large_client_header_buffers number size
用以缓冲较大的client端请求的header(client_header_buffer_size无法容下的),故此值应大于前者,为节约内存空间,此buffer有个数限制,默认个数为4个,默认大小为8k。建立长连接后此buffer会被释放。
client_body_buffer_size size

用以缓冲client端请求的body,默认大小为8k|16k(视操作系统而定,32位系统为8k,64位系统为16k,后同),如果请求的body大于此值,则Nginx会将此请求缓存至磁盘。而当磁盘空间不足时,Nginx会断开此链接。

验证问题:

使用dd命令填满Nginx磁盘并构造post请求(纯body大小10k,无header),server报如下错误:

nginx 保持header nginx client_header_buffer_默认大小

client报错如下,连接被重置:

nginx 保持header nginx client_header_buffer_服务器_02

解决思路①:

设置配置文件中client_body_buffer_size为16k,设置完成并重载后请求正常转发。

下载相关(缓冲proxy端数据):

proxy_buffering on|off

设置是否启用proxy端服务器的响应内容缓冲,默认为on。若启用缓冲,则Nginx会以最快速度缓冲后端响应内容在内存或者磁盘上;若关闭,则Nginx会按照响应内容的多少立刻同步到客户端。

proxy_buffer_size size

用以缓冲proxy端服务器的第一部分内容,默认大小4k|8k,通常用以存放响应的header。

proxy_buffers number size

当proxy_buffering启用时生效,用以缓冲proxy端服务器的响应内容,默认数量为8个,默认大小为4k|8k。

proxy_busy_buffers_size size

当proxy_buffering启用时生效,若proxy端响应内容未完全获取到buffer中,用以限制向client传输响应内容的buffer总量,默认大小8k|16k。

proxy_max_temp_file_size size

当proxy_buffering启用时生效,若proxy端响应内容超过proxy_buffer_size和proxy_buffers时,Nginx会把超过的部分写入磁盘上的临时文件中,临时文件默认最大值为1024m,设置此值为0则关闭临时文件缓存功能。当磁盘空间不足时,会断开和proxy端的连接,而client端则会获取响应内容超时。

验证问题:

用curl命令做了一个简单的测试,反复尝试获取后端100k的文件10次,总计成功1次,其他9次均在第一秒钟内获取了一部分数据,此后再无响应,直至超时。如图:

nginx 保持header nginx client_header_buffer_默认大小_03

在启用proxy_buffering时,Nginx会同时从proxy端获取响应内容并向client端发送,当获取速度大于发送速度直至所有proxy_buffers占满时,Nginx会报错并断开与proxy端的连接,报错如下:

解决思路②:

设置配置文件中proxy_max_temp_file_size项为0关闭此功能并重载后重试curl 100k文件,100%成功,进一步增大获取的文件大小,直至10m亦100%成功,如图:

缺点:一般情况下server-proxy端网络状况会好于server-client端,将proxy_max_temp_file_size设置为0相当于关闭Nginx upstream磁盘缓存,这在传输大文件时会明显影响性能。

解决思路③:

设置配置文件中proxy_buffers总数大于需要转发的文件大小,此处设置为proxy_buffers 8 2m,设置完成并重载后请求正常转发。

缺点:将proxy_buffers调大相当于增加内存利用量,而所有上述proxy buffer参数都是作用到每一个请求的,当proxy_buffers设置偏大时,遇到高并发的大文件请求时Nginx进程的内存使用量将会明显上升。对比测试了调大proxy_buffers后不同并发数时Nginx的内存使用量,如下图所示,在400并发的时候Nginx的内置占用已超过800MB,而默认proxy_buffers设置时1000并发时Nginx内存占用量也仅在100MB左右。

结论:

综上,个人以为最合理的解决方案应该是这样的:

采用Tengine作为负载均衡服务器。
设置监控脚本监控磁盘利用率。当磁盘满时,设置proxy_request_buffering为off,proxy_max_temp_file_size为0并重载Tengine;当磁盘利用率恢复正常时,恢复Tengine的设置。
如果只用Nginx的话,将2中的设置proxy_request_buffering为off改为调大client_body_buffer_size。