Redis集群 — Sentinel

Redis集群 — Sentinel

主从同步的问题

当主节点宕机后,从节点如何跟上?大致需要经历以下步骤:

  1. 手动将从节点设置为主节点
  2. 通知所有应用立即切换新的主节点地址
  3. 通知其他从节点复制新的主节点地址

这些操作都需要人工干预,并且主节点宕机,主从同步被断开,应用无法感知主节点的变化,这势必会导致数据的丢失甚至造成应用方服务不可用。

那么有没有一种解决方案可以实现当主节点断开后,自从切换主节点并更新应用地址,通知所有从节点,实现真正的高可用呢?Sentinel 就是用来解决这种问题的。

Sentinel 概述

Sentinel 能自动完成故障发现和故障转移,通知应用方,实现高可用。可以将 Sentinel 看作是一个 zookeeper 集群(或 eureka 集群),一般由3-5个节点组成。

客户端连接的是 Redis Sentinel 集群,通过 Sentinel 查询主节点的地址,再连接主节点进行数据交互。当 Redis 主节点挂掉后,Sentinel 重新将新主节点地址通知给客户端,从而实现应用无需重启自动完成主节点的切换。

Sentinel 集群并不是用来取代 Redis 主从复制的,而是用来 “监控” Redis 主从复制的各个节点状态的,每个 Sentinel 节点会监控其他 Sentinel 节点和数据节点。

当 Sentinel 集群的多数节点都认为某个 Redis 数据节点不可达时,会重新选举出一个主节点,自动完成故障转移并将变化实时通知应用方,整个过程全自动,有效的解决了 Redis 的高可用问题。

引用《Redis开发与运维》

消息丢失

Redis 主从复制是异步的,意味着当主节点挂掉后,从节点可能没有完全收到同步消息,导致这部分数据丢失。sentinel 不能保证数据完全不丢失,但是可以通过配置使得消息仅可能的少丢失。

min-slaves-to-write 1
min-slaves-max-lag 10

第一个参数:至少存在一个从节点在进行 “正常复制”,否则停止对外写服务,丧失「可用性」

第二个参数:主节点在 10s 内没有收到从节点的信号,标记从节点异常。

实战:搭建 Redis Sentinel 集群

1⃣️ 启动三个数据节点

启动过程参考:主从复制 — 踩坑:从节点连接失败 Error condition on socket for SYNC: Connection refused

启动成功后的 docker instant

三个数据节点

2⃣️启动三个 Sentinel 节点

sentinel 关键配置 redis-sentinel.conf:

sentinel monitor mymaster 172.17.0.2 6379 2         # 1 需手动修改IP
sentinel down-after-milliseconds mymaster 30000     # 2 默认
sentinel parallel-syncs mymaster 1                  # 3 默认
sentinel failover-timeout mymaster 180000           # 4 默认
  • #1:监听一个主数据节点并设置别名,下线该主节点必须要有两个 sentinel 节点同意。
  • #2:别名为 mymaster 的节点在 30s 内无响应将被判定为不可达,选举时作为下线依据。
  • #3:故障转移期间指向新主节点的从节点数,注意:如果使用从节点读取数据时请设置一个较小的数字避免故障转移期间所有读请求不可用。配置较少的值不会导致大量从节点发起复制,而是采用轮询的方式。
  • #4

docker 启动,使用 redis-sentinel 命令或者在 redis-server 携带 --sentinel 参数:

# sentinel 的默认端口是 26379
docker run -d -p 16379:26379 --name sentinel-1 \
-v ~/Learning/redis-learning/redis-sentinel/sentinel1.conf:/usr/local/etc/redis/sentinel.conf \
redis redis-sentinel /usr/local/etc/redis/sentinel.conf

# sentinel2
docker run -d -p 26379:26379 --name sentinel-2 \
-v ~/Learning/redis-learning/redis-sentinel/sentinel2.conf:/usr/local/etc/redis/sentinel.conf \
redis redis-sentinel /usr/local/etc/redis/sentinel.conf

