内容简介:基本架构如图所示,客户端发起 http 请求给 nginx,nginx 转发请求给网关,网关再转发请求到后端微服务。故障现象是,每隔十几分钟或者几个小时不等,客户端就会得到一个或者连续多个请求超时错误。查看 nginx 日志,对应请求返回 499;查看网关日志,没有收到对应的请求。
一、故障
基本架构如图所示,客户端发起 http 请求给 nginx,nginx 转发请求给网关,网关再转发请求到后端微服务。
故障现象是,每隔十几分钟或者几个小时不等,客户端就会得到一个或者连续多个请求超时错误。查看 nginx 日志,对应请求返回 499;查看网关日志,没有收到对应的请求。
从日志分析,问题应该处在 nginx 或者 spring-cloud-gateway 上。
nginx 版本:1.14.2,spring-cloud 版本:Greenwich.RC2。
nginx 主要配置如下:
[root@wh-hlwzxtest1 conf]# cat nginx.conf worker_processes 8; events { use epoll; worker_connections 10240; } http { include mime.types; default_type application/octet-stream; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; #gzip on; upstream dbg2 { server 10.201.0.27:8888; keepalive 100; } server { listen 80; server_name localhost; charset utf-8; location /dbg2/ { proxy_pass http://dbg2/; proxy_http_version 1.1; proxy_set_header Connection ""; } } }
为了提高性能,nginx 发送给网关的请求为 http 1.1,可以复用 tcp 连接。
二、排查
1、查看 tcp 连接
[root@10.197.0.38 logs]# ss -n | grep 10.201.0.27:8888 tcp ESTAB 0 0 10.197.0.38:36674 10.201.0.27:8888 tcp ESTAB 0 0 10.197.0.38:40106 10.201.0.27:8888 [root@10.201.0.27 opt]# ss -n | grep 10.197.0.38 tcp ESTAB 0 0 ::ffff:10.201.0.27:8888 ::ffff:10.197.0.38:40106 tcp ESTAB 0 0 ::ffff:10.201.0.27:8888 ::ffff:10.197.0.38:39266
可以看到 nginx 和网关之间建立的 socket 连接为 (10.201.0.27:8888,10.197.0.38:40106),另外的 2 条记录就很可疑了。猜测原因是:一端异常关闭了 tcp 连接却没有通知对端,或者通知了对端但对端没有收到。
2、抓包分析
先看下 nginx 的抓包数据:
序号 8403:转发 http 请求给网关;
序号 8404:在 RTT 时间内没有收到 ack 包,重发报文;
序号 8505:RTT 约等于 0.2s,tcp 重传;
序号 8506:0.4s 没收到 ack 包,tcp 重传;
序号 8507:0.8s 没收到 ack 包,tcp 重传;
序号 8509:1.6s 没收到 ack 包,tcp 重传;
...
序号8439:28.1s(128RTT)没收到 ack 包,tcp 重传。
序号 8408:请求设置了超时时间为 3s,因此发送 FIN 包。
再看下网关的抓包数据:
序号 1372:17:24:31 收到了 nginx 发过来的 ack 确认包,对应 nginx 抓包图中的序号 1348(nginx 那台服务器时间快了差不多 1 分 30 秒);
序号 4221:2 小时后,发送 tcp keep-alive 心跳报文,(从 nginx 抓包图中也可以看出这 2 小时之内该 tcp 连接空闲);
序号 4253:75s 后再次发送 tcp keep-alive 心跳;
序号 4275:75s 后再次发送心跳;
连续 9 次;
序号 4489:发送 RST 包,通过对端重置连接。
2 小时,75s, 9 次,系统默认设置。
[root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 [root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 [root@eureka2 opt]# cat /proc/sys/net/ipv4/tcp_keepalive_probes 9
具体这几个参数的作用,参考文章: 为什么基于TCP的应用需要心跳包
3、分析
通过以上抓包分析,基本确认了问题出在 nginx 上。19:25 时,网关发送 tcp keep-alive 心跳包给 nginx 那台服务器,此时那台服务器上保留着该 tcp 连接,却没有回应;22:20 时,nginx 发送 http 请求给网关,而网关已经关闭该 tcp 连接,因此没有应答。
三、解决
1、proxy_send_timeout
nginx 中与 upstream 相关的超时配置主要有如下参数,参考: Nginx的超时timeout配置详解
proxy_read_timeout:nginx 接收 upstream server 数据超时, 默认 60s, 如果连续的 60s 内没有收到 1 个字节, 连接关闭; proxy_send_timeout:nginx 发送数据至 upstream server 超时, 默认 60s, 如果连续的 60s 内没有发送 1 个字节, 连接关闭。
这几个参数,都是针对 http 协议层面的。比如 proxy_send_timeout = 60s,并不是指如果 60s 没有发送 http 请求,就关闭连接;而是指发送 http 请求后,在两次 write 操作期间,如果超过 60s,就关闭连接。所以这几个参数,显然不是我们需要的。
2、upstream 模块的 keepalive_timeout 参数
查看官网文档, Module ngx_http_upstream_module ,
Syntax: keepalive_timeout timeout; Default: keepalive_timeout 60s; Context: upstream This directive appeared in version 1.15.3.
Sets a timeout during which an idle keepalive connection to an upstream server will stay open.
设置 tcp 连接空闲时间超过 60s 后关闭,这正是我们需要的。
为了使用该参数,升级 nginx 版本到 1.15.8,配置文件如下:
http { upstream dbg2 { server 10.201.0.27:8888; keepalive 100; keepalive_requests 30000; keepalive_timeout 300s; } ... }
设置 tcp 连接上跑了 30000 个 http 请求或者空闲 300s,那么就关闭连接。
之后继续测试,没有发现丢包。
序号 938:空闲 5 分钟后,nginx 主动发起 FIN 报文,关闭连接。
以上所述就是小编给大家介绍的《从一起丢包故障来谈谈 nginx 中的 tcp keep-alive》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。