哨兵

哨兵至少需要 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.confport 26379sentinel 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 2sentinel 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># 监控指定mastersentinel 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();