几个 Nginx proxy timeout 指令的解释

有三个指令,分别是:

proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout

 

proxy_connect_timeout  是和后端建立连接的超时时间,记住不要超过 75s 。如果超时,Nginx 的返回码是多少?

proxy_read_timeout  是从后端读取数据的超时时间,两次读取操作的时间间隔如果大于这个值,和后端的连接会被关闭。如果一个请求时间时间非常大,要把这个值设大点。如果超时,Nginx 的返回码是多少?

proxy_send_timeout  是向后端写数据的超时时间,两次写操作的时间间隔大于这个值,也就是过了这么长时间后端还是没有收到数据(我理解是 根据 TCP ACK 判断),连接会被关闭。如果超时,Nginx 的返回码是多少?

 

上面三个指令 默认的值都是 60s 。

 

Nginx 机器关闭 443 端口导致的 LVS 故障

今天在下线 Nginx 443 端口的时候发现 正常的 80 的服务也挂了,然后发现 Nginx 前面的 LVS 日志报如下:

Dec 20 16:44:04 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.54]:443.
Dec 20 16:44:04 lb16 Keepalived_healthcheckers: Removing service [10.0.11.54]:443 from VS [125.39.216.11_443]:0
Dec 20 16:44:04 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.71]:443.
Dec 20 16:44:04 lb16 Keepalived_healthcheckers: Removing service [10.0.11.71]:443 from VS [123.150.178.140_443]:0
Dec 20 16:44:04 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.71]:443.
Dec 20 16:44:04 lb16 Keepalived_healthcheckers: Removing service [10.0.11.71]:443 from VS [125.39.216.11_443]:0
Dec 20 16:44:09 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.54]:443.
Dec 20 16:44:09 lb16 Keepalived_healthcheckers: Removing service [10.0.11.54]:443 from VS [123.150.178.140_443]:0
Dec 20 16:44:09 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.56]:443.
Dec 20 16:44:09 lb16 Keepalived_healthcheckers: Removing service [10.0.11.56]:443 from VS [123.150.178.140_443]:0
Dec 20 16:44:13 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.56]:443.
Dec 20 16:44:13 lb16 Keepalived_healthcheckers: Removing service [10.0.11.56]:443 from VS [125.39.216.11_443]:0
Dec 20 16:44:23 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.25]:443.
Dec 20 16:44:23 lb16 Keepalived_healthcheckers: Removing service [10.0.11.25]:443 from VS [123.150.178.140_443]:0
Dec 20 16:44:23 lb16 Keepalived_healthcheckers: Lost quorum 1-0=1 > 0 for VS [123.150.178.140_443]:0
Dec 20 16:44:23 lb16 Keepalived_healthcheckers: Executing [ip addr del 123.150.178.140/32 dev lo] for VS [123.150.178.140_443]:0
Dec 20 16:44:24 lb16 Keepalived_healthcheckers: SSL handshake/communication error connecting to server (openssl errno: 1) [10.0.11.25]:443.
Dec 20 16:44:24 lb16 Keepalived_healthcheckers: Removing service [10.0.11.25]:443 from VS [125.39.216.11_443]:0
Dec 20 16:44:24 lb16 Keepalived_healthcheckers: Lost quorum 1-0=1 > 0 for VS [125.39.216.11_443]:0
Dec 20 16:44:24 lb16 Keepalived_healthcheckers: Executing [ip addr del 125.39.216.11/32 dev lo] for VS [125.39.216.11_443]:0

 

原来 LVS 检查 后端的 443 端口,发现全部失败之后,就把对应的 VIP 删掉了,reload LVS 之后又好了。

这是一个大坑,这是 LVS 的自动切换机制,是这样:

1. LVS 的 VIP 可以有多个端口,当某个端口检查全部失败时(也就是所有后端的这个端口都挂掉),LVS 会删除这个 VIP,其他端口的正常访问会失败。

2. 如果 reload LVS 的时候所有后端的某个端口都已经挂了,VIP 则不会删除,其他的端口服务没有问题。

所以,对于 Nginx 配置里面的 端口 删除,必须要谨慎,要检查 LVS,否则可能酿成大错。

 

关于 Nginx upstream keepalive 的说明

模块是 HttpUpstreamModule,配置的一个例子:

[shell]upstream http_backend {
server 127.0.0.1:8080;

keepalive 16;
}
server {

location /http/ {
proxy_pass http://http_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";

}
}[/shell]

 

有几点要说明:

1. 默认情况下 Nginx 访问后端都是用的短连接(HTTP1.0),一个请求来了,Nginx 新开一个端口和后端建立连接,请求结束连接回收。如果像上面的配置一样设置了长连接,Nginx 会接受客户端的请求,处理完成之后 Nginx 会「继续保持和后端的长连接」,如果并发请求超过了 keepalive 指定的最大连接数,Nginx 会启动新的连接 来转发请求,新连接在请求完毕后关闭,而且新建立的连接是长连接,这可能会造成额外的问题,最后再说。

