第四章 主备切换机制:心跳检测、故障检测、自动切换、手动切换
主备切换,说白了就是让系统在出问题时能自己“换人上岗”。
我做了这么多年信号系统,见过太多因为切换失败导致业务中断的案例。有一次在高铁信号系统项目里,主控挂了,备机死活切不过来——原因特别简单,心跳检测的阈值设得太死了。嗯,今天咱们就把这块彻底讲透。
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 自动切换:让机器自己决策
自动切换是主备架构的核心价值。说白了,就是让系统在无人干预的情况下,自己完成“换人”。
自动切换的流程,我一般这么设计:
- 故障确认:连续 N 次检测到故障,避免误判
- 状态冻结:主节点停止接受新请求
- 数据同步:确保备节点数据是最新的
- 角色切换:备节点升为主,接管服务
- 健康检查:确认新主节点正常工作
这里有个关键点:脑裂问题。
什么是脑裂?就是主备之间网络断了,但两边都以为对方死了,结果两个节点都抢着当主。这在信号系统里是致命的——两个主同时发指令,设备会疯掉。
我解决脑裂的办法是引入仲裁机制:
// 仲裁逻辑
if (canReachPeer() == false) {
// 尝试获取仲裁锁
if (acquireArbitrationLock() == true) {
// 拿到锁,可以升主
becomePrimary();
} else {
// 没拿到锁,保持备机状态
stayStandby();
}
}
记住:自动切换不是越快越好。我见过有人把切换时间压到 1 秒以内,结果网络抖动一次就切一次,业务反而更不稳定。
4.4 手动切换:最后的“保险栓”
自动切换再智能,也得留一手手动操作。为什么?因为有些场景机器判断不了。
比如:
- 主节点硬件有隐患,但还没彻底挂
- 需要做计划内维护,比如升级系统
- 自动切换逻辑本身出了 bug
手动切换的设计,我强调三点:
- 操作要简单:一键切换,别搞复杂流程
- 要有确认步骤:防止误操作
- 要有回退能力:切错了能切回来
我曾经在一个项目里,手动切换按钮藏在三级菜单里。结果故障时运维找了半天才找到——嗯,从那以后我要求手动切换必须放在首页显眼位置。
手动切换的典型实现:
// 手动切换接口
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 切换策略的选择
自动切换和手动切换不是二选一,而是互补关系。
我一般这样配置:
| 场景 | 推荐策略 | 说明 |
|---|---|---|
| 心跳超时 | 自动切换 | 机器判断更及时 |
| 资源异常 | 自动切换 + 人工确认 | 先切,再通知运维确认 |
| 计划维护 | 手动切换 | 人为控制切换时机 |
| 未知故障 | 手动切换 | 避免自动决策出错 |
最后提醒:无论自动还是手动,切换完成后一定要做全链路验证。我见过切换成功了,但新主节点连不上数据库——这种切换等于没切。
好了,主备切换机制就讲到这里。下一章咱们聊聊数据同步,这是保证切换后业务连续性的关键。