docker swarm mode 下容器重啓IP引起的 CLOSE_WAIT 問題

問題

問題簡述

以下圖. server docker restart後, client端寫入的日誌丟失, 而且無報錯.
由於不支持時序圖, 把時序圖代碼嵌入在代碼裏.linux

​```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

clipboard.png

問題定位過程

爲何卡在CLOSE_WAIT.

看tcp狀態轉換圖, 能夠看到client收到了fin, 一直沒有recv, 一直卡在CLOSE_WAIT. 和實際的代碼是吻合的.
那麼, 爲何在server docker restart 引起CLOSE_WAIT後, client發消息仍然不報錯呢?
由於:docker

  1. tcp協議容許client在收到fin後, 繼續發送消息.
  2. server 在docker restart後 ip 改變, client仍是往原來的ip發送消息, 沒有主機通知client rst, 致使消息在系統buffer裏積壓.

積壓信息以下:segmentfault

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的.api

iex(client@client.)25> socket |> :inet.port
{:ok, 57395}
iex(client@client.)26> socket |> :gen_tcp.send("aaa")
:ok

若是主動close, 則會進入LAST_ACK狀態socket

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的. 顯然, 應用層心跳是一個解決方案. 那麼, 不使用心跳, 只發不收的狀況下, 何時才能檢測到錯誤呢?tcp

  1. send buffer 滿
  2. tcp keepalive, 默認狀況下須要2小時才能檢測到鏈接錯誤. 見linux keepalive探測對應用層socket api的影響
相關文章
相關標籤/搜索