第四章 主备切换机制:心跳检测、故障检测、自动切换、手动切换

主备切换,说白了就是让系统在出问题时能自己“换人上岗”。

我做了这么多年信号系统,见过太多因为切换失败导致业务中断的案例。有一次在高铁信号系统项目里,主控挂了,备机死活切不过来——原因特别简单,心跳检测的阈值设得太死了。嗯,今天咱们就把这块彻底讲透。

4.1 心跳检测:系统的“脉搏”

心跳检测是主备切换的基础。没有心跳,你根本不知道对方是死是活。

我个人习惯把心跳分为三个层次:

  • 应用层心跳:业务进程是否正常响应
  • 系统层心跳:操作系统是否还活着
  • 硬件层心跳:电源、网卡、磁盘等硬件状态

你想想看,如果只做应用层心跳,系统内核挂了你也发现不了。反过来,只做硬件层心跳,应用进程卡死了你还以为一切正常。

核心原则:三层心跳必须同时存在,缺一不可。

心跳检测的典型实现,我一般用这种模式:

// 伪代码:心跳发送端(主节点)
while (true) {
    sendHeartbeat(peer, "I_AM_ALIVE", timestamp);
    sleep(HEARTBEAT_INTERVAL);  // 通常 1-3 秒
}

// 伪代码:心跳接收端(备节点)
onReceiveHeartbeat() {
    lastHeartbeatTime = now();
}

checkHeartbeatTimeout() {
    if (now() - lastHeartbeatTime > HEARTBEAT_TIMEOUT) {
        // 超时,触发故障处理
        triggerFailover();
    }
}

我的经验:心跳间隔和超时时间的比例,我建议 1:3 到 1:5。比如间隔 1 秒,超时设 3-5 秒。太短容易误判,太长切换太慢。

4.2 故障检测:别被“假死”骗了

故障检测比心跳检测复杂得多。为什么?因为“故障”本身是个模糊概念。

我曾经遇到过一个案例:主节点 CPU 飙到 100%,但心跳还能发出去。备机收到心跳,以为一切正常。结果业务全堵死了。

所以故障检测不能只看心跳,还要看:

  • 业务健康度:请求成功率、响应延迟
  • 资源使用率:CPU、内存、磁盘、网络
  • 关键进程状态:核心进程是否在运行

我常用的故障检测策略是这样的:

检测维度 检测方式 判定条件
心跳超时 定时检查心跳时间戳 超过阈值且无响应
业务探针 模拟请求/响应 连续 N 次失败
资源监控 系统指标采集 CPU > 95% 持续 30 秒
进程守护 进程表检查 关键进程消失

避坑指南:我曾经把故障检测阈值设得太敏感,结果网络抖动一下,备机就抢着切换。切换本身有开销,频繁切换比不切换更可怕。

4.3 自动切换:让机器自己决策

自动切换是主备架构的核心价值。说白了,就是让系统在无人干预的情况下,自己完成“换人”。

自动切换的流程,我一般这么设计:

  1. 故障确认:连续 N 次检测到故障,避免误判
  2. 状态冻结:主节点停止接受新请求
  3. 数据同步:确保备节点数据是最新的
  4. 角色切换:备节点升为主,接管服务
  5. 健康检查:确认新主节点正常工作

这里有个关键点:脑裂问题

什么是脑裂?就是主备之间网络断了,但两边都以为对方死了,结果两个节点都抢着当主。这在信号系统里是致命的——两个主同时发指令,设备会疯掉。

我解决脑裂的办法是引入仲裁机制

// 仲裁逻辑
if (canReachPeer() == false) {
    // 尝试获取仲裁锁
    if (acquireArbitrationLock() == true) {
        // 拿到锁,可以升主
        becomePrimary();
    } else {
        // 没拿到锁,保持备机状态
        stayStandby();
    }
}

记住:自动切换不是越快越好。我见过有人把切换时间压到 1 秒以内,结果网络抖动一次就切一次,业务反而更不稳定。

4.4 手动切换:最后的“保险栓”

自动切换再智能,也得留一手手动操作。为什么?因为有些场景机器判断不了。

比如:

  • 主节点硬件有隐患,但还没彻底挂
  • 需要做计划内维护,比如升级系统
  • 自动切换逻辑本身出了 bug

手动切换的设计,我强调三点:

  1. 操作要简单:一键切换,别搞复杂流程
  2. 要有确认步骤:防止误操作
  3. 要有回退能力:切错了能切回来

我曾经在一个项目里,手动切换按钮藏在三级菜单里。结果故障时运维找了半天才找到——嗯,从那以后我要求手动切换必须放在首页显眼位置。

手动切换的典型实现:

// 手动切换接口
POST /api/manual-failover
{
    "target": "standby-01",      // 目标备机
    "reason": "planned-maintenance",  // 切换原因
    "force": false               // 是否强制切换
}

// 响应
{
    "status": "success",
    "message": "切换完成,新主节点:standby-01",
    "timestamp": "2024-01-15T10:30:00Z"
}

我的建议:手动切换一定要有操作日志。谁切的、什么时候切的、为什么切——这些信息在事后排查时特别重要。

4.5 切换策略的选择

自动切换和手动切换不是二选一,而是互补关系。

我一般这样配置:

场景 推荐策略 说明
心跳超时 自动切换 机器判断更及时
资源异常 自动切换 + 人工确认 先切,再通知运维确认
计划维护 手动切换 人为控制切换时机
未知故障 手动切换 避免自动决策出错

最后提醒:无论自动还是手动,切换完成后一定要做全链路验证。我见过切换成功了,但新主节点连不上数据库——这种切换等于没切。

好了,主备切换机制就讲到这里。下一章咱们聊聊数据同步,这是保证切换后业务连续性的关键。