获得直连地址后,您可以绕过代理,用直连模式访问云数据库Redis集群实例,缩短Redis服务的响应时间。本章节以使用Jedis和PhpRedis客户端为例,介绍了直连访问的注意事项和方法。

前提条件

  • 开通直连访问
  • 将客户端地址加入Redis白名单
  • 使用JedisPhpRedis等支持Redis Cluster的客户端。
    说明
    • 使用不支持Redis Cluster的客户端,可能因客户端无法重定向请求到正确的分片而获取不到需要的数据。
    • Jedis对于Redis Cluster的支持是基于JedisCluster这个类,详细说明请参见Jedis文档
    • 您可以在Redis官网的客户端列表里查找更多支持Redis Cluster的客户端。
  • Redis客户端所在的ECS与目标Redis实例在同一VPC网络。

背景信息

开启直连模式时,云Redis系统会为云Redis集群中所有数据分片的master节点分配一个虚拟IP(VIP)地址。客户端在首次向直连地址发送请求前会通过DNS服务器解析直连地址,解析结果会是集群中一个随机数据分片的VIP。获取到VIP后,客户端即可通过Redis Cluster协议操作云Redis集群中的数据。下图展示了直连模式下Redis集群版的服务架构。
图 1. Redis集群版直连模式服务架构
Redis集群版直连模式服务架构

注意事项

  • 由于部署架构的不同,相对标准架构来说,集群架构的实例在原生Redis命令的支持上有一定的区别(例如不支持SWAPDB命令、Lua存在使用限制等)。更多信息,请参见集群架构实例的命令限制
  • 直连模式下,如果执行变更实例配置,系统会采用Slot(槽)迁移的方式来完成,此场景下,客户端可能因访问到正在迁移的Slot而提示MOVEDTRYAGAIN等错误信息。如需确保请求的成功执行,请为客户端设计重试机制。更多信息,请参见Redis客户端重试指南
  • 直连模式支持使用SELECT命令切换DB,但部分Redis Cluster客户端(例如stackExchange.redis)不支持SELECT命令,如果使用该类客户端则只能使用DB0。
  • 直连地址仅支持通过阿里云内网访问,且同时支持VPC免密账号密码认证

Jedis连接代码

import redis.clients.jedis.HostAndPort;
 import redis.clients.jedis.JedisCluster;
 import redis.clients.jedis.JedisPoolConfig;
 
 import java.util.HashSet;
 import java.util.Set;
  
 public class DirectTest  {
     private static final int DEFAULT_TIMEOUT = 2000;
     private static final int DEFAULT_REDIRECTIONS = 5;
     private static final JedisPoolConfig DEFAULT_CONFIG = new JedisPoolConfig();
 
     public static void main(String args[]){
  
         // 开通直连访问时申请到的直连地址
         String host = "r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com";
         int port = 6379;
         String password = "xxxx";
  
         Set<HostAndPort>  jedisClusterNode = new HashSet<HostAndPort>();
         jedisClusterNode.add(new HostAndPort(host, port));
  
         JedisCluster jc = new JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
                 DEFAULT_REDIRECTIONS,password, "clientName", DEFAULT_CONFIG);
  
         jc.set("key","value");
         jc.get("key");
  
         jc.close();
     }
 }
import redis.clients.jedis.*;
  
 import java.util.HashSet;
 import java.util.Set;
 public class main {
     private static final int DEFAULT_TIMEOUT = 2000;
     private static final int DEFAULT_REDIRECTIONS = 5;
     private static final JedisPoolConfig DEFAULT_CONFIG = new JedisPoolConfig();
 
     public static void main(String args[]){
         JedisPoolConfig config = new JedisPoolConfig();
         // 最大空闲连接数,由于直连模式为客户端直接连接某个数据库分片,需要保证:业务机器数 * MaxTotal < 单个数据库分片的最大连接数。
         // 其中社区版单个分片的最大连接数为10,000,企业版单个分片的最大连接数为30,000。
         config.setMaxTotal(30);
         // 最大空闲连接数, 根据业务需要设置。
         config.setMaxIdle(20);
         config.setTestOnBorrow(false);
         config.setTestOnReturn(false);
 
         // 开通直连访问时申请到的直连地址
         String host = "r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com";
         int port = 6379;
         // 实例的密码
         String password = "xxxxx";
 
         Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
         jedisClusterNode.add(new HostAndPort(host, port));
         JedisCluster jc = new JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
                 DEFAULT_REDIRECTIONS,password, "clientName", config);
     }
 }
说明 Jedis使用说明请参见官方文档

PhpRedis连接代码示例

