使用 sentinel 模式连接
简介
百度智能云数据库 Redis 支持基于 sentinel 模式连接,本文档将介绍如何使用 sentinel 模式连接到 云数据库 Redis 集群。
背景信息
使用 sentinel 模式连接集群时,客户端先从 sentinel 上获取 Redis 节点的地址,然后连接 Redis 节点,流程如下:
上图中,客户端先给 sentinel 节点发送 sentinel get-master-by-name 获取到对应 master 的地址,而后连接 master 并执行读写命令。
百度智能云数据库 Redis 单机版和集群版均提供了 sentinel 系列的命令,将自己伪装为一个 sentinel 节点,此前使用 sentinel 模式的客户端,无需变更代码即可接入百度云Redis集群。其原理如下图所示:
上图中 Redis 节点兼任 sentinel 节点的功能,因此在使用 sentinel 模式时接入云数据库 Redis 时,需要明白 sentinel 节点和 Redis 节点实际上是同一个。
主流客户端示例
下面介绍各主流客户端使用 sentinel 模式接入的方式。
go-redis
redis.NewSentinelClient用来创建一个连接到 sentinel 节点的客户端,可以直接给 sentinel 节点发送命令。
sentinel := redis.NewSentinelClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "sentinel-password",
})
sentinel.GetMasterAddrByName(ctx, "master-name")
通常我们需要使用 redis.NewFailoverClient ,这个客户端会从 sentinel 中或许 redis 节点的信息,然后创建连接到 redis 节点的客户端,用户可以直接发送命令读写 redis。
cli := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{"127.0.0.1:26379"},
Password: "redis-password",
SentinelPassword: "sentinel-password",
})
client := cli.Get(ctx, "key")
Jedis
String host = "redis.*******.scs.bj.baidubce.com";
int port = 6379;
String password = "*****";
String masterName = "master-name";
JedisClientConfig clientConfig = DefaultJedisClientConfig.builder().password(password).build();
JedisClientConfig sentinelConfig = DefaultJedisClientConfig.builder().password(password).build();
Set<HostAndPort> sentinels = new HashSet<HostAndPort>();
sentinels.add(HostAndPort.from(host + ":" + Integer.toString(port)));
JedisPoolConfig poolConfig = new JedisPoolConfig();
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels, poolConfig, clientConfig, sentinelConfig);
Jedis jedis = pool.getResource();
jedis.set("foo", "bar");
这里实际是将 redis 实例作为了 sentinel 节点,因此 sentinel 的地址和密码和云数据库 Redis 集群的地址是一样的。 注意:
- JedisSentinelPool 有多个构造函数,如果集群有密码,在传入密码的时候,需要同时传入 sentinel-password 和 password。
- 使用 sentinel 模式时,如果出现如下错误,这是正常的,可以将 JedisClientConfig的 blockingSocketTimeoutMillis设置为一个很大的值即可。
ERROR 2024-05-30 11:31:01,604 redis.clients.jedis.JedisSentinelPool$MasterListener: Lost connection to Sentinel at 127.0.0.1:26379. Sleeping 5000ms and retrying.
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: Read timed out
at redis.clients.jedis.util.RedisInputStream.ensureFill(RedisInputStream.java:251)
at redis.clients.jedis.util.RedisInputStream.readByte(RedisInputStream.java:47)
at redis.clients.jedis.Protocol.process(Protocol.java:135)
at redis.clients.jedis.Protocol.read(Protocol.java:221)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:350)
at redis.clients.jedis.Connection.getUnflushedObject(Connection.java:316)
at redis.clients.jedis.JedisPubSubBase.process(JedisPubSubBase.java:115)
at redis.clients.jedis.JedisPubSubBase.proceed(JedisPubSubBase.java:92)
at redis.clients.jedis.Jedis.subscribe(Jedis.java:7941)
at redis.clients.jedis.JedisSentinelPool$MasterListener.run(JedisSentinelPool.java:377)
这个错误的原因是,sentinel 客户端启动后会发送一个 subscribe 给 redis 实例,但默认阻塞命令的超时时间的 5 秒,而 subscribe 本就需要阻塞等待后端响应,所以每隔 5 秒就会断开一次。而这个 5 秒是由 JedisClientConfig的blockingSocketTimeoutMillis 参数决定的。
redis-py
host = "redis.*******.scs.bj.baidubce.com"
port = 6379
password = "*****"
sentinel_password = password
# 这里 password 是 Redis 节点的密码,sentinel_kwargs 中的 password 是 sentinel 的密码
sentinel = Sentinel(host, port, password=password, sentinel_kwargs={'password': sentinel_password})
若访问不通时,请自查是否漏掉了 sentinel password,形如下面这样的配置是不对的:
sentinel = Sentinel(..., password='123456'})