淘淘商城第38讲——使用Jedis操作集群版Redis时,报错:redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException

问题描述

今天碰到一个问题,那就是在使用Jedis操作集群版Redis时,报了一个如下这样一个错误,具体错误信息记录如下:

redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException: Too many Cluster redirections?
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:34)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:67)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:83)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:67)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:83)
	at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:67)
	at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:29)
	at redis.clients.jedis.JedisCluster.get(JedisCluster.java:91)
	at com.taotao.test.jedis.JedisTest.testJedisCluster(JedisTest.java:61)

下面我给出了我的测试代码,你能看懂我写的啥吗?
在这里插入图片描述
这里,我还得说明一点,我使用的Redis是redis-3.2.3.tar.gz这个版本,使用的Jedis是jedis-2.7.2.jar这个版本。

报错原因

报了这个异常之后,我试图想找到报错原因,于是我在网上找了好多的文章。终于,皇天不负有心人,这个报错原因我想我是给找出来了。这还得从Jedis的源码说起。

先从Jedis的源码中找到这个异常,这个异常是在JedisClusterCommand类中,如下所示。

if (redirections <= 0) {
    throw new JedisClusterMaxRedirectionsException("Too many Cluster redirections? key=" + key);
}

当我们在使用Jedis操作集群版Redis时,会使用到JedisCluster类,该类中所有API的调用方式都类似下面这样:

public String set(final String key, final String value) {
    return new JedisClusterCommand<String>(connectionHandler, maxRedirections) {
        @Override
        public String execute(Jedis connection) {
            return connection.set(key, value);
        }
    }.run(key);
}

所以咱们要分析的重点代码应该在JedisClusterCommand这个类里,重要代码如下:

public T run(int keyCount, String... keys) {
    if (keys == null || keys.length == 0) {
        throw new JedisClusterException("No way to dispatch this command to Redis Cluster.");
    }
    if (keys.length > 1) {
        int slot = JedisClusterCRC16.getSlot(keys[0]);
        for (int i = 1; i < keyCount; i++) {
            int nextSlot = JedisClusterCRC16.getSlot(keys[i]);
            if (slot != nextSlot) {
                throw new JedisClusterException("No way to dispatch this command to Redis Cluster "
                        + "because keys have different slots.");
            }
        }
    }

    return runWithRetries(SafeEncoder.encode(keys[0]), this.redirections, false, false);
}

private T runWithRetries(byte[] key, int redirections, boolean tryRandomNode, boolean asking) {
    // 问题应该出自这里,应该是redirections参数的值小于等于0,才导致抛出了下面这个异常
    if (redirections <= 0) {
        JedisClusterMaxRedirectionsException exception = new JedisClusterMaxRedirectionsException(
                "Too many Cluster redirections? key=" + SafeEncoder.encode(key));
        throw exception;
    }

    Jedis connection = null;
    try {
        if (asking) {
            // TODO: Pipeline asking with the original command to make it
            // faster....
            connection = askConnection.get();
            connection.asking();

            // if asking success, reset asking flag
            asking = false;
        } else {
            if (tryRandomNode) {
                connection = connectionHandler.getConnection();
            } else {
                connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16.getSlot(key));
            }
        }

        return execute(connection);
    } catch (JedisConnectionException jce) {
        if (tryRandomNode) {
            // maybe all connection is down
            throw jce;
        }

        // release current connection before recursion
        releaseConnection(connection);
        connection = null;

        // retry with random connection
        return runWithRetries(key, redirections - 1, true, asking);
    } catch (JedisRedirectionException jre) {
        // if MOVED redirection occurred,
        if (jre instanceof JedisMovedDataException) {
            // it rebuilds cluster's slot cache
            // recommended by Redis cluster specification
            this.connectionHandler.renewSlotCache(connection);
        }

        // release current connection before recursion or renewing
        releaseConnection(connection);
        connection = null;

        if (jre instanceof JedisAskDataException) {
            asking = true;
            askConnection.set(this.connectionHandler.getConnectionFromNode(jre.getTargetNode()));
        } else if (jre instanceof JedisMovedDataException) {
        } else {
            throw new JedisClusterException(jre);
        }

        return runWithRetries(key, redirections - 1, false, asking);
    } finally {
        releaseConnection(connection);
    }
}

看了以上runWithRetries方法之后,我们大概是知道报错原因了,就是redirections这个参数搞的鬼!那么redirections参数到底应该怎么理解呢?该参数代表的是节点调转次数,实际上也可以看做是重试次数,也就是说连接集群中某个节点失败后,重连其他节点的次数。

解决方案

初始化JedisCluster时,设定构造JedisCluster对象的maxRedirections参数,如下图所示。
在这里插入图片描述
运行以上测试方法,虽然需要等待很长时间,Eclipse控制台才打印出结果,但是没有报其他连接重试次数太多的异常了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李阿昀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值