前言
我们在使用nginx做反向代理的时候,可能会遇到这个场景:后端正常的业务处理时间超过了nginx的超时时间,导致nginx主动返回504。为解决这个问题,我们网上搜索发现可以通过调整这几个参数来调大nginx的超时时间。
proxy_connect_timeout
proxy_send_timeout
proxy_read_timeout
我们调大之后发现问题确实解决了。那么这个几个参数是什么意义?是否应该都调大呢?
nginx 三个代理超时时间配置
我们先看下nginx官网对他们的解释:
proxy_connect_timeout 60s;
Defines a timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75
seconds. 定义与被代理服务器建立连接的超时时间。请注意,此超时通常不能超过75秒。proxy_send_timeout dedault 60s
Sets a timeout for transmitting a request to the proxied server. The timeout is set only between two successive write operations, not for
the transmission of the whole request. If the proxied server does not
receive anything within this time, the connection is closed.
设置将请求传输到被代理服务器的超时时间。只在两个连续的写操作之间设置超时,而不是整个请求的传输。如果代理服务器在这段时间内没有收到数据,连接将被关闭。proxy_read_timeout default 60s
Defines a timeout for reading a response from the proxied server. The timeout is set only between two successive read operations, not for
the transmission of the whole response. If the proxied server does not
transmit anything within this time, the connection is closed.
定义从被代理服务器读取响应的超时。超时设置仅在两个连续的读取操作之间,而不是整个响应的传输。如果代理服务器在这段时间内没有传输数据,连接将被关闭。
网上有些文章说 proxy_read_timeout 和 proxy_send_timeout 指的是nginx到后端的数据传送和回传超时时间,不知是不是因为版本的原因,这个说法是错误的。按照现在官网的说法是两次“读”和“写”操作之间的间隔,并不是整个数据传输时间。具体“读”和“写”是什么意思不清楚,但我推测是从socket中的读写操作。并且我在实验中发现,大致上是收到的俩个tcp报文是间隔时间超过上面的超时时间nginx就会主动断开连接。如果nginx和后端一直有数据传输,是不会触发超时的。比如websocket 可以通过定时发送心跳包来保持长连接,只要心跳包的间隔小于上面设置的send和read的超时时间即可。
并且值得注意的是并不是所有超时nginx都会记录504状态码。比如在nginx收到后端响应头前超时,nginx会返回504状态码并记录error日志;但在client已经开始收到response header后,比如传输body中超时,nginx断开连接后会在access日志中记录200,客户端则会抛出连接断开的异常。
proxy_connect_timeout 官网的注释是不能超过75s。其他系统环境不清楚,linux 下这个超时时间是和内核的syn重使次数net.ipv4.tcp_syn_retries 对应的超时时间取最小值。比如一般net.ipv4.tcp_syn_retries设置重试次数为6,若建连时间超过127s系统就会关闭,那么nginx设置再大的proxy_connect_timeout 也是没有意义的
看完上面对这三个参数的解释后,那我们在调整超时时间的时候改怎么调整呢?
我的建议是再生产环境中可以根据业务场景调大proxy_read_timeout 和 proxy_send_timeout ,但是不建议 调大proxy_connect_timeout。
原因是生产环境中nginx和后端服务器一般是走内网通讯,正常情况下建链时间本来就不应该太长。另外一个很重要的原因是如果nginx开启的被动健康检查,过长的proxy_connect_timeout 会严重影响被动健康检查的效果
nginx的被动健康检查
upstream upcheck {
server 127.0.0.1:8000 max_fails=1 fail_timeout=10s ;
}
这个一个默认的nginx被动健康检查的配置max_fails=1 fail_timeout=10s 这段的大致意思是10s内,这个节点的请求失败数超过1则会将其标记为down,下个10s内不会有新的请求到达该节点。详细的介绍大家可以去官网或者论坛去搜索,这里不再赘述。下面我着重讲一下proxy_conncet_timeout对他的影响。
被动健康检查一个常见的场景就是当后端有台服务器宕机的时候,如果proxy_connect_timeout 设置为5s,5s后nginx和后端建链超时,该条请求被标记为失败,该节点被置为down。这样可以在一定程度上做到快速止损。而假如我们将proxy_connect_timeout 设置为60s,所有到达该节点的请求60s后才会因为超时剔除宕机节点。
下面我做一个简单的实验来对比一下宕机情况下proxy_connect_timeout设置为5s和60s的区别
两组upstream都为2个节点,采用默认被动健康检查配置(max_fails=1 fail_timeout=10s),其中一个节点不可用(ping不通),关闭nginx重试,每秒请求一次,持续10分钟
nginx配置
server {
listen 80;
server_name _ ;
access_log /opt/log/ngx-lan_access.log main;
location = /upcheck {
proxy_next_upstream_tries 1;
proxy_connect_timeout 5s;
proxy_pass http://upcheck;
}
location = /upcheck2 {
proxy_next_upstream_tries 1;
proxy_connect_timeout 60s;
proxy_pass http://upcheck2;
}
upstream upcheck {
server 127.0.0.1:8500 max_fails=1 fail_timeout=10s ;
server 192.168.100.100:8500 max_fails=1 fail_timeout=10s ;
}
upstream upcheck2 {
server 127.0.0.1:8500 max_fails=1 fail_timeout=10s ;
server 192.168.100.100:8500 max_fails=1 fail_timeout=10s ;
}
}
用curl访问
for i in {1..600}; do curl http://127.0.0.1/upcheck -I > /dev/null 2>&1 & sleep 1; done
for i in {1..600}; do curl http://127.0.0.1/upcheck2 -I > /dev/null 2>&1 & sleep 1; done
- 1
- 2
然后统计一下这两组配置的200状态码
5s的这组 200状态码个数是 558
60s的这组 200状态码个数是 468
这个对比还是很明显的
总结
我们在设置nginx的超时配置的时候,proxy_connect_timeout 建议设置的比较小,proxy_read_timeout 和 proxy_send_timeout 可以适当调大