2. keepalive 指定的 数值 是 Nginx 每个 worker  连接后端的最大长连接数,而不是整个 Nginx 的。 而且这里的后端指的是「所有的后端」,而不是每一个后端(已验证)。

3. 在官网上  可以看到 还有一个 single 参数,说实话我没懂什么意思,但是它已经被废弃了,看这里

4. HttpUpstreamModule 模块里面还有一共指令:least_conn,解释如下:

Specifies that a group should use a load balancing method where a request is passed to the server with the least number of active connections, taking into account weights of servers. If there are several such servers, they are tried in turn using a weighted round-robin balancing method.

翻译:

指定服务器组的负载均衡方法,根据其权重值,将请求发送到「活跃连接数最少」的那台服务器。 如果这样的服务器有多台,那就采取有权重的轮转法进行尝试。

使用这个指令的时候后端服务器的连接数好像会变的非常少,很奇怪,我还没亲自测过,待确认。

 

最后再说一下第 1 点,先说一个现象,我发现当 keepalive 设置小的时候,比如1,那么并发请求上去之后 Nginx 会出现大量的 TIME_WAIT,而如果把 keepalive 关掉(proxy_http_version 1.1 和 proxy_set_header Connection “” 指令也去掉),那么 TIME_WAIT 就会出现在后端服务器了,后端用的是 tornado,相信 jetty 和 tomcat 也是一样。

这个现象是怎么产生的呢,做一个测试。

命令:ab -n 100 -c 5 http://pxe1.hy01/

pxe1.hy01 (Nginx,上面配置了长连接,ip: 10.0.31.84) ,它会转到 pxe0.hy01 的 8888 端口(tornado,ip: 10.0.11.12)

测试的时候在 pxe1.hy01 执行抓包命令:

# tcpdump -i em2 -A host 10.0.11.12 -n

看到:

00:22:53.252039 IP 10.0.31.84.53915 > 10.0.11.12.ddi-tcp-1: Flags [P.], seq 81:161, ack 275, win 123, length 80
@.@.].
..T
…..”…p%8|..P..{>…GET / HTTP/1.1
Host: http_backend
User-Agent: ApacheBench/2.3
Accept: */*

但是如果把 pxe1.hy01 的长连接设置都去掉的话,抓包如下:

00:23:58.111974 IP 10.0.31.84.54051 > 10.0.11.12.ddi-tcp-1: Flags [P.], seq 1:100, ack 1, win 115, length 99
E…..@.@.Z=
..T
….#”…O…SUP..s>…GET / HTTP/1.0
Host: http_backend
Connection: close
User-Agent: ApacheBench/2.3
Accept: */*

那么上面出现的现象就好解释了,是这样:

Nginx 和后端的长连接不够用时 Nginx 会新建连接来处理新的请求,而我们的配置已经配置死了 HTTP1.1,建立连接后,后端认为是「长连接」而不会主动关闭连接(一般有个空闲超时),关闭连接由 Nginx 来做了,所以 Nginx 会出现大量的 TIME_WAIT。

而默认情况下,Nginx 用 HTTP1.0 请求后端,后端处理完成后就主动关闭连接,所以 TIME_WAIT 在后端。

那么现在有新的问题了,如果开启了长连接,而长连接又大量不够用,此时 Nginx 存在的 TIME_WAIT 可能会大量占用端口,导致端口用尽,如果用尽,后果很严重。

所以,「慎用 Nginx 的 长连接」。

 

把 DNS 域的 @ 配置成 CNAME 的一个坑

如果你在一个域里面( 比如 test.com. )同时配置下面两条记录:

@ CNAME 默认(或其他线路) www.test.com.
@ MX         默认(或其他线路) 「记录值」.

那么 向 test.com. 域发邮件可能失败,原因是:

CNAME 其实只是一个别名,DNS 服务器在收到常规 DNS 记录 (非 CNAME )请求的时候会检查是否已经有对应的 CNAME 记录,如果有就重新启动对 CNAME 值的查询并返回,如果显示指定查询的就是 CNAME 记录,则直接返回。

所以,即使在配了 CNAME 之后又配置了 A、MX 等记录,也没用,因为 CNAME 优先级高。

 

记住,「请不要对 @ 设置 CNAME 记录」。

 

RFC 的说明如下:

When a DNS resolver encounters a CNAME record while looking for a regular resource record, it will restart the query using the canonical name instead of the original name. (If the resolver is specifically told to look for CNAME records, the canonical name (right-hand side) is returned, rather than restarting the query.)

An alias defined in a CNAME record must have no other resource records of other types (MX, A, etc.). (RFC 1034 section 3.6.2, RFC 1912 section 2.4) The exception is when DNSSEC is being used, in which case there can be DNSSEC related records such as RRSIG, NSEC, etc. (RFC 2181 section 10.1)

 

参考

http://blog.clanzx.net/2013/09/02/dns-cname.html

 

update at Sat Jul 18 10:13:15 CST 2015

增加一点,AWS 的 route53 可以通过 alias 实现对 @ 增加 CNAME 记录,但只限于 LB 和 S3 等服务,具体请看  http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html

http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html

0FA5B5CB-62E7-4441-AE4C-95D729B51EAC