在上一篇 Nginx 反向代理的优化点一 中简单介绍了反向代理服务与上游服务通信时开启 HTTP/1.1,看似一个简单的指令却能优化服务间网络传输的性能。本篇继续介绍另一个容易被忽视的优化点:buffer 缓冲区。
Nginx 与上游服务通信时可以把返回的响应内容(包含响应头 header 和响应体 body )存入到缓冲区,当缓冲区的数据积累到一定的体积再一并发送给客户端。
缓冲区的开关通过 proxy_buffing
指令来配置,默认是开启的。如果 Nginx 代理的只是常规 API 服务时,默认的配置可以很好的工作,但是当上游服务返回的响应内容的体积较大时,此时默认的内存缓冲区容易被存满,内存缓冲区存满后则会将响应体作为临时文件写入到磁盘,在请求完成后再删除临时文件。
当 Nginx 代理的上游服务响应的是一个体积较大的 HTML 文件时就容易出现上面说的超出内存缓冲区体积限制的情况,当此类请求量较频繁时则意味着频繁的磁盘 IO 读写,也意味着性能的低下,此时可以调整默认缓冲区的存储大小,但是这类调整也需要谨慎,把内存作为缓冲区时要防止内存被完全占用的情况出现,所以要视情况谨慎处理,需要评估服务器的内存能否承载足够的流量。
调整缓冲区的响应体由 proxy_buffers
指令来控制,该指令有两个值可以配置,以下是默认配置。
proxy_buffers 8 4k|8k;
前面的 8 代表的是缓冲区的数量,而后面的 4k|8k 代表的就是单个缓冲区的体积大小,那么对于大体积的 HTML 文件来说,设置多大合适呢?假如 HTML 体积是 176KB,那么单个缓冲区的体积为 176KB / 8,结果是 22KB,而在实际设置缓冲区的大小时最好是能稍微超过 176KB,这里建议设置成 24KB,这样最大能缓冲 192KB。
为了验证上面的理论,可以通过一个测试来进行验证,测试的方法是对 Nginx 的临时目录进行 watch,如果缓冲区设置的太小,那么临时目录就会写入一些缓冲文件。
先配置一个固定的临时目录用来写入缓冲文件,这里需要用到 proxy_temp_path
指令来配置。
proxy_temp_path /var/folders/kf/v_zfp_6n42jg32n5pwgdjcy40000gn/nginx_tmp 1;
编写一段 Node.js 代码来对临时目录进行 watch。
var watch = require('fs').watch;
var tempPath = '/var/folders/kf/v_zfp_6n42jg32n5pwgdjcy40000gn/nginx_tmp';
var watcher = watch(tempPath, { recursive: true }, (eventType, filename) => {
console.log(eventType, filename);
});
使用 autocannon
测试工具来模拟大量的请求,具体的参数如下。
npx autocannon -c 300 -p 20 -l http://test.example.com/index.html
请求的 HTML 文件体积为 335KB(使用 Node.js 服务来输出 HTML 文件,这里省略了该服务搭建的介绍及反向代理服务的常规配置),当 proxy_buffers
的值配置为 8 36k
时,watch 的脚本会在控制台输出如下写入临时文件的信息,说明缓冲区写入到了磁盘中。
rename 7/0000000047
rename 8/0000000048
rename 9/0000000049
rename 0/0000000050
rename 1/0000000051
rename 2/0000000052
rename 3/0000000053
rename 8/0000000048
rename 9/0000000049
...
当把单个缓冲体积从 36k 改成 48k 时,watch 脚本不会有任何输出,说明缓冲区完全写入到了内存中。
通过测试,也验证了上面的理论:当遇到使用 Nginx 作为反向代理时,代理的请求为较大的 HTML 文件时,需要注意使用 proxy_buffers
来配置缓冲区的内容尽量不要写入到磁盘。
不过在配置缓冲区最大响应体的体积时,如果想图省事配置一个较大的体积也是有问题的。缓冲区并不是越大越好, proxy_buffers
的配置时作用到每一个请求的,如果缓冲区配置过大,那么当并发数较大时就有可能导致 Nginx 所在的服务器的内存被占满。
那么把缓冲区配置写入到内存而不是磁盘会不会提升上游服务的性能呢,带着这个疑问分别对写入缓冲到内存和硬盘做了压测,发现两个测试结果并没有很明显高下立判的差异。这个结果让人很疑惑,到底缓冲写入内存会不会有效提升上游服务的性能呢?Google 之后发现了有同样疑惑的同学提出的问题而引发的讨论,摘取一段讨论的结果如下,详细的讨论可以见文末最后一个参考链接。
在全缓冲模式下,上游的数据能读多少读多少,不会受下游写速度的影响。因为内存占用会明显高很多(同时有可能在超出 proxybuffers
的大小之后,刷到临时文件,除非你把 proxymaxtempfile_size 置为 0)。 全缓冲模式的最大意义是避免下游慢速连接影响到并发能力较弱的后端服务器,而不是提升吞吐量或者降低延时。