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