Redis cluster使用pipeline

一般解决思路

redis集群有16384个slot,例如有3个节点,那么每个节点可能分配的slot为Node A是0-5500,Node B是5501-11000,Node C是11001-16383。pipeline是要基于某个节点的,所以如果要用pipeline查询某些key的值,那么就需要通过JedisClusterCRC16.getSlot(key)计算key的slot值,通过上面每个节点的slot分布,就知道了哪些key应该在哪些节点上。再获取这个节点的JedisPool就可以使用pipeline进行读写了。实现上面的过程可以有很多种方式,本文将介绍一种也许是代码量最少的一种解决方法。本文基于redis 3.2.9(如何安装redis集群请参考https://www.jianshu.com/p/64d05c4e0ae2)以及


    redis.clients
    jedis
    2.9.0

解决方案

上一节提到的过程,其实在JedisClusterInfoCache对象中都已经帮助开发人员实现了,但是这个对象在JedisClusterConnectionHandler中为protected并没有对外开放,而且通过JedisCluster的API也无法拿到JedisClusterConnectionHandler对象。所以通过下面两个类将这些对象暴露出来,这样使用getJedisPoolFromSlot就可以知道每个key对应的JedisPool了。

class JedisClusterPlus extends JedisCluster {

    public JedisClusterPlus(Set jedisClusterNode, int connectionTimeout, int soTimeout, final GenericObjectPoolConfig poolConfig) {
        super(jedisClusterNode);
        super.connectionHandler = new JedisSlotAdvancedConnectionHandler(jedisClusterNode, poolConfig,
                connectionTimeout, soTimeout);
    }

    public JedisSlotAdvancedConnectionHandler getConnectionHandler() {
        return (JedisSlotAdvancedConnectionHandler)this.connectionHandler;
    }
}
public class JedisSlotAdvancedConnectionHandler extends JedisSlotBasedConnectionHandler{

    public JedisSlotAdvancedConnectionHandler(Set nodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout) {
        super(nodes, poolConfig, connectionTimeout, soTimeout);
    }

    public JedisPool getJedisPoolFromSlot(int slot) {
        JedisPool connectionPool = cache.getSlotPool(slot);
        if (connectionPool != null) {
            // It can't guaranteed to get valid connection because of node
            // assignment
            return connectionPool;
        } else {
            renewSlotCache(); //It's abnormal situation for cluster mode, that we have just nothing for slot, try to rediscover state
            connectionPool = cache.getSlotPool(slot);
            if (connectionPool != null) {
                return connectionPool;
            } else {
                throw new JedisNoReachableClusterNodeException("No reachable node in cluster for slot " + slot);
            }
        }
    }
}

Demo

public class Tester {
    public static void main(String[] args) {
        Set jedisClusterNode = new HashSet<>();
        HostAndPort hostAndPort1 = new HostAndPort("hostA",7000);
        HostAndPort hostAndPort2 = new HostAndPort("hostB",7001);
        HostAndPort hostAndPort3 = new HostAndPort("hostC",7002);
        jedisClusterNode.add(hostAndPort1);
        jedisClusterNode.add(hostAndPort2);
        jedisClusterNode.add(hostAndPort3);

        JedisClusterPlus jedisClusterPlus = new JedisClusterPlus(jedisClusterNode, 2000, 2000, new JedisPoolConfig());
        JedisSlotAdvancedConnectionHandler jedisSlotAdvancedConnectionHandler = jedisClusterPlus.getConnectionHandler();

        String[] testKeys = {"foo","bar","xyz"};

        Map> poolKeys = new HashMap<>();

        for (String key : testKeys) {
            int slot = JedisClusterCRC16.getSlot(key);
            JedisPool jedisPool = jedisSlotAdvancedConnectionHandler.getJedisPoolFromSlot(slot);
            if (poolKeys.keySet().contains(jedisPool)){
                List keys = poolKeys.get(jedisPool);
                keys.add(key);
            }else {
                List keys = new ArrayList<>();
                keys.add(key);
                poolKeys.put(jedisPool, keys);
            }
        }

        for (JedisPool jedisPool : poolKeys.keySet()) {
            Jedis jedis = jedisPool.getResource();
            Pipeline pipeline = jedis.pipelined();

            List keys = poolKeys.get(jedisPool);

            keys.forEach(key ->pipeline.get(key));

            List result = pipeline.syncAndReturnAll();

            System.out.println(result);

            jedis.close();
        }
    }
}

你可能感兴趣的:(Redis cluster使用pipeline)