前言
一个老生常谈的话题。 今天业务又遇到了,特此把情形分析一下,加深一次印象。
业务使用 Redis subscribe 订阅 channel, 分布在多地域多机房,通过内网的nginx代理,然后公网环境读取。
但是却经常发生断线的操作。但实际上,nginx并没有做过什么限制。 而且这2小时断一次很像是有些参数没有调对。
为了查清楚到底是谁发起的 rst, 从nginx和 应用两边抓包。但是并没有看到nginx这边出现rst。 应用这边也没有看到。
问题
起初是有想到是因为 TCP keepalive 这个内核参数, 但是我们应用每30s都会去PING/PONG一下, 理论上是不会触发这个啊。
又查了一段时间,忽然想到PING/PONG 可能跟 subscribe 是两条连接。
在重新复习一下参数.
1 | sysctl net.ipv4.tcp_keepalive_time net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_intvl |
这是默认参数,这段参数的含义:net.ipv4.tcp_keepalive_time是连接时长,当超过这个时间后,每个net.ipv4.tcp_keepalive_intvl的时间间隔会发送keepalive数据包,net.ipv4.tcp_keepalive_probe是发送keepalived数据包的频率。
根本原因
那问题就很清晰了,ipvsadm -l --timeout 连接默认过期时间是900s,也就是说,900s没消息 ipvs就给你断了。 然后我们redis subscribe 是被动收数据的,也就是说可能会出现超过900s也没有消息收到的情况。 而我们系统默认是 7200s(2小时)没消息后才会发keepalive_probes的包。
整体的流程如下:
- server 订阅 redis sub.
- redis sub 超过900s 没有消息,ipvs 断开了这个连接。
- server 服务器
net.ipv4.tcp_keepalive_time是 7200s,所以并没有发现自己连接已经断开了. - 经过 7200s,没有收到数据, 系统开始执行 tcp_keepalive_probe, 当发送第一个包的时候,才发现连接已经被关闭了。
解决
1 | sysctl -w net.ipv4.tcp_keepalive_time=600 && sysctl -w net.ipv4.tcp_keepalive_probes=10 && sysctl -w net.ipv4.tcp_keepalive_intvl=30 |