问题描述
今天碰到一个问题,那就是在使用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控制台才打印出结果,但是没有报其他连接重试次数太多的异常了。