<?php
 // 直连地址和连接端口
 $array = ['r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com:6379'];
 // 连接密码
 $pwd = "xxxx";
 
 // 使用密码连接集群
 $obj_cluster = new RedisCluster(NULL, $array, 1.5, 1.5, true, $pwd);
 
 // 输出连接结果
 var_dump($obj_cluster);
 
 if ($obj_cluster->set("foo", "bar") == false) {
     die($obj_cluster->getLastError());
 }
 $value = $obj_cluster->get("foo");
 echo $value;
 ?>
说明 PhpRedis使用说明请参见官方文档

Spring Data Redis代码示例

@Bean
     JedisConnectionFactory redisConnectionFactory() {
         List<String> clusterNodes = Arrays.asList("host1:port1", "host2:port2", "host3:port3");
         RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes);
         redisClusterConfiguration.setPassword("xxx");
 
         JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
         // 最大空闲连接数,由于直连模式为客户端直接连接某个数据库分片,需要保证:业务机器数 * MaxTotal < 单个数据库分片的最大连接数。
         // 其中社区版单个分片的最大连接数为10,000,企业版单个分片的最大连接数为30,000。
         jedisPoolConfig.setMaxTotal(30);
         // 最大空闲连接数, 根据业务需要设置。
         jedisPoolConfig.setMaxIdle(20);
         // 关闭 testOn[Borrow|Return],防止产生额外的 PING
         jedisPoolConfig.setTestOnBorrow(false);
         jedisPoolConfig.setTestOnReturn(false);
 
         return new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig);
     }
@Bean
     public LettuceConnectionFactory redisConnectionFactory() {
         List<String> clusterNodes = Arrays.asList("host1:port1", "host2:port2", "host3:port3");
         RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodes);
         redisClusterConfiguration.setPassword("xxx");
 
         ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
             .enablePeriodicRefresh(Duration.ofSeconds(15))                     // 集群拓扑刷新周期,建议为15s,若配置的值太小会产生大量的Cluster Nodes调用,影响性能。
             .dynamicRefreshSources(false)                                      // 是否采用使用Cluster Nodes中获取的IP用来作为集群拓扑刷新的调用节点。连接云数据库Redis实例时,需要配置为false。
             .enableAllAdaptiveRefreshTriggers()                                // 开启集群拓扑刷新,包含遇到MOVED等消息就自动刷新集群一次。
             .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build();   // 为防止集群拓扑刷新频率过高,此参数只允许在对应时间内产生一次拓扑刷新。
 
         LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder().
             clientOptions(ClusterClientOptions.builder()
                 .validateClusterNodeMembership(false)
                 .topologyRefreshOptions(topologyRefreshOptions).build()).build();
 
         return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
     }
说明 Spring Data Redis使用说明请参见Spring

Lettuce连接代码示例

Lettuce支持完整Redis API的同步和异步通信使用。由于Lettuce客户端在请求多次请求超时后,不再自动重连,当云数据库Redis因故障等因素导致代理或者数据库节点发生切换时,可能出现连接超时导致无法重连。为避免此类风险,推荐您使用其他客户端。

public class ClusterDemo {
     public static void main(String[] args) throws Exception {
         String host = "r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com";
         int port = 30001;
         String password = "xxxx";
 
         RedisURI redisURI = RedisURI.Builder.redis(host)
             .withPort(port)
             .withPassword(password)
             .build();
 
         ClusterTopologyRefreshOptions refreshOptions = ClusterTopologyRefreshOptions.builder()
             .enablePeriodicRefresh(Duration.ofSeconds(15))                     // 集群拓扑刷新周期,建议为15s,若配置的值太小会产生大量的Cluster Nodes调用,影响性能。
             .dynamicRefreshSources(false)                                      // 是否采用使用Cluster Nodes中获取的IP用来作为集群拓扑刷新的调用节点。连接云数据库Redis实例时,需要配置为false。
             .enableAllAdaptiveRefreshTriggers()                                // 开启集群拓扑刷新,包含遇到MOVED等消息就自动刷新集群一次。
             .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(15)).build();   // 为防止集群拓扑刷新频率过高,此参数只允许在对应时间内产生一次拓扑刷新。
 
         RedisClusterClient redisClient = RedisClusterClient.create(redisURI);
         redisClient.setOptions(ClusterClientOptions.builder()
             .socketOptions(SocketOptions.builder()
                 .keepAlive(true) // 开启keepAlive参数。
                 .build())
             .validateClusterNodeMembership(false)
             .topologyRefreshOptions(refreshOptions).build());
 
         StatefulRedisClusterConnection<String, String> connection = redisClient.connect();
         connection.sync().set("key", "value");
     }
 }
说明 Lettuce使用说明请参见Lettuce

常见问题

  • Q:通过Lettuce连接Redis时,报错Connection to xxx not allowed. This partition is not known in the cluster view.

    A:请按照上述Lettuce连接代码示例配置refreshOptions选项。