redis实战第十五篇 redis cluster的批处理中ask重定向解决方案

时间:2022-07-23
本文章向大家介绍redis实战第十五篇 redis cluster的批处理中ask重定向解决方案,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

ask重定向现象请参考【传送门

分别使用mget和pipline做批处理 1.使用mget批量获取,如果存在重定向问题,会抛出异常。

    @Test
    public void testMget(){
        JedisCluster jedis = RedisClusterUtil.getJedis();
        List<String> results = null;
        results = jedis.mget("user:{info}:id","user:{info}:age");
        for(String res:results){
            System.out.println(res);
        }
        results = jedis.mget("user:{info}:id","user:{info}:age","user:{info}:name","user:{info}:email");
        for(String res:results){
            System.out.println(res);
        }
    }

返回结果如下所示,第一次mget执行成功,是因为两个键都迁移完成,第二次获取失败是因为存在ask重定向问题。

232132
20

redis.clients.jedis.exceptions.JedisDataException: TRYAGAIN Multiple keys request during rehashing of slot

2.使用pipline做批量处理

@Test
    public void testPiplione(){
        //创建JedisCluster时,节点地址可以只填写部分,集群内部可以通过cluster nodes获取所有节点信息
        JedisSlotBasedConnectionHandler connectionHandler = new JedisCluster(new HostAndPort("192.168.0.31",6380),1000,1000,5,"1234@abcd",new JedisPoolConfig()){
            public JedisSlotBasedConnectionHandler getConnectionHandler() {
                return (JedisSlotBasedConnectionHandler) super.connectionHandler;
            }
        }.getConnectionHandler();
        List<String> keys = Arrays.asList("user:{info}:id","user:{info}:age","user:{info}:name","user:{info}:email");
        Jedis jedis = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(keys.get(3)));
        try {
            Pipeline pipelined = jedis.pipelined();
            for (String key : keys) {
                pipelined.get(key);
            }
            List<Object> results = pipelined.syncAndReturnAll();
            for (Object result : results) {
                System.out.println(result);
            }
        } finally {
            jedis.close();
        }
    }

批处理结果如下,当存在重定向问题时,pipline不会抛出异常,而是直接返回异常对象,并且成功迁移的键能获取到值。

redis.clients.jedis.exceptions.JedisAskDataException: ASK 5642 192.168.0.33:6380
redis.clients.jedis.exceptions.JedisAskDataException: ASK 5642 192.168.0.33:6380
peter
132132@163.com

基于异常结果对象,可以获取到对应的重定向节点信息,根据获取到的节点信息获取连接再次发送请求。

@Test
    public void testPiplione2(){
        JedisSlotBasedConnectionHandler connectionHandler = new JedisCluster(new HostAndPort("192.168.0.31",6380),1000,1000,5,"1234@abcd",new JedisPoolConfig()){
            public JedisSlotBasedConnectionHandler getConnectionHandler() {
                return (JedisSlotBasedConnectionHandler) super.connectionHandler;
            }
        }.getConnectionHandler();
        List<String> keys = Arrays.asList("user:{info}:id","user:{info}:age","user:{info}:name","user:{info}:email");
        Jedis jedis = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(keys.get(3)));
        try {
            Pipeline pipelined = jedis.pipelined();
            for (String key : keys) {
                pipelined.get(key);
            }
            List<Object> results = pipelined.syncAndReturnAll();
            for (int i =0 ; i<keys.size() ; i++) {
                // 键顺序和结果顺序,Pipeline严格按照键发送的顺序返回结果,即使出现异常也是如此
                Object result = results.get(i);
                //判断是否是异常结果对象
                if (result != null && result instanceof JedisAskDataException) {
                    JedisAskDataException askException = (JedisAskDataException) result;
                    //根据异常结果对象获取节点信息
                    HostAndPort targetNode = askException.getTargetNode();
                    //根据节点信息获取jedis连接
                    Jedis targetJedis = connectionHandler.getConnectionFromNode(targetNode);
                    try {
                        // 执行asking
                        targetJedis.asking();
                        // 获取key并执行
                        String key = keys.get(i);
                        String targetResult = targetJedis.get(key);
                        System.out.println(targetResult);
                    } finally {
                        targetJedis.close();
                    }
                } else {
                    System.out.println(result);
                }
            }
        }finally {
            jedis.close();
        }
    }

以下是执行结果,可以看出pipline可以根据返回的异常结果对象,获取ask重定向节点信息,发送ask请求,获取返回结果,这点和mget不一样,mget出现ask重定向问题时会直接抛出异常。

232132
20
peter
132132@163.com

集群环境下的批量操作场景,建议优先选择pipline,这样不仅可以处理slot迁移过程的ask重定向问题,还可以提高redis的IO效率。