apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigFileController.java/ConfigFileController/queryConfig
class ConfigFileController:  String queryConfig(ConfigFileOutputFormat outputFormat, String appId, String clusterName,
                     String namespace, String dataCenter, String clientIp, String clientLabel,
                     HttpServletRequest request,
                     HttpServletResponse response) throws IOException {
    //strip out .properties suffix
    namespace = namespaceUtil.filterNamespaceName(namespace);
    //fix the character case issue, such as FX.apollo <-> fx.apollo
    namespace = namespaceUtil.normalizeNamespace(appId, namespace);

    if (Strings.isNullOrEmpty(clientIp)) {
      clientIp = tryToGetClientIp(request);
    }

    //1. check whether this client has gray release rules
    boolean hasGrayReleaseRule = grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp,
        namespace);

    String cacheKey = assembleCacheKey(outputFormat, appId, clusterName, namespace, dataCenter);

    //2. try to load gray release and return
    if (hasGrayReleaseRule) {
      Tracer.logEvent("ConfigFile.Cache.GrayRelease", cacheKey);
      return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp, clientLabel,
          request, response);
    }

    //3. if not gray release, check weather cache exists, if exists, return
    String result = localCache.getIfPresent(cacheKey);

    //4. if not exists, load from ConfigController
    if (Strings.isNullOrEmpty(result)) {
      Tracer.logEvent("ConfigFile.Cache.Miss", cacheKey);
      result = loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp, clientLabel,
          request, response);

      if (result == null) {
        return null;
      }
      //5. Double check if this client needs to load gray release, if yes, load from db again
      //This step is mainly to avoid cache pollution
      if (grayReleaseRulesHolder.hasGrayReleaseRule(appId, clientIp, namespace)) {
        Tracer.logEvent("ConfigFile.Cache.GrayReleaseConflict", cacheKey);
        return loadConfig(outputFormat, appId, clusterName, namespace, dataCenter, clientIp, clientLabel,
            request, response);
      }

      localCache.put(cacheKey, result);
      logger.debug("adding cache for key: {}", cacheKey);

      Set<String> watchedKeys =
          watchKeysUtil.assembleAllWatchKeys(appId, clusterName, namespace, dataCenter);

      for (String watchedKey : watchedKeys) {
        watchedKeys2CacheKey.put(watchedKey, cacheKey);
      }

      cacheKey2WatchedKeys.putAll(cacheKey, watchedKeys);
      logger.debug("added cache for key: {}", cacheKey);
    } else {
      Tracer.logEvent("ConfigFile.Cache.Hit", cacheKey);
    }

    return result;
  }