哨兵

哨兵至少需要 3 个实例,来保证自己的健壮性

当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方

哨兵(sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossip protocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master

stateDiagram-v2
  state SentinelCluster {
    sentinel1 --> sentinel2: 监控
    sentinel2 --> sentinel1: 监控
    sentinel2 --> sentinel3: 监控
    sentinel3 --> sentinel2: 监控
    sentinel3 --> sentinel1: 监控
    sentinel1 --> sentinel3: 监控
  }
  master --> slave1: 复制
  master --> slave2: 复制
  SentinelCluster --> master: 监控
  SentinelCluster --> slave1: 监控
  SentinelCluster --> slave2: 监控

高可用读写分离:

stateDiagram-v2
  state SentinelCluster {
    sentinel1
    sentinel2
    sentinel3
  }
  state SlaveCluster {
    slave1
    slave2
  }
  master --> slave1: 复制
  master --> slave2: 复制
  SentinelCluster --> master: 监控
  SentinelCluster --> slave1: 监控
  SentinelCluster --> slave2: 监控
  client1 --> SlaveCluster
  client2 --> SlaveCluster
  client3 --> SlaveCluster

原理

三个定时任务:

如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络联系上,就可以认为主从节点断连了。如果发生断连的次数超过了 10 次,就说明这个从库的网络状况不好,不适合作为新主库

sdown与odown:

sentinel领导节点选举:通过raft算法选举出领导节点, 由sentienl领导节点进行故障转移的操作

故障转移

选择一个新主节点:

graph TB
  从节点列表 --> 过滤掉不在线或者网络状况不好的
  过滤掉不在线或者网络状况不好的 --> A{slave-priority最大的}
  A --> |yes| 选择完毕
  A --> |no| B{继续选择}
  B --> 复制偏移量最大的节点
  复制偏移量最大的节点 --> |yes| 选择完毕
  复制偏移量最大的节点 --> |no| runId最大的节点

消息发送

哨兵集群的自动发现通过 pub/sub 系统实现的,每个哨兵都会往 __sentinel__:hello 这个 channel 里发送一个消息,内容是自己的 host、ip 和 runid 还有对这个 master 的监控配置,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在

在哨兵内部,sentinel 内部不同的事件会创建不同的 pub/sub 频道来进行消息的同步

// 通过该方法发送消息
void sentinelEvent(int level, char *type, sentinelRedisInstance *ri, const char *fmt, ...);

if (level != LL_DEBUG) {
    channel = createStringObject(type,strlen(type));
    payload = createStringObject(msg,strlen(msg));
    pubsubPublishMessage(channel,payload,0);
    decrRefCount(channel);
    decrRefCount(payload);
}

// 调用
sentinelEvent(LL_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum);
频道名称 含义
+sdown 哨兵判断主节点主观下线
+odown 哨兵判断主节点客观下线
+new-epoch 当前的纪元被更新,进入新的纪元
+try-failover 达到故障切换的条件,开始故障切换
+failover-state-select-slave 开始要选一个从节点作为主节点
+selected-slave 找到一个合适的从节点作为新的主节点
+failover-end 故障切换成功完成
+switch-master 主节点发生切换,主节点的信息发生替换

数据丢失

解决:拒绝客户端的写请求

哨兵配置

# sentinel.conf
port 26379
sentinel monitor mymaster 172.17.0.5 6379 2

这个配置代表需要监控127.0.0.1:6379这个主节点,2代表判断主节点失败至少需要2个Sentinel节点同意,mymaster是主节点的别名

# 启动哨兵
redis-sentinel sentinel.conf

哨兵的一些配置项:

# 如果超过了down-after-milliseconds配置的时间且没有有效的回复,则判定节点不可达
sentinel down-after-milliseconds <master-name> <times>
# 用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数
sentinel parallel-syncs <master-name> <nums>
# slaveof no one一直失败(例如该从节点此时出现故障),当此过程超过failover-timeout时,则故障转移失败
sentinel failover-timeout <master-name> <times>
# 主节点通信密码
sentinel auth-pass <master-name> <password>
# 当一些警告级别的Sentinel事件发生(指重要事件,例如-sdown:客观下线、-odown:主观下线)时,会触发对应路径的脚本
sentinel notification-script <master-name> <script-path>
# 故障转移结束后,会触发对应路径的脚本
sentinel client-reconfig-script <master-name> <script-path>

动态调整配置:sentinel set 命令

监控多个主节点

sentinel monitor master-business-1 10.10.xx.1 6379 2
sentinel monitor master-business-2 10.16.xx.2 6380 2
stateDiagram-v2
  state SentinelCluster {
    sentinel1
    sentinel2
    sentinel3
  }
  state RedisCluster1 {
    master1 --> slave1
    master1 --> slave2
  }
  state RedisCluster2 {
    master2 --> slave3
    master2 --> slave4
  }
  SentinelCluster --> RedisCluster1: 监控
  SentinelCluster --> RedisCluster2: 监控

部署

运维

节点下线:

节点上线:

API

# 查看所有被监控的主节点状态以及相关的统计信息
sentinel masters
# 查看指定主节点
sentinel master <master name>
# 查看指定主节点的从节点
sentinel slaves <master name>
# 列出指定的主从集群sentinel节点(不包含本节点)
sentinel sentinels <master name>
# 返回指定的主节点地址和端口
sentinel get-master-addr-by-name <master name>
# 对符合<pattern>(通配符风格)主节点的配置进行重置
sentinel reset <pattern>
# 强制对集群进行故障转移
sentinel failover <master name>
# 检测当前可达的Sentinel节点总数是否达到<quorum>的个数
sentinel ckquorum <master name>
# 将Sentinel节点的配置强制刷到磁盘上
sentinel flushconfig
# 取消当前sentinel节点对指定master的监控那个
sentinel remove <master name>
# 监控指定master
sentinel monitor <master name> <ip> <port> <quorum>
# Sentinel节点之间用来交换对主节点是否下线的判断
sentinel is-master-down-by-addr

客户端连接

1)遍历Sentinel节点集合获取一个可用的Sentinel节点

2)通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息

3)验证当前获取的“主节点”是真正的主节点,这样做的是为了防止获取之后主节点又发生了变化

4)保持和Sentinel节点集合的“联系”,时刻获取关于主节点的相关“信息”

Java 客户端:

JedisSentinelPool pool =
    new JedisSentinelPool("mymaster", Set.of("192.168.1.101:26379","192.168.1.101:26380","192.168.1.101:26381"));
Jedis resource = pool.getResource();
System.out.println(resource.ping());
resource.close();