Nginx配置中一些细节坑
反代 - proxy reverse
后端处理时间非常长(>3分钟 >5分钟 等)
- 一般来说个人建议如果后端处理某个请求需要很久才返回response,应该将请求设计为异步请求,即通过不断得请求某个state接口获取处理状态,而不是一直holding等待返回
首先这种情况下,proxy_connect_timeout, proxy_read_timeout 和 proxy_send_timeout 参数值都要根据实际情况调大
然后需要注意nginx -> upstream的链路上是否有防火墙策略配置,特别是有状态型防火墙
nginx默认对client和 upstream都没有开启 TCP keepalive,即TCP会话存活检查 (不是HTTP keepalive,不是TCP会话复用!!!)
这种情况下,如果nginx -> upstream的链路上部署了有状态防火墙策略配置且该防火墙配置了300s timeout
若一个请求后端处理需要530秒,nginx将处理请求转发给upstream,nginx就会一直hold着会话(ESTABLISHED)
在等候后端处理完毕回包期间,该TCP会话上nginx<->upstream之间不会有额外的流量
这样就容易导致链路上的有状态防火墙判定该会话已经失效,然后拦截
当后端处理完毕回包时,报文无法被nginx接收到,然后待nginx 到达 proxy_connect_timeout阈值后,nginx向请求方抛出504 timeout错误
且此时在nginx error.log中可见 upstream timed out (110: Connection timed out) while reading response header from upstream
错误字样
这种情况下,需要启用nginx对upstream的tcp keepalive (即socket中的SO_KEEPALIVE option),proxy_socket_keepalive on;
开启后,nginx就会在会话中根据内核参数中的 net.ipv4.tcp_keepalive_intvl , net.ipv4.tcp_keepalive_probes 以及 net.ipv4.tcp_keepalive_time 配置的规则对 upstream 发送keepalive probe packet 探测会话存活,同时避免链路有状态防火墙拦截请求(建议多数情况下都开启,根据实际情况调整参数值,方便nginx在upstream不健康时主动断开)
ps: 如果需要nginx对client发送tcp keepalive probe,需要在listen指令中配置 so_keepalive=on (忽略此参数的话,操作系统的设置将对套接字有效,而linux下TCP KeepAlive并不是默认开启的,在Linux系统上没有一个全局的选项去开启TCP的KeepAlive。需要开启KeepAlive的应用必须在TCP的socket中单独开启)
简述关于tcp keepalive probe:
Linux Kernel有三个选项影响到KeepAlive的行为:
- tcp_keepalive_time 7200// 距离上次传送数据多少时间未收到新报文判断为开始检测,单位秒,默认7200s
- tcp_keepalive_intvl 75// 检测开始每多少时间发送心跳包,单位秒,默认75s
- tcp_keepalive_probes 9// 发送几次心跳包对方未响应则close连接,默认9次
TCP socket也有三个选项和内核对应,通过setsockopt系统调用针对单独的socket进行设置:
- TCPKEEPCNT: 覆盖 tcpkeepaliveprobes
- TCPKEEPIDLE: 覆盖 tcpkeepalivetime
- TCPKEEPINTVL: 覆盖 tcpkeepalive_intvl
参考:
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_socket_keepalive
https://mailman.nginx.org/pipermail/nginx/2020-November/060173.html
https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
https://www.cnblogs.com/linguoguo/p/15795045.html
upstream / 后端 是https, 即 proxy_pass https://servers;
如果nginx配置了多个https域名到同一个upstream则需要配置
proxy_ssl_server_name on; proxy_ssl_name $ssl_server_name; proxy_ssl_session_reuse off;
否则很有可能会因为ssl 会话复用引发https证书检验错误的问题