@Override
 public String quit() {
   for (RedisClient slave : slaves) {
     slave.quit();
   }
   return master.quit();
 }
 @Override
 public void close() {
   master.close();
   for (RedisClient slave : slaves) {
     slave.close();
   }
 }
 @Override
 public RedisClient route(String key) {
   if (this.ketamaClients == null || this.ketamaClients.size() == 0) {
     return null;
   }
   long hash = HashAlgorithm.KETAMA_HASH.hash(key);
   RedisClient client = this.getClientByHash(hash);
   int tries = 0;
   while ((client == null || client.isAvailable()) && tries++ < MAX_TRIES) {
     hash = this.nextHash(hash, key, tries);
     client = this.getClientByHash(hash);
   }
   return client;
 }
 /**
  * Construct a new ketamaRouter
  *
  * @param redisClientMap key is redis client, value is weight, must > 0.
  */
 public KetamaRouter(Map<RedisClient, Integer> redisClientMap) {
   this.redisClientMap = redisClientMap;
   ketamaClients = new TreeMap<Long, List<RedisClient>>();
   // build ke
   for (RedisClient client : redisClientMap.keySet()) {
     String hashKey = client.hashKey();
     int numReps = NUM_REPS * redisClientMap.get(client);
     for (int i = 0; i < numReps / 4; i++) {
       byte[] digest = HashAlgorithm.computeMd5(hashKey + "-" + i);
       for (int h = 0; h < 4; h++) {
         long k =
             (long) (digest[3 + h * 4] & 0xFF) << 24
                 | (long) (digest[2 + h * 4] & 0xFF) << 16
                 | (long) (digest[1 + h * 4] & 0xFF) << 8
                 | digest[h * 4] & 0xFF;
         this.getClientList(ketamaClients, k).add(client);
       }
     }
   }
 }
  public MSRedisClient(RedisConnection masterConnection, List<RedisConnection> slaveConnections) {
    this.masterConnection = masterConnection;
    this.slaveConnections = slaveConnections;
    master = new SyncRedisClient(masterConnection);
    virtualSlaves = new CopyOnWriteArrayList<RedisClient>();
    slaves = new LinkedBlockingQueue<RedisClient>(slaveConnections.size());
    StringBuilder builder = new StringBuilder();
    builder.append(String.format("%s|%s", masterConnection.address, masterConnection.db));
    for (final RedisConnection connection : slaveConnections) {
      final RedisClient redisClient = new SyncRedisClient(connection);

      slaves.add(redisClient);
      builder.append(";").append(String.format("%s|%s", connection.address, connection.db));
      // 根据权重将slave node放到虚拟节点中,权重高的放的越多。被访问的次数也就越多
      for (int i = 0; i < connection.weight; i++) {
        virtualSlaves.add(redisClient);
      }
      // redis 连接异常时自动从slave列表中删除
      redisClient.setClosedHandler(
          new AsyncRedisClient.ClosedHandler() {
            @Override
            public void onClosed(AsyncRedisClient client) {
              slaves.remove(redisClient);
              while (virtualSlaves.remove(redisClient)) {}
            }
          });
      // redis 重连成功时重新加入slave列表
      redisClient.setConnectedHandler(
          new AsyncRedisClient.ConnectedHandler() {
            @Override
            public void onConnected(AsyncRedisClient client) {
              slaves.add(redisClient);
              for (int i = 0; i < connection.weight; i++) {
                virtualSlaves.add(redisClient);
              }
            }
          });
    }

    hashKey = builder.toString();
  }
 @Override
 public Long zremRangeByScore(String key, String min, String max) {
   return master.zremRangeByScore(key, min, max);
 }
 @Override
 public Long smove(String source, String destination, Object member) {
   return master.smove(source, destination, member);
 }
 @Override
 public String flushAll() {
   return master.flushAll();
 }
 @Override
 public String scriptFlush() {
   return master.scriptFlush();
 }
 @Override
 public Long scriptExists(String script) {
   return master.scriptExists(script);
 }
 @Override
 public Reply evalSha(String sha1, String[] keys, byte[]... args) {
   return master.evalSha(sha1, keys, args);
 }
 @Override
 public Long zadd(String key, double score, Object member, ZEntity... others) {
   return master.zadd(key, score, member, others);
 }
 @Override
 public Long sunionStore(String destination, String key, String... keys) {
   return master.sunionStore(destination, key, keys);
 }
 @Override
 public List<?> sunion(String key, String... keys) {
   return master.sunion(key, keys);
 }
 @Override
 public List<?> srandomMember(String key, int count) {
   return master.srandomMember(key, count);
 }
 @Override
 public Object srandomMember(String key) {
   return master.srandomMember(key);
 }
 @Override
 public Object spop(String key) {
   return master.spop(key);
 }
 @Override
 public Double zscore(String key, Object member) {
   return master.zscore(key, member);
 }
 @Override
 public Reply eval(String script, String[] keys, byte[]... args) {
   return master.eval(script, keys, args);
 }
 @Override
 public Long zcard(String key) {
   return master.zcard(key);
 }
 @Override
 public String scriptLoad(String script) {
   return master.scriptLoad(script);
 }
 @Override
 public Long zcount(String key, String min, String max) {
   return master.zcount(key, min, max);
 }
 @Override
 public List<Integer> scriptExists(String[] scripts) {
   return master.scriptExists(scripts);
 }
 @Override
 public Double zincrBy(String key, double increment, Object member) {
   return master.zincrBy(key, increment, member);
 }
 @Override
 public String scriptKill() {
   return master.scriptKill();
 }
 @Override
 public Long zunionStore(
     String destination, String[] keys, int[] weights, ZSetAggregate aggregate) {
   return master.zunionStore(destination, keys, weights, aggregate);
 }
 @Override
 public boolean isAvailable() {
   return master.isAvailable();
 }
 @Override
 public Long zremRangeByRank(String key, int start, int stop) {
   return master.zremRangeByRank(key, start, stop);
 }
 @Override
 public Long zrem(String key, Object... members) {
   return master.zrem(key, members);
 }
 @Override
 public Long sisMember(String key, Object member) {
   return master.sisMember(key, member);
 }