- 求職 : Linux運維
- 論壇徽章:
- 203
|
問題背景
MongoDB云數(shù)據(jù)庫是由3個節(jié)點組成的復(fù)制集,node3原來是 Primary 節(jié)點,因為硬件故障宕機(jī),云數(shù)據(jù)庫高可用模塊檢測到后,立即進(jìn)行了主備切換,保證服務(wù)正常,node3重啟之后重新加入復(fù)制集,變?yōu)?Hidden 節(jié)點,最終的狀態(tài)如下表所示。
PRIMARY SECONDARY HIDDEN
node1:port1 node2:port2 node3:port3
宕機(jī)引發(fā)的問題
node3重新加入后,服務(wù)正常,但復(fù)制集內(nèi)部的通信卻還有問題。
從node3的 rs.status()看整個復(fù)制集,一切正常,說明 node3到 node1、node2發(fā)送心跳請求都正常(每個節(jié)點周期性向其他節(jié)點發(fā)送心跳,通過心跳應(yīng)答來更新其他節(jié)點的狀態(tài)信息)。
當(dāng)從 node1、node2的 rs.status()看,node3卻處于宕機(jī)狀態(tài),錯誤如下
{
"_id" : 3,
"name" : "node3:port3",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : Timestamp(0, 0),
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2016-07-21T12:30:17.807Z"),
"lastHeartbeatRecv" : ISODate("2016-07-21T12:30:17.544Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Couldn't get a connection within the time limit",
"configVersion" : -1
}
也就是說 node1向 node3發(fā)送心跳信息是一直失敗的,失敗的原因是Couldn’t get a connection within the time limit
node1上執(zhí)行 netstat,發(fā)現(xiàn) node1已經(jīng)建立了到 node3的連接 (注意這條連接 tcp keepalive timer 是關(guān)閉的)
tcp 0 0 node1:58347 node3:port3 ESTABLISHED off (0.00/0/0)
然而從 node3上執(zhí)行 netstat,這個連接并不存在
也就是說,這個連接是 node3宕機(jī)之前建立的連接,因為 tcp keepalive 沒有打開,加上 node3異常退出,所以這條連接只是一邊斷開了,node1一端還一直保留著這條連接。
此時只要 node1往 node3通過這個連接發(fā)送心跳數(shù)據(jù),就會發(fā)現(xiàn)對端已經(jīng)關(guān)閉,但實際上沒有發(fā)送任何數(shù)據(jù),在從連接池獲取連接的時候就已經(jīng)出錯了(Couldn’t get a connection within the time limit
),所以心跳并沒有發(fā)出,事后確定是 MongoDB 的 bug導(dǎo)致,參考SERVER-24058.
解決問題
解決這個問題,最直接的方法就是把 node1、node2的 mongod 進(jìn)程重啟,一切就會恢復(fù)正常,但作為云服務(wù)應(yīng)該盡量避免這么做,以減少對用戶的影響。猜測只要上述連接只要能在 node1上關(guān)閉,node1重新建立連接就能恢復(fù)正常,于是嘗試來干掉這條 tcp連接。
首先嘗試了tcpkill,但并不能滿足需求,tcpkill 的工作方式類似于 tcpdump,要在滿足條件的連接上抓到數(shù)據(jù)包才會觸發(fā) kill 連接的動作,而上述的連接上已經(jīng)沒有任何的數(shù)據(jù)發(fā)送了。
tcpkill 依賴 libpcap、libnet這2個庫,抓包的功能由 libpcap 實現(xiàn),而kill連接(實際上是往連接上發(fā)送 reset 包)由 libnet 實現(xiàn)。libnet 自帶的 sample 里包含了一個簡單的工具,能往指定連接發(fā)包。
# ./libnet/sample/tcp2
libnet 1.1 packet shaping: TCP[raw]
usage: /tmp/libnet/sample/.libs/lt-tcp2 -s source_ip.source_port -d destination_ip.destination_port [-p payload]
利用這個小工具,向上述連接隨便發(fā)送了一些數(shù)據(jù),連接立即被關(guān)閉了,node1向 node3新建立了一條連接來發(fā)送心跳,一切恢復(fù)正常。
為什么關(guān)閉tcp keepalive
設(shè)計上是應(yīng)用層會做 keepalive,但實現(xiàn)上的缺陷并沒有達(dá)到預(yù)期的效果,參考SERVER-24711,遇到類似問題的用戶請升級到MongoDB-3.2最新版本。
|
|