服务之间调用连接不上运维层面问题分析

笔者: 张首富
时间: 20201118 晚

问题描述

1,20201118日上午清华反应 callout 服务调用 robot 服务失败;网络-赵晨排查从网络层面排查并没有重传,丢包现象;

2,20201118日下午李刚反应 auth 等多个 java 服务连不上数据库,连不上数据库的 java 服务都是部署在同一台机器上;

过程分析

上面两个问题其实是有相同点的,相同点就是服务之间的调用不能正常通信(好像是废话),相同的地方就是都去连接服务端固定的一个端口;

通过监控平台分析发下,在发生问题的时间点上,出问题的两台客户端机器上的TCP_timeout都过多,cpu 内存磁盘等指标都趋于正常。所以从 这个问题开始着手分析;监控数据如下

服务之间调用连接不上运维层面问题分析

服务之间调用连接不上运维层面问题分析

然后怀疑是 TCP timeout 连接数过多产生的问题,针对这方面进行排查

排查过程

查看系统默认 tcp 相关指标

# 是否允许将TIME-WAIT sockets重新用于新的TCP连接,默认是否
[root@idc-111 ~]# cat /proc/sys/net/ipv4/tcp_tw_reuse
0
# 是否开启TCP连接中TIME-WAIT sockets的快速回收,默认是否
[root@idc-111 ~]# cat /proc/sys/net/ipv4/tcp_tw_recycle
0
# 系統默认的TIMEOUT时间
[root@idc-111 ~]# cat /proc/sys/net/ipv4/tcp_fin_timeout
60
# 系统可用的 tcp-udp 连接数
[root@idc-111 ~]# cat /proc/sys/net/ipv4/ip_local_port_range
32768    60999
# 系统默认可用 28321个
[root@idc-111 ~]# echo "" | awk 'BEGIN{print 60999-32768}'
28231

通过查看这个指标,和上面的监控图来看,使我们客户端可用的 tcp 端口不够用了,所以导致连接失败;然后我们来看下这个端口范围是否真的会影响连接

主要分析ip_local_port_range参数

网上关于 net.ipv4.ip_local_port_range 的值的效果众说纷纭(下面所说的连接都假定使用的是相同的协议(都是 TCP 或 UDP)):

  • 大部分文章都说这个值决定了客户端的一个 ip 可用的端口数量,即一个 ip 最多只能创建 60K 多一点的连接(1025-65535),如果要突破这个限制需要客户端机器绑定多个 ip。
  • 还有部分文章说的是这个值决定的是 socket 四元组中的本地端口数量,即一个 ip 对同一个目标 ip+port 最多可以创建 60K 多一点连接,只要目标 ip 或端口不一样就可以使用相同的本地端口,不一定需要多个客户端 ip 就可以突破端口数量限制。

查看帮助文档介绍的模糊不清

ip_local_port_range - 2 INTEGERS
    Defines the local port range that is used by TCP and UDP to
    choose the local port. The first number is the first, the
    second the last local port number.
    If possible, it is better these numbers have different parity.
    (one even and one odd values)
    The default values are 32768 and 60999 respectively.

实验测试上述内容

实际两台主机为

[root@idc-111 ~]# uname -a
Linux idc-111 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:24 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

1, 相同目标 ip 和相同目标端口下的端口数量限制

先设置 ip_local_port_range 的值为非常小的范围:

$ echo "net.ipv4.ip_local_port_range = 61000  61001" >> /etc/sysctl.conf
$ sysctl -p
$ cat /proc/sys/net/ipv4/ip_local_port_range
61000       61001

然后对相同 ip 和端口发送 tcp 请求。创建两个连接,达到最大端口数量限制:

[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18941
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18940
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"

[root@220 ~]# ss -ant |grep 172.16.102.220:600
TIME-WAIT  0      0      172.16.102.220:60001              172.16.102.221:80
TIME-WAIT  0      0      172.16.102.220:60000              172.16.102.221:80

然后再创建第三个连接,此时预期应该会失败,因为超出的端口数量现在:

[root@220 ~]# telnet 172.16.102.221 80
Trying 172.16.102.221...
telnet: connect to address 172.16.102.221: Cannot assign requested address

确实创建第三个连接的时候失败了。然后等了一会超过了 timeout 超时时间,回收之后又可以继续连接了

[root@220 ~]# ss -ant |grep 172.16.102.220
ESTAB      0      0      172.16.102.220:22                 192.168.5.62:53827
[root@220 ~]# telnet 172.16.102.221 80
Trying 172.16.102.221...
Connected to 172.16.102.221.
Escape character is '^]'.
^CConnection closed by foreign host.

2, 相同目标 ip 不同目标端口

下面看看相同目标 ip 不同目标端口是否可以突破这个端口限制:

[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18941
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18940
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.221 443 &
[1] 18942
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.221 443 &
[1] 18943
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"


[root@220 ~]# ss -ant |grep 172.16.102.220:600
TIME-WAIT  0      0      172.16.102.220:60001              172.16.102.221:80
TIME-WAIT  0      0      172.16.102.220:60000              172.16.102.221:80
TIME-WAIT  0      0      172.16.102.220:60001              172.16.102.221:443
TIME-WAIT  0      0      172.16.102.220:60000              172.16.102.221:443

可以看到相同目标 ip 不同目标端口下,每个目标端口都有一个独立的端口限制,即,相同源 ip 的源端口是可以相同的。

按照推测这两个目标端口应该只能创建四个连接,下面试试看:

[root@220 ~]# telnet 172.16.102.221 80
Trying 172.16.102.221...
telnet: connect to address 172.16.102.221: Cannot assign requested address

[root@220 ~]# telnet 172.16.102.221 443
Trying 172.16.102.221...
telnet: connect to address 172.16.102.221: Cannot assign requested address

确实是不能再创建连接了,因为每个目标端口都达到了 ip_local_port_range 的限制。

3,多个目标 ip 相同目标端口

下面看一下多个目标 ip 相同目标端口下的情况:

[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18941
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.221 80 &
[1] 18940
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"

[root@220 ~]# nohup telnet 172.16.102.222 80 &
[1] 18941
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"
[root@220 ~]# nohup telnet 172.16.102.222 80 &
[1] 18940
[root@220 ~]# nohup: 忽略输入并把输出追加到"nohup.out"


[root@220 ~]# ss -ant |grep 172.16.102.220:600
TIME-WAIT  0      0      172.16.102.220:60001              172.16.102.221:80
TIME-WAIT  0      0      172.16.102.220:60000              172.16.102.221:80
TIME-WAIT  0      0      172.16.102.220:60001              172.16.102.222:80
TIME-WAIT  0      0      172.16.102.220:60000              172.16.102.222:80

可以看到,每个目标 ip 都有独立的 ip_local_port_range 限制。

4, 多个目标 ip 不同目标端口

下面看一下多个目标 ip 相同不同端口下的情况,按照前面的经验两个 ip 加两个端口应该只能创建 8 个连接,下面这个自行测试,

处理办法

vim /etc/sysctl.conf
net.ipv4.ip_local_port_range = 10000 65535
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

1,增加可用的端口范围

2,开启重用

3,开启TCP连接中TIME-WAIT sockets的快速回收

4,缩短 timeout 的时间

总结

通过上面的实验发现,昨天发生问题的时候我们属于第一种情况,客户端连接同一台机器的同一个端口。当 tcp timeout 过的时候造成新的连接连接不上,所以出现连接失败的情况;还请排查为什么会出现那么多 timeout建议检查分析代码

上一篇:八边形


下一篇:电竞CSGO数据API接口 - 【联赛列表】API调用示例代码