加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

MongoDB一次节点宕机引发的思考

发布时间:2019-11-04 17:40:30 所属栏目:编程 来源:java架构coid
导读:简介 最近一个 MongoDB 集群环境中的某节点异常下电了,导致业务出现了中断,随即又恢复了正常。 通过ELK 告警也监测到了业务报错日志。 运维部对于节点下电的原因进行了排查,发现仅仅是资源分配上的一个失误导致。 在解决了问题之后,大家也对这次中断的

如下是一个PSS(一主两备)架构的副本集,主节点除了与两个备节点执行数据复制之外,三个节点之间还会通过心跳感知彼此的存活。

MongoDB一次节点宕机引发的思考(源码剖析)

一旦主节点发生故障以后,备节点将在某个周期内检测到主节点处于不可达的状态,此后将由其中一个备节点事先发起选举并最终成为新的主节点。 这个检测周期 由electionTimeoutMillis 参数确定,默认是10s。

MongoDB一次节点宕机引发的思考(源码剖析)

接下来,我们通过一些源码看看该机制是如何实现的:

<<来自 MongoDB 3.4源码>>

db/repl/replication_coordinator_impl_heartbeat.cpp

相关方法

  • ReplicationCoordinatorImpl::_startHeartbeats_inlock 启动各成员的心跳
  • ReplicationCoordinatorImpl::_scheduleHeartbeatToTarget 调度任务-(计划)向成员发起心跳
  • ReplicationCoordinatorImpl::_doMemberHeartbeat 执行向成员发起心跳
  • ReplicationCoordinatorImpl::_handleHeartbeatResponse 处理心跳响应
  • ReplicationCoordinatorImpl::_scheduleNextLivenessUpdate_inlock 调度保活状态检查定时器
  • ReplicationCoordinatorImpl::_cancelAndRescheduleElectionTimeout_inlock 取消并重新调度选举超时定时器
  • ReplicationCoordinatorImpl::_startElectSelfIfEligibleV1 发起主动选举

db/repl/topology_coordinator_impl.cpp

相关方法

  • TopologyCoordinatorImpl::prepareHeartbeatRequestV1 构造心跳请求数据
  • TopologyCoordinatorImpl::processHeartbeatResponse 处理心跳响应并构造下一步Action实例

下面这个图,描述了各个方法之间的调用关系

MongoDB一次节点宕机引发的思考(源码剖析)

图-主要关系

心跳的实现

首先,在副本集组建完成之后,节点会通过ReplicationCoordinatorImpl::_startHeartbeats_inlock方法开始向其他成员发送心跳:

  1. void ReplicationCoordinatorImpl::_startHeartbeats_inlock() { 
  2.  const Date_t now = _replExecutor.now(); 
  3.  _seedList.clear(); 
  4.  //获取副本集成员 
  5.  for (int i = 0; i < _rsConfig.getNumMembers(); ++i) { 
  6.  if (i == _selfIndex) { 
  7.  continue; 
  8.  } 
  9.  //向其他成员发送心跳 
  10.  _scheduleHeartbeatToTarget(_rsConfig.getMemberAt(i).getHostAndPort(), i, now); 
  11.  } 
  12.  //仅仅是刷新本地的心跳状态数据 
  13.  _topCoord->restartHeartbeats(); 
  14.  //使用V1的选举协议(3.2之后) 
  15.  if (isV1ElectionProtocol()) { 
  16.  for (auto&& slaveInfo : _slaveInfo) { 
  17.  slaveInfo.lastUpdate = _replExecutor.now(); 
  18.  slaveInfo.down = false; 
  19.  } 
  20.  //调度保活状态检查定时器 
  21.  _scheduleNextLivenessUpdate_inlock(); 
  22.  } 

在获得当前副本集的节点信息后,调用_scheduleHeartbeatToTarget方法对其他成员发送心跳,

这里_scheduleHeartbeatToTarget 的实现比较简单,其真正发起心跳是由 _doMemberHeartbeat 实现的,如下:

  1. void ReplicationCoordinatorImpl::_scheduleHeartbeatToTarget(const HostAndPort& target, 
  2.  int targetIndex, 
  3.  Date_t when) { 
  4.  //执行调度,在某个时间点调用_doMemberHeartbeat 
  5.  _trackHeartbeatHandle( 
  6.  _replExecutor.scheduleWorkAt(when, 
  7.  stdx::bind(&ReplicationCoordinatorImpl::_doMemberHeartbeat, 
  8.  this, 
  9.  stdx::placeholders::_1, 
  10.  target, 
  11.  targetIndex))); 

ReplicationCoordinatorImpl::_doMemberHeartbeat 方法的实现如下:

  1. void ReplicationCoordinatorImpl::_doMemberHeartbeat(ReplicationExecutor::CallbackArgs cbData, 
  2.  const HostAndPort& target, 
  3.  int targetIndex) { 
  4.  LockGuard topoLock(_topoMutex); 
  5.  //取消callback 跟踪 
  6.  _untrackHeartbeatHandle(cbData.myHandle); 
  7.  if (cbData.status == ErrorCodes::CallbackCanceled) { 
  8.  return; 
  9.  } 
  10.  const Date_t now = _replExecutor.now(); 
  11.  BSONObj heartbeatObj; 
  12.  Milliseconds timeout(0); 
  13.  //3.2 以后的版本 
  14.  if (isV1ElectionProtocol()) { 
  15.  const std::pair<ReplSetHeartbeatArgsV1, Milliseconds> hbRequest = 
  16.  _topCoord->prepareHeartbeatRequestV1(now, _settings.ourSetName(), target); 
  17.  //构造请求,设置一个timeout 
  18.  heartbeatObj = hbRequest.first.toBSON(); 
  19.  timeout = hbRequest.second; 
  20.  } else { 
  21.  ... 
  22.  } 
  23.  //构造远程命令 
  24.  const RemoteCommandRequest request( 
  25.  target, "admin", heartbeatObj, BSON(rpc::kReplSetMetadataFieldName << 1), nullptr, timeout); 
  26.  //设置远程命令回调,指向_handleHeartbeatResponse方法 
  27.  const ReplicationExecutor::RemoteCommandCallbackFn callback = 
  28.  stdx::bind(&ReplicationCoordinatorImpl::_handleHeartbeatResponse, 
  29.  this, 
  30.  stdx::placeholders::_1, 
  31.  targetIndex); 
  32.  _trackHeartbeatHandle(_replExecutor.scheduleRemoteCommand(request, callback)); 

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读