使用单机模式连接
简介
百度智能云数据库 Redis 集群版与原生 Redis 连接方式完全兼容,支持所有主流 Redis 支持的客户端。本文档将介绍如何使用不同编程语言的主流 SDK 连接到 Redis 集群。
背景信息
云数据库 Redis 采用基于代理的 Redis 集群方案,客户端连接经由负载均衡器连接到 Proxy 上,后续客户端的请求将有 Proxy 负责路由到对应的 Redis 分片上。其结构如下:
集群版中所有的数据被放置在不同的分片上,每个分片由主从节点构成,包含同一份数据的多个副本,配合故障自动切换,可实现服务的高可用,提高数据的安全性。
因此在使用云数据库 Redis 服务时,无论购买的集群版本还是标准版本,均可将其视为单个 Redis 节点,因此可以使用与连接单机版 redis 一样的方法连接云数据库 Redis 集群。
主流客户端示例
redis-cli
$ ./redis-cli -h redis.*******.scs.bj.baidubce.com -p 6379
> AUTH <password> # 使用默认用于 default 认证
go-redis
安装客户端:
github:https://github.com/redis/go-redis
$ go get github.com/redis/go-redis/v8
连接示例代码:
host := "redis.*******.scs.bj.baidubce.com"
port := 6379
password := "*****"
rdb := redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", host, port),
Password: password,
PoolFIFO: true,
})
var ctx = context.Background()
_ = rdb.Set(ctx, "key", "value", 0).Err()
val, _ := rdb.Get(ctx, "key").Result()
if val != "value" {
panic("")
}
如果开启了连接池,需要注意 PoolFIFO 这个参数。这里 PoolFIFO 指定连接池的工作方式,FIFO 意思是 First In First Out,这里 PoolFIFO 默认值为 false,因此在从连接池中拿连接时,默认会拿最近放入的连接。如果 PoolFIFO 保持为默认值,即 false,则会优先使用最近使用过的连接。 但为了负载更加均衡,我们希望能轮流地使用所有的连接,因此这里建议将 PoolFIFO 设置为 true。
如有想要使用 Cluster 模式连接云数据库 Redis 集群,下面是一个例子:
host := "redis.*******.scs.bj.baidubce.com"
port := 6379
password := "*****"
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{fmt.Sprintf("%s:%d", host, port)},
Password: password,
})
var ctx = context.Background()
_ = rdb.Set(ctx, "key", "value", 0).Err()
val, _ := rdb.Get(ctx, "key").Result()
if val != "value" {
panic("")
}
Jedis
安装 Jedis 客户端
在 pom.xml 中添加如下内容:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.0</version>
</dependency>
连接示例代码
String host = "redis.*******.scs.bj.baidubce.com";
int port = 6379;
String password = "*****";
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(300);
config.setTestOnBorrow(false);
config.setTestOnReturn(false);
try (JedisPool pool = new JedisPool(config, host, port, 3000, password)) {
Jedis jedis = pool.getResource();
jedis.set("client", "jedis");
assert jedis.get("client").equals("jedis");
}
catch (Exception e) {
e.printStackTrace();
}
你可以在 JedisPoolConfig 对象上配置连接池的细节:
config.setMaxTotal(10); // 设置最大连接数
config.setTestOnBorrow(false); // 从连接池中拿连接是测试其可用性
config.setTestOnReturn(false); // 放回连接池中是测试其可用性
config.setTestWhileIdle(false); // 连接空闲时测试其可用性
config.setLifo(false); // 设置连接池是否为后进先出
这里 setTestOnBorrowsetTestOnReturnsetTestWhileIdle 用于连接保活,连接池可以快速发现并剔除错误连接。如果高频地从连接池获取连接,开启后集群会增加不少探测流量。通常连接不会无故断开,另外用户在执行命令时也会做异常的检测,所以这里建议设置为 false。
setLifo 用于配置连接池获取连接的策略,和前文描述的 golang 客户端中 PoolFIFO 含义类似,这里建议设置为 false,以保证 Proxy 上流量更均衡。
lettuce
安装 lettuce 客户端
推荐使用最新版本(目前为 6.3.2.RELEASE 以上版本)连接云数据库 Redis 集群。因为旧版本中存在已知问题,如在 lettuce 5.3.2 之前版本响应解析部分存在bug,在 lettuce 5.2 中新建连接是会发送 COMMAND 命令,这会返回几十KB的响应,大量新建连接会严重降低性能。
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.3.2.RELEASE</version>
</dependency>
连接示例代码
String host = "redis.*******.scs.bj.baidubce.com";
int port = 6379;
String password = "*****";
RedisURI uri = RedisURI.builder().withHost(host).withPort(port).withPassword(password).build();
RedisClient client = RedisClient.create(uri);
StatefulRedisConnection<String, String> connection = client.connect();
RedisStringCommands<String, String> sync = connection.sync();
sync.set("client", "lettuce");
String value = sync.get("client");
assert value.equals("lettuce");
使用同步接口:
下面的示例中使用的是同步接口:
RedisStringCommands<String, String> sync = connection.sync();
String value = sync.get("client");
在执行 sync.get 后,lettuce 会将 GET client命令放入后台待发送的命令队列中,并构造一个 future 并等待其完成,此时该线程被阻塞。lettuce 的工作线程收到该请求的响应后,会将结果传递给对应的 future,此前被阻塞在 sync.get("client")上线程可继续执行。
使用异步接口:
使用异步接口,可以在执行命令后不被阻塞地等待,通常是使用回调的方式来处理结果,下面是一个例子:
RedisAsyncCommands<String, String> async = connection.async();
async.set("client", "lettuce");
RedisFuture<String> future = async.get("client");
future.thenRun(new Runnable() {
@Override
public void run() {
try {
System.out.println("Got value: " + future.get());
} catch (Exception e) {
e.printStackTrace();
}
}
});
需要注意的是,这里传递给 thenRun 的回调是在 lettuce 工作线程的 EvenLoop 中执行的,需要保证该回调函数能快速地执行完成,否则将会阻塞事件循环,并影响其他使用该连接的线程。
在 lettuce 中,支持单个链接被多处使用,但部分 Redis 命令(比如 blpop)需要独占连接,使用这类命令时需要使用单独的连接。
使用连接池
因为 lettuce 默认不使用连接池,而是使用单连接发送请求。如果使用云数据库 Redis集群版,集群如果有多个 Proxy 且客户端数量较少,则只有少量的连接连到 Proxy 上,这会出现负载不均的问题,这种场景下可以配置使用连接池,使用方法如下: https://lettuce.io/core/release/reference/#_connection_pooling
redis-py
安装客户端:
$ pip install redis
连接示例代码
import redis
host = "redis.*******.scs.bj.baidubce.com"
port = 6379
password = "*****"
pool = redis.BlockingConnectionPool(host=host,
port=port,
password=password,
max_connections=10,
socket_connect_timeout=0.1,
socket_timeout=2)
client = redis.Redis(connection_pool=pool)
client.set("client", "redis")
assert client.get("client").decode() == "redis"
node-redis
安装客户端:
$ npm install redis
连接示例代码:
import { createClient } from 'redis';
let host = "redis.*******.scs.bj.baidubce.com";
let port = 6379;
let password = "*****";
const client = createClient({
url: `redis://${host}:${port}`,
password: password,
});
client.on('error', err => console.log('Redis Client Error', err));
await client.connect();
await client.set('key', 'value');
const value = await client.get('key');
console.log(value) // "value"
await client.disconnect();
php-redis
示例代码:
<?php
$host = 'redis.*******.scs.bj.baidubce.com';
$port = 8108;
$password = "*****";
$redis = new Redis();
$redis->connect($host, $port);
$redis->auth("12345");
$redis->set("abc", "ABC");
assert($redis->get("abc") == "ABC");
$cluster = new RedisCluster(NULL, Array($host . ':' . $port), 1.5, 1.5, true, $password);
$cluster->set("def", "DEF");
assert($cluster->get("def") == "DEF");
?>
以上示例展示了使用不同编程语言的主流 SDK 连接到 Redis 集群的方式,每个客户端均有大量配置项,上文中无法完全罗列,还请用户参考对应 SDK 的官方文档获取更全面的帮助信息。