# sentinel3
docker run -d -p 36379:26379 --name sentinel-3 \
-v ~/Learning/redis-learning/redis-sentinel/sentinel3.conf:/usr/local/etc/redis/sentinel.conf \
redis redis-sentinel /usr/local/etc/redis/sentinel.conf

建议编写 docker-comose 文件,一次性启动。

此时的所有数据节点和 sentinel 节点情况如下:

redis sentinel 集群架构

启动成功之后,在 sentinel 节点中使用 redis-cli -h localhost -p 26379 指定端口连接服务端,否则会使用默认 6379 端口。通过 info sentinel 指令查询集群信息。

info sentinel 集群信息

在配置过程中我们并没有添加从节点和其他 sentinel 节点的信息,sentinel 节点会监控所有数据节点和其他 sentinel 节点,从主节点中获取从节点以及其他 sentinel 节点的信息,并在运行过程中自动添加到配置文件中

sentinel known-replica mymaster 172.17.0.4 6379
sentinel known-replica mymaster 172.17.0.3 6379
sentinel known-sentinel mymaster 172.17.0.6 26379 393261959962179fd483cd989ae3d38c02e413e3
sentinel known-sentinel mymaster 172.17.0.7 26379 f547d9e9f388c13fd03fdee1cc857fb784d2e2c2

4⃣️sentinel 常用命令

  • sentinel masters:展示所有被监控的主节点以及相关统计信息
  • sentinel master {mymaster}:展示指定主节点的监控信息以及相关统计信息
  • sentinel slaves {mymaster}:展示指定主节点的所有从节点信息
  • sentinel sentinels {mymaster}:展示监控指定主节点的所有 sentinel 节点信息,不包括当前节点
  • sentinel get-master-addr-by-name {mymaster}:查询当前主节点是哪个
  • sentinel reset {mymaster}:清除指定通配符的主节点相关状态,进行故障转移,重新发现从节点和 sentinel 节点
  • sentinel failover {mymaster}:对指定主节点进行强制故障转移,不和其他 sentinel 节点协商,转移完成之后其他 sentinel 节点自动更新配置。
  • sentinel ckquorum {mymaster}:检查当前可达的 sentinel 节点数是否达到了 quorum 个数,如果没达到将无法完成故障转移,redis 高可用将不可用。
  • sentinel remove {mymaster}:取消当前 sentinel 节点对主节点的监控。
  • sentinel monitor <mastername> <ip> <port> <quorum>:命令的形式运行时添加对主节点的监控。

实战:Java 客户端的 Sentinel 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class RedisSentinelClient {
private static final JedisSentinelPool SENTINEL_POOL;

static {
Set<String> sentinels = new HashSet<>();
sentinels.add("localhost:16379");
sentinels.add("localhost:26379");
sentinels.add("localhost:36379");
// mymaster 是在 sentinel.conf 里面配置的主节点别名
SENTINEL_POOL = new JedisSentinelPool("mymaster", sentinels, new JedisPoolConfig(), 5000);
}

/**
* 注意:部署在 docker 时,sentinel 返回的主节点 IP 是容器 IP,应用无法直接通过容器内的 IP 连接到主节点。
* <p>
* 解决方案:
* 1. 应用和 redis 都属于同一宿主机同一个容器网络,可以直接通过容器IP互联。
* 2. 应用和 redis 容器不在一个宿主机,设置 redis 容器网络模式为 host,应用和访问普通宿主机一样访问。
* 3. 不采用 docker 部署。
*/
public static void main(String[] args) {
HostAndPort currentHostMaster = SENTINEL_POOL.getCurrentHostMaster();
System.out.println("当前主节点:" + currentHostMaster.getHost() + ",当前端口:" + currentHostMaster.getPort());
// try 的自动资源管理,finally 自动 jedis.close()
try (Jedis jedis = SENTINEL_POOL.getResource()) {
ScanParams scanParams = new ScanParams();
scanParams.count(1000);
scanParams.match("*");
String cursor = "0";
do {
ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
cursor = scanResult.getCursor();
List<String> result = scanResult.getResult();
System.out.println("cursor: " + cursor);
System.out.println("result: " + result);
} while (!"0".equalsIgnoreCase(cursor));
} catch (Exception e) {
e.printStackTrace();
}
}
}

Comments