深入检出 Redis Cluster ③:客户端

1. moved 重定向

概述: 客户端存取键值的时候使用的重定向

  • 命中则直接返回
  • 不命中则返回目标 ip 及对应的槽给客户端,通过 cluster keyslot ${key} 命令可算出 key 对应的槽

-c: cluster 模式执行,出现 moved 不命中的情况可直接跳转到对应节点上执行命令。非 cluster 模式时会出现 moved 错误
image.png

2. ask 重定向

概述: 扩容或缩容中槽在迁移时客户端访问源节点发生的重定向

  1. 客户端向源节点发送键命令后源节点回复 ask 转向
  2. 客户端再向目标节点发送 Asking 命令

3. smart 客户端

原理:
假设有 n 个节点,若随机选择一个结点发送命令,那么命中率为 n/1,有性能问题
smart 客户端先为所有节点创建连接池,再根据 key 获得相应的 slot 进行直连
过程:

  1. 从集群中选一个可运行节点,得到 cluster slots 的映射集,为每个节点创建 JedisPool
  2. 执行键命令

image.png
工厂类:

  • 无需手动归还连接池
  • 使用单例模式
    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
    @Conponent
    public class JedisClusterFactory {
    private JedisCluster jedisCluster;
    private List<String> hostPortList;
    private int timeout;
    private Logger logger = LoggerFactory.getLogger(JedisClusterFactory.class);

    public void init() {
    // 可以设置相关参数
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();

    Set<HostAndPort> nodeSet = new HashSet<HostAndPort>();
    for (String hostPort : hostPortList) {
    String[] arr = hostPort.split(":");
    if (arr.length != 2) {
    continue;
    }
    nodeSet.add(new HosrAndPort(arr[0], Integer.parseInt(arr[1])));
    }

    try {
    jedisCluster = new JedisCluster(nodeSet, timeout, jedisPoolConfig);
    } catch (Exception e) {
    logger.error(e.getMessage(), e);
    }
    }

    public void destroy() {
    if (jedisCluster != null) {
    try {
    jedisCluster.close();
    } catch (IOException e) {
    logger.error(e.getMessage(), e);
    }
    }
    }

    // set and get
    }
    获取所有节点:
    1
    2
    // 获取所有节点的连接池
    Map<String, JedisPool> jedisPoolMap = jedisCluster.getClusterNodes();