docker swarm mode 下容器重启IP引发的 CLOSE_WAIT 问题
问题
问题简述
如下图. server docker restart后, client端写入的日志丢失, 并且无报错.
因为不支持时序图, 把时序图代码嵌入在代码里.
```sequence client->server: log_data client->server: log_data server->server: docker restart server->client: fin client->server: log_data loss without error ```
tcp state diagram
问题定位过程
为什么卡在CLOSE_WAIT.
看tcp状态转换图, 可以看到client收到了fin, 一直没有recv, 一直卡在CLOSE_WAIT. 和实际的代码是吻合的.
那么, 为什么在server docker restart 引发CLOSE_WAIT后, client发消息仍然不报错呢?
因为:
- tcp协议允许client在收到fin后, 继续发送消息.
- server 在docker restart后 ip 改变, client还是往原来的ip发送消息, 没有主机通知client rst, 导致消息在系统buffer里积压.
积压信息如下:
root@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0 tcp 1 402 10.0.0.186:62281 10.0.0.16:27017 CLOSE_WAIT 4308/server root@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0 tcp 1 70125 10.0.0.186:62281 10.0.0.16:27017 CLOSE_WAIT 4308/server
此时, 在elixir socket接口层面来看, 不管socket的状态, 还是发送, 都是ok的.
iex(client@client.)25> socket |> :inet.port {:ok, 57395} iex(client@client.)26> socket |> :gen_tcp.send("aaa") :ok
如果主动close, 则会进入LAST_ACK状态
iex(client@client.)27> socket |> :gen_tcp.close() :ok
root@9eeaefa7fe57:/# netstat -nap | grep 27017 | grep 10.0.0 tcp 1 70126 10.0.0.186:62281 10.0.0.16:27017 LAST_ACK -
CLOSE_WAIT的恢复
如果代码还是只发不收. 是检测不到CLOSE_WAIT的. 显然, 应用层心跳是一个解决方案. 那么, 不使用心跳, 只发不收的情况下, 什么时候才能检测到错误呢?
- send buffer 满
- tcp keepalive, 默认情况下需要2小时才能检测到连接错误. 见linux keepalive探测对应用层socket api的影响
相关推荐
王道革 2020-11-25
bwyyziq 2020-11-22
pigsmall 2020-11-19
changecan 2020-11-19
helloWorldAndYou 2020-11-16
nginxs 2020-11-14
红石丶 2020-11-13
WanKaShing 2020-11-12
yangkang 2020-11-12
滴水穿石点石成金 2020-11-12
张荣珍 2020-11-12
wuxunanjing 2020-11-11
魅惑青花瓷 2020-11-11
lihongtai 2020-11-09
yangkang 2020-11-09
worldsnow 2020-11-06
MichaelJScofield 2020-11-06
TaoTaoFu 2020-11-06