public void doSubscribe(final URL url, final NotifyListener listener) {
   String service = RedisRegistryUtil.toServicePath(url, root);
   RedisRegistryNotifier notifier = notifiers.get(service);
   if (notifier == null) {
     RedisRegistryNotifier newNotifier = new RedisRegistryNotifier(service, this);
     notifiers.putIfAbsent(service, newNotifier);
     notifier = notifiers.get(service);
     if (notifier == newNotifier) {
       notifier.start();
     }
   }
   boolean success = false;
   RpcException exception = null;
   for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
     JedisPool jedisPool = entry.getValue();
     try {
       Jedis jedis = jedisPool.getResource();
       try {
         if (service.endsWith(Constants.ANY_VALUE)) {
           admin = true;
           Set<String> keys = jedis.keys(service);
           if (keys != null && keys.size() > 0) {
             Map<String, Set<String>> serviceKeys = RedisRegistryUtil.getServiceKeys(keys, root);
             for (Set<String> sk : serviceKeys.values()) {
               doNotify(jedis, sk, url, Arrays.asList(listener));
             }
           }
         } else {
           doNotify(
               jedis,
               jedis.keys(service + Constants.PATH_SEPARATOR + Constants.ANY_VALUE),
               url,
               Arrays.asList(listener));
         }
         success = true;
         break; // 只需读一个服务器的数据
       } finally {
         jedisPool.returnResource(jedis);
       }
     } catch (Throwable t) { // 尝试下一个服务器
       exception =
           new RpcException(
               "Failed to subscribe service from redis registry. registry: "
                   + entry.getKey()
                   + ", service: "
                   + url
                   + ", cause: "
                   + t.getMessage(),
               t);
     }
   }
   if (exception != null) {
     if (success) {
       logger.warn(exception.getMessage(), exception);
     } else {
       throw exception;
     }
   }
 }
 private void deferExpired() {
   for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
     JedisPool jedisPool = entry.getValue();
     try {
       Jedis jedis = jedisPool.getResource();
       try {
         RedisRegistryUtil.publishToJedis(jedis, getRegistered(), root, expirePeriod);
         if (admin) {
           clean(jedis);
         }
         if (!replicate) {
           break; //  如果服务器端已同步数据,只需写入单台机器
         }
       } finally {
         jedisPool.returnResource(jedis);
       }
     } catch (Throwable t) {
       logger.warn(
           "Failed to write provider heartbeat to redis registry. registry: "
               + entry.getKey()
               + ", cause: "
               + t.getMessage(),
           t);
     }
   }
 }
  public AbstractRedisRegistry(URL url) {
    super(url);
    RedisRegistryUtil.assertNotAnyHost(url);
    GenericObjectPoolConfig config = RedisRegistryUtil.genericObjectPoolConfig(url);
    List<String> addresses = RedisRegistryUtil.getAddresses(url);

    for (String address : addresses) {
      JedisPool jedisPool = RedisRegistryUtil.initJedisPoolAndCheck(url, config, address);
      jedisPools.put(address, jedisPool);
    }

    this.replicate = RedisRegistryUtil.getReplicate(url);
    this.reconnectPeriod =
        url.getParameter(
            Constants.REGISTRY_RECONNECT_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RECONNECT_PERIOD);
    this.root = RedisRegistryUtil.getGroup(url);
    this.expirePeriod =
        url.getParameter(Constants.SESSION_TIMEOUT_KEY, Constants.DEFAULT_SESSION_TIMEOUT);
    this.expireFuture =
        expireExecutor.scheduleWithFixedDelay(
            new Runnable() {
              public void run() {
                try {
                  deferExpired(); // 延长过期时间
                } catch (Throwable t) { // 防御性容错
                  logger.error(
                      "Unexpected exception occur at defer expire time, cause: " + t.getMessage(),
                      t);
                }
              }
            },
            expirePeriod / 2,
            expirePeriod / 2,
            TimeUnit.MILLISECONDS);
  }
  private void doNotify(
      Jedis jedis, Collection<String> keys, URL url, Collection<NotifyListener> listeners) {
    if (keys == null || keys.size() == 0 || listeners == null || listeners.size() == 0) {
      return;
    }
    long now = System.currentTimeMillis();
    List<URL> result = new ArrayList<URL>();
    List<String> categories =
        Arrays.asList(url.getParameter(Constants.CATEGORY_KEY, new String[0]));
    String consumerService = url.getServiceInterface();
    for (String key : keys) {
      if (!Constants.ANY_VALUE.equals(consumerService)) {
        String prvoiderService = RedisRegistryUtil.toServiceName(key, root);
        if (!prvoiderService.equals(consumerService)) {
          continue;
        }
      }
      String category = RedisRegistryUtil.toCategoryName(key);
      if (!categories.contains(Constants.ANY_VALUE) && !categories.contains(category)) {
        continue;
      }

      Map<String, String> values = jedis.hgetAll(key);
      List<URL> urls = RedisRegistryUtil.getUrlsForDoNotify(url, now, values);

      if (urls.isEmpty()) {
        urls.add(RedisRegistryUtil.setUrlProperties(url, key, category, root));
      }
      result.addAll(urls);
      if (logger.isWarnEnabled()) {
        logger.warn("redis notify: " + key + " = " + urls);
      }
    }
    if (result == null || result.size() == 0) {
      return;
    }
    for (NotifyListener listener : listeners) {
      notify(url, listener, result);
    }
  }
 public void doRegister(URL url) {
   String key = RedisRegistryUtil.toCategoryPath(url, root);
   String value = url.toFullString();
   String expire = String.valueOf(System.currentTimeMillis() + expirePeriod);
   boolean success = false;
   RpcException exception = null;
   for (Map.Entry<String, JedisPool> entry : jedisPools.entrySet()) {
     JedisPool jedisPool = entry.getValue();
     try {
       Jedis jedis = jedisPool.getResource();
       try {
         jedis.hset(key, value, expire);
         jedis.publish(key, Constants.REGISTER);
         success = true;
         if (!replicate) {
           break; //  如果服务器端已同步数据,只需写入单台机器
         }
       } finally {
         jedisPool.returnResource(jedis);
       }
     } catch (Throwable t) {
       exception =
           new RpcException(
               "Failed to register service to redis registry. registry: "
                   + entry.getKey()
                   + ", service: "
                   + url
                   + ", cause: "
                   + t.getMessage(),
               t);
     }
   }
   if (exception != null) {
     if (success) {
       logger.warn(exception.getMessage(), exception);
     } else {
       throw exception;
     }
   }
 }
 // 监控中心负责删除过期脏数据
 private void clean(Jedis jedis) {
   RedisRegistryUtil.clean(jedis, root);
 }