Redis-jedis客戶端報Too many Cluster redirections異常

jedis客戶端報Too many Cluster redirections異常?很困擾?不知道是什麼問題?請看如下文章,爲你一一解答。java

1.解決方案

  • 暫沒發現比較好的解決方案。

2.環境

  • Redis 3.x Cluster
  • Jedis 2.8
  • Jdk1.8

3.場景

4.問題現象

請求間歇性穿透緩存。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:85)
    at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
    at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
    at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
    at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:85)
    at redis.clients.jedis.JedisClusterCommand.runWithRetries(JedisClusterCommand.java:68)
    at redis.clients.jedis.JedisClusterCommand.run(JedisClusterCommand.java:29)
    at redis.clients.jedis.JedisCluster.set(JedisCluster.java:75)

5.問題緣由

經過分析如下代碼得知錯誤緣由:緩存

private T runWithRetries(byte[] key, int redirections, boolean tryRandomNode, boolean asking) {
    if (redirections <= 0) {
      throw new JedisClusterMaxRedirectionsException("Too many Cluster redirections?");
    }

    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);
    }
  }

當發生JedisConnectionException或者JedisRedirectionException時,會從新調用當前方法。網絡

JedisConnectionException

與該錯誤關係不大。dom

由於該錯誤是:鏈接Redis錯誤,若是鏈接第一個節點失敗,嘗試第二個節點也失敗,會直接推斷成所有節點down掉拋出錯誤,中斷處理。ui

JedisRedirectionException

和該錯誤關係很大。this

對該錯誤處理時,並處理了如下兩個異常:
JedisMovedDataException:節點重置/遷移後,會拋出該異常
JedisAskDataException: 數據遷移,發生asking問題, 獲取asking的Jediscode

今後推斷,發生該問題的緣由爲:ip

  1. 節點主從切換/遷移後,客戶端與redis的slot不一致致使一直重試
  2. asking 一直失敗,當槽點數值分佈在兩個節點上時,容易引發該錯誤

所以,致使該錯誤的緣由可爲:ci

  1. 節點主從切換/遷移後,網絡等各類緣由致使更新slot信息失敗
  2. asking時一直指向同一個節點,致使asking一直失敗(該概率較少?)
相關文章
相關標籤/搜索