问题背景
MongoDB云数据库是由3个节点组成的复制集,node3原来是 Primary 节点,因为硬件故障宕机,云数据库高可用模块检测到后,立即进行了主备切换,保证服务正常,node3重启之后重新加入复制集,变为 Hidden 节点,最终的状态如下表所示。
Primary | Secondary | Hidden |
---|---|---|
node1:port1 | node2:port2 | node3:port3 |
宕机引发的问题
node3重新加入后,服务正常,但复制集内部的通信却还有问题。
从node3的 rs.status()看整个复制集,一切正常,说明 node3到 node1、node2发送心跳请求都正常(每个节点周期性向其他节点发送心跳,通过心跳应答来更新其他节点的状态信息)。
当从 node1、node2的 rs.status()看,node3却处于宕机状态,错误如下
{
"_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发送心跳信息是一直失败的,失败的原因是Couldn’t get a connection within the time limit
node1上执行 netstat,发现 node1已经建立了到 node3的连接 (注意这条连接 tcp keepalive timer 是关闭的)
tcp 0 0 node1:58347 node3:port3 ESTABLISHED off (0.00/0/0)
然而从 node3上执行 netstat,这个连接并不存在
也就是说,这个连接是 node3宕机之前建立的连接,因为 tcp keepalive 没有打开,加上 node3异常退出,所以这条连接只是一边断开了,node1一端还一直保留着这条连接。
此时只要 node1往 node3通过这个连接发送心跳数据,就会发现对端已经关闭,但实际上没有发送任何数据,在从连接池获取连接的时候就已经出错了(Couldn’t get a connection within the time limit
),所以心跳并没有发出,事后确定是 MongoDB 的 bug导致,参考SERVER-24058.
解决问题
解决这个问题,最直接的方法就是把 node1、node2的 mongod 进程重启,一切就会恢复正常,但作为云服务应该尽量避免这么做,以减少对用户的影响。猜测只要上述连接只要能在 node1上关闭,node1重新建立连接就能恢复正常,于是尝试来干掉这条 tcp连接。
首先尝试了tcpkill,但并不能满足需求,tcpkill 的工作方式类似于 tcpdump,要在满足条件的连接上抓到数据包才会触发 kill 连接的动作,而上述的连接上已经没有任何的数据发送了。
tcpkill 依赖 libpcap、libnet这2个库,抓包的功能由 libpcap 实现,而kill连接(实际上是往连接上发送 reset 包)由 libnet 实现。libnet 自带的 sample 里包含了一个简单的工具,能往指定连接发包。
# ./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]
利用这个小工具,向上述连接随便发送了一些数据,连接立即被关闭了,node1向 node3新建立了一条连接来发送心跳,一切恢复正常。
为什么关闭tcp keepalive
设计上是应用层会做 keepalive,但实现上的缺陷并没有达到预期的效果,参考SERVER-24711,遇到类似问题的用户请升级到MongoDB-3.2最新版本。
作者简介
张友东,阿里巴巴技术专家,主要关注分布式存储、Nosql数据库等技术领域,先后参与TFS(淘宝分布式文件系统)、Redis云数据库等项目,目前主要从事MongoDB云数据库的研发工作,致力于让开发者用上最好的MongoDB云服务。
评论前必须登录!
注册