4. 哨兵连接错误码:连接超时(10002)、主节点不可用(10003)、哨兵选举失败(10004)、从节点同步延迟(10005)

在哨兵模式下,后端服务与 Redis 集群的交互链路更长、状态更复杂。客户端不仅需要连接 Redis 主节点,还需要感知哨兵集群的选举状态和从节点的数据一致性。本章聚焦四个核心错误码,分别对应连接超时、主节点不可用、哨兵选举失败和从节点同步延迟,并给出断言验证的实战方案。

4.1 错误码定义与语义

下表定义了四个错误码的数值、语义和典型触发场景,后端断言逻辑必须严格区分这些错误码,不能混用或降级处理。

错误码 语义 典型触发场景 安全合规关注点
10002 连接超时 客户端与哨兵或主节点建立 TCP 连接时,超过配置的 connectTimeout(如 5s)仍未收到 SYN-ACK 超时时间过短可能导致误判;过长则阻塞业务线程,需结合熔断策略
10003 主节点不可用 哨兵标记主节点为 sdown 或 odown,客户端通过哨兵获取主节点地址失败或返回空 必须区分客观下线与主观下线,避免在哨兵选举期间错误返回 10003
10004 哨兵选举失败 哨兵集群在 quorum 超时内未能选出新主节点,或选举轮次超过最大限制 选举期间应返回 10004 而非 10003,防止客户端误认为主节点永久不可用
10005 从节点同步延迟 从节点与主节点的复制偏移量差距超过阈值(如 100MB 或 10s),导致读取到过期数据 强一致性场景下必须拒绝读取;最终一致性场景可降级但需记录审计日志
合规提示: 错误码 10002~10005 属于哨兵模式下的专用错误码,不应与普通 Redis 单机错误码(如 10001 连接拒绝)共用。建议在 API 网关层统一拦截并映射到业务错误码,避免泄露内部拓扑信息。

4.2 错误码断言验证流程

后端在调用哨兵模式下的 Redis 操作时,必须按照以下流程进行断言验证,确保错误码的返回符合预期。

  1. 获取哨兵拓扑:通过哨兵节点获取当前主节点地址和从节点列表。
  2. 建立连接:尝试连接主节点,设置 connectTimeout 为 3s。
  3. 判断连接结果:
    • 若连接超时 → 返回 10002。
    • 若连接被拒绝或主节点地址为空 → 返回 10003。
  4. 检查哨兵状态:若主节点不可用,查询哨兵集群的选举状态:
    • 若选举进行中且未完成 → 返回 10004。
    • 若选举已完成但新主节点仍不可用 → 返回 10003。
  5. 检查从节点延迟:若操作允许读取从节点,检查复制偏移量:
    • 若延迟超过阈值 → 返回 10005。
    • 若延迟在阈值内 → 正常返回数据。
断言验证示例(Java + Jedis):
public RedisResponse getWithSentinelAssert(String key) {
    try {
        // 1. 获取主节点地址
        HostAndPort master = sentinelPool.getCurrentMaster();
        if (master == null) {
            return RedisResponse.error(10003, "主节点不可用");
        }
        // 2. 尝试连接
        try (Jedis jedis = new Jedis(master.getHost(), master.getPort(), 3000)) {
            String value = jedis.get(key);
            return RedisResponse.success(value);
        } catch (JedisConnectionException e) {
            // 3. 判断超时
            if (e.getMessage().contains("timeout")) {
                return RedisResponse.error(10002, "连接超时");
            }
            // 4. 检查哨兵选举状态
            if (sentinelPool.isElectionInProgress()) {
                return RedisResponse.error(10004, "哨兵选举失败");
            }
            return RedisResponse.error(10003, "主节点不可用");
        }
    } catch (Exception e) {
        return RedisResponse.error(10003, "主节点不可用");
    }
}

4.3 哨兵选举失败(10004)与主节点不可用(10003)的区分

这两个错误码最容易混淆。后端必须通过哨兵集群的 is-master-down-by-addr 命令和选举轮次来判断当前状态。

状态 哨兵响应 返回错误码 说明
主节点客观下线,选举未开始 返回 odown,且 leader_epoch 为 0 10003 主节点已确认不可用,但哨兵尚未发起选举
选举进行中,未选出新主 返回 sdown,且 leader_epoch 大于 0 但无新主节点 10004 哨兵正在选举,客户端应等待或重试
选举完成,新主节点已选出 返回新主节点地址,leader_epoch 更新 正常返回 客户端应使用新主节点地址重新连接
选举超时,未达成 quorum 返回 sdown,leader_epoch 回滚 10004 选举失败,客户端应触发降级策略
安全合规风险: 如果后端在选举期间错误地返回 10003,客户端可能会触发主节点切换的熔断逻辑,导致整个集群被标记为不可用。正确的做法是返回 10004,并设置合理的重试间隔(如 1s、2s、4s 指数退避),避免对哨兵集群造成压力。

4.4 从节点同步延迟(10005)的断言验证

从节点同步延迟是哨兵模式下最容易忽略的错误码。后端在读取从节点时,必须验证复制偏移量,否则可能返回过期数据。

验证步骤:

  1. 通过 INFO replication 命令获取从节点的 master_repl_offset 和主节点的 master_repl_offset。
  2. 计算延迟差值:delay = master_offset - slave_offset。
  3. 若 delay > maxReplLag(如 100MB 或 10s),则返回 10005。
  4. 若延迟在阈值内,则允许读取。
断言验证示例(Python + redis-py):
def get_with_replication_check(key, max_lag_bytes=100*1024*1024):
    slave = get_slave_connection()
    master = get_master_connection()
    # 获取偏移量
    slave_info = slave.info('replication')
    master_info = master.info('replication')
    slave_offset = slave_info['slave_repl_offset']
    master_offset = master_info['master_repl_offset']
    lag = master_offset - slave_offset
    if lag > max_lag_bytes:
        return {'code': 10005, 'message': f'从节点同步延迟 {lag} bytes'}
    value = slave.get(key)
    return {'code': 0, 'data': value}

4.5 错误码断言验证架构图

下图展示了后端服务在哨兵模式下进行错误码断言验证的完整流程,涵盖四个错误码的判定路径。

哨兵模式错误码断言验证流程 客户端请求 获取哨兵主节点地址 主节点为空? 返回 10003 尝试连接主节点 连接成功? 是 连接超时? 返回 10002 检查哨兵选举状态 选举进行中? 返回 10004 哨兵选举失败 否 延迟超阈值? 返回 10005 同步延迟

4.6 错误码断言验证清单

以下清单总结了四个错误码的断言验证要点,后端开发人员应将其纳入单元测试和集成测试。

错误码 断言条件 预期行为 测试用例
10002 连接超时异常 返回 10002,不重试 模拟网络延迟 10s,connectTimeout 设为 3s
10003 主节点地址为空或连接被拒绝 返回 10003,触发熔断 停止主节点进程,哨兵标记 sdown
10004 哨兵选举进行中或选举超时 返回 10004,指数退避重试 模拟哨兵选举,quorum 未达成
10005 从节点复制延迟超过阈值 返回 10005,降级或拒绝读取 暂停从节点复制,延迟超过 100MB
最佳实践: 建议在 Redis 客户端封装层统一处理这四个错误码,并记录到审计日志。对于 10004,重试次数建议不超过 3 次,避免对哨兵集群造成压力。对于 10005,强一致性业务(如支付、库存)应直接拒绝读取,最终一致性业务(如用户资料)可降级返回缓存数据。