源码解析-Apollo配置读取

​ 注:Apollo版本1.2.0

Apollo配置中心,需要读取这么几个信息,如下,这几个配置是如何以及从哪个文件中读取的

1. app.id

​ app.id配置在/META-INF/app.properties中,maven的resources//META-INF/app.properties,如下是app.properties文件内容:

app.id=apollo-test
my.key=my-value

​ 在DefaultApplicationProvider中,会从该文件中读取:

public void initialize() {
  try {
    InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("/META-INF/app.properties");
    if (in == null) {
      in = DefaultApplicationProvider.class.getResourceAsStream("/META-INF/app.properties");
    }

    this.initialize(in);
  } catch (Throwable var2) {
    Throwable ex = var2;
    logger.error("Initialize DefaultApplicationProvider failed.", ex);
  }

}

public void initialize(InputStream in) {
  try {
    if (in != null) {
      try {
        this.m_appProperties.load(new InputStreamReader(new BOMInputStream(in), StandardCharsets.UTF_8));
      } finally {
        in.close();
      }
    }

    this.initAppId();
  } catch (Throwable var6) {
    Throwable ex = var6;
    logger.error("Initialize DefaultApplicationProvider failed.", ex);
  }
}

...

private void initAppId() {
    this.m_appId = System.getProperty("app.id");
    if (!Utils.isBlank(this.m_appId)) {
        this.m_appId = this.m_appId.trim();
        logger.info("App ID is set to {} by app.id property from System Property", this.m_appId);
    } else {
        this.m_appId = this.m_appProperties.getProperty("app.id");
        if (!Utils.isBlank(this.m_appId)) {
            this.m_appId = this.m_appId.trim();
            logger.info("App ID is set to {} by app.id property from {}", this.m_appId, "/META-INF/app.properties");
        } else {
            this.m_appId = null;
            logger.warn("app.id is not available from System Property and {}. It is set to null", "/META-INF/app.properties");
        }
    }
}
  1. 首先从JDK的system property中获取
  2. 如果步骤1没有获取到,则再从app.properties文件中读取

2. IDC/env/meta地址

​ Apollo配置中心服务端的地址、IDC、env,这几个配置需要在server.properties中配置好,文件路径如下:

#Linux环境
/opt/settings/server.properties
#Windows环境
C:/opt/settings/server.properties

​ 文件内容如下:

IDC=AliCloud-A
ENV=PRO
PRO.meta=http://apollo.xxx.com

​ 这个文件内容是在DefaultServerProvider中读取的,

public void initialize() {
    try {
        String path = Utils.isOSWindows() ? "C:/opt/settings/server.properties" : "/opt/settings/server.properties";
        File file = new File(path);
        if (file.exists() && file.canRead()) {
            logger.info("Loading {}", file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(file);
            this.initialize(fis);
            return;
        }

        this.initialize((InputStream)null);
    } catch (Throwable var4) {
        Throwable ex = var4;
        logger.error("Initialize DefaultServerProvider failed.", ex);
    }

}

public void initialize(InputStream in) {
    try {
        if (in != null) {
            try {
                this.m_serverProperties.load(new InputStreamReader(new BOMInputStream(in), StandardCharsets.UTF_8));
            } finally {
                in.close();
            }
        }

        this.initEnvType();
        this.initDataCenter();
    } catch (Throwable var6) {
        Throwable ex = var6;
        logger.error("Initialize DefaultServerProvider failed.", ex);
    }

}
...
  private void initEnvType() {
  this.m_env = System.getProperty("env");
  if (!Utils.isBlank(this.m_env)) {
    this.m_env = this.m_env.trim();
    logger.info("Environment is set to [{}] by JVM system property 'env'.", this.m_env);
  } else {
    this.m_env = System.getenv("ENV");
    if (!Utils.isBlank(this.m_env)) {
      this.m_env = this.m_env.trim();
      logger.info("Environment is set to [{}] by OS env variable 'ENV'.", this.m_env);
    } else {
      this.m_env = this.m_serverProperties.getProperty("env");
      if (!Utils.isBlank(this.m_env)) {
        this.m_env = this.m_env.trim();
        logger.info("Environment is set to [{}] by property 'env' in server.properties.", this.m_env);
      } else {
        this.m_env = null;
        logger.info("Environment is set to null. Because it is not available in either (1) JVM system property 'env', (2) OS env variable 'ENV' nor (3) property 'env' from the properties InputStream.");
      }
    }
  }
}

private void initDataCenter() {
  this.m_dc = System.getProperty("idc");
  if (!Utils.isBlank(this.m_dc)) {
    this.m_dc = this.m_dc.trim();
    logger.info("Data Center is set to [{}] by JVM system property 'idc'.", this.m_dc);
  } else {
    this.m_dc = System.getenv("IDC");
    if (!Utils.isBlank(this.m_dc)) {
      this.m_dc = this.m_dc.trim();
      logger.info("Data Center is set to [{}] by OS env variable 'IDC'.", this.m_dc);
    } else {
      this.m_dc = this.m_serverProperties.getProperty("idc");
      if (!Utils.isBlank(this.m_dc)) {
        this.m_dc = this.m_dc.trim();
        logger.info("Data Center is set to [{}] by property 'idc' in server.properties.", this.m_dc);
      } else {
        this.m_dc = null;
        logger.debug("Data Center is set to null. Because it is not available in either (1) JVM system property 'idc', (2) OS env variable 'IDC' nor (3) property 'idc' from the properties InputStream.");
      }
    }
  }
}

3.客户端拉取Apollo配置

​ com.ctrip.framework.apollo.internals.ConfigServiceLocator的updateConfigServices()如下,

  1. updateConfigServices()中assembleMetaServiceUrl(),获取Apollo的configServer地址
  2. ConfigUtil#getAppId()方法中,从META-INF/app.properties读取到配置的appId
  3. ConfigUtil#getLocalIp方法中,获取本地的IP地址
  4. 拼接为请求apollo配置的url,即:http://{apollo address}/services/config?appId={appId}&ip={ip}
  5. 最后发起Http请求,获取配置信息
private synchronized void updateConfigServices() {
    String url = this.assembleMetaServiceUrl();
    HttpRequest request = new HttpRequest(url);
    int maxRetries = 2;
    Throwable exception = null;

    for(int i = 0; i < maxRetries; ++i) {
        Transaction transaction = Tracer.newTransaction("Apollo.MetaService", "getConfigService");
        transaction.addData("Url", url);

        label84: {
            try {
                HttpResponse<List<ServiceDTO>> response = this.m_httpUtil.doGet(request, this.m_responseType);
                transaction.setStatus("0");
                List<ServiceDTO> services = (List)response.getBody();
                if (services == null || services.isEmpty()) {
                    this.logConfigService("Empty response!");
                    continue;
                }

                this.setConfigServices(services);
            } catch (Throwable var14) {
                Throwable ex = var14;
                Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
                transaction.setStatus(ex);
                exception = ex;
                break label84;
            } finally {
                transaction.complete();
            }

            return;
        }

        try {
            this.m_configUtil.getOnErrorRetryIntervalTimeUnit().sleep(this.m_configUtil.getOnErrorRetryInterval());
        } catch (InterruptedException var13) {
        }
    }

    throw new ApolloConfigException(String.format("Get config services failed from %s", url), exception);
}

private void setConfigServices(List<ServiceDTO> services) {
    this.m_configServices.set(services);
    this.logConfigServices(services);
}

private String assembleMetaServiceUrl() {
    String domainName = this.m_configUtil.getMetaServerDomainName();
    String appId = this.m_configUtil.getAppId();
    String localIp = this.m_configUtil.getLocalIp();
    Map<String, String> queryParams = Maps.newHashMap();
    queryParams.put("appId", queryParamEscaper.escape(appId));
    if (!Strings.isNullOrEmpty(localIp)) {
        queryParams.put("ip", queryParamEscaper.escape(localIp));
    }

    return domainName + "/services/config?" + MAP_JOINER.join(queryParams);
}

​ com.ctrip.framework.apollo.util.ConfigUtil实现如下:

  1. Foundation.server()中利用SPI找到ServerProvider,即DefaultServerProvider类
  2. 上面已经分析过,DefaultServerProvider中会读取server.properties获取env、IDC、meta地址
public Env getApolloEnv() {
  	//
    return EnvUtils.transformEnv(Foundation.server().getEnvType());
}

public String getLocalIp() {
    return Foundation.net().getHostAddress();
}

public String getMetaServerDomainName() {
    return MetaDomainConsts.getDomain(this.getApolloEnv());
}

​ com.ctrip.framework.apollo.core.MetaDomainConsts实现如下:

  1. 利用SPI获取MetaServerProvider的所有实现者
  2. 排序,将order值小的排在前面
  3. 遍历MetaServerProvider,调用getMetaServerAddress(env),获取到不为空的地址后退出
  4. 如果遍历完后没有获取到apollo.meta地址,则降级为使用http://apollo.meta
public static String getDomain(Env env) {
    String metaServerAddress = getMetaServerAddress(env);
    return metaServerAddress.contains(",") ? selectMetaServerAddress(metaServerAddress) : metaServerAddress;
}

public static String getMetaServerAddress(Env env) {
    if (!metaServerAddressCache.containsKey(env)) {
        initMetaServerAddress(env);
    }

    return (String)metaServerAddressCache.get(env);
}

private static void initMetaServerAddress(Env env) {
    if (metaServerProviders == null) {
        synchronized(LOCK) {
            if (metaServerProviders == null) {
                metaServerProviders = initMetaServerProviders();
            }
        }
    }

    String metaAddress = null;
    Iterator i$ = metaServerProviders.iterator();

    while(i$.hasNext()) {
        MetaServerProvider provider = (MetaServerProvider)i$.next();
        metaAddress = provider.getMetaServerAddress(env);
        if (!Strings.isNullOrEmpty(metaAddress)) {
            logger.info("Located meta server address {} for env {} from {}", new Object[]{metaAddress, env, provider.getClass().getName()});
            break;
        }
    }

    if (Strings.isNullOrEmpty(metaAddress)) {
        metaAddress = "http://apollo.meta";
        logger.warn("Meta server address fallback to {} for env {}, because it is not available in all MetaServerProviders", metaAddress, env);
    }

    metaServerAddressCache.put(env, metaAddress.trim());
}

private static List<MetaServerProvider> initMetaServerProviders() {
    Iterator<MetaServerProvider> metaServerProviderIterator = ServiceBootstrap.loadAll(MetaServerProvider.class);
    List<MetaServerProvider> metaServerProviders = Lists.newArrayList(metaServerProviderIterator);
    Collections.sort(metaServerProviders, new Comparator<MetaServerProvider>() {
        public int compare(MetaServerProvider o1, MetaServerProvider o2) {
            return Integer.compare(o1.getOrder(), o2.getOrder());
        }
    });
    return metaServerProviders;
}

​ MetaServerProvider的实现类是DefaultMetaServerProvider、LegacyMetaServerProvider,执行顺序是DefaultMetaServerProvider->LegacyMetaServerProvider。

​ DefaultMetaServerProvider直接从System property、env中读取apollo.meta,如果有则直接返回

public class DefaultMetaServerProvider implements MetaServerProvider {
    public static final int ORDER = 0;
    private static final Logger logger = LoggerFactory.getLogger(DefaultMetaServerProvider.class);
    private final String metaServerAddress = this.initMetaServerAddress();

    public DefaultMetaServerProvider() {
    }

    private String initMetaServerAddress() {
        String metaAddress = System.getProperty("apollo.meta");
        if (Strings.isNullOrEmpty(metaAddress)) {
            metaAddress = System.getenv("APOLLO_META");
        }

        if (Strings.isNullOrEmpty(metaAddress)) {
            metaAddress = Foundation.server().getProperty("apollo.meta", (String)null);
        }

        if (Strings.isNullOrEmpty(metaAddress)) {
            metaAddress = Foundation.app().getProperty("apollo.meta", (String)null);
        }

        if (Strings.isNullOrEmpty(metaAddress)) {
            logger.warn("Could not find meta server address, because it is not available in neither (1) JVM system property 'apollo.meta', (2) OS env variable 'APOLLO_META' (3) property 'apollo.meta' from server.properties nor (4) property 'apollo.meta' from app.properties");
        } else {
            metaAddress = metaAddress.trim();
            logger.info("Located meta services from apollo.meta configuration: {}!", metaAddress);
        }

        return metaAddress;
    }

    public String getMetaServerAddress(Env targetEnv) {
        return this.metaServerAddress;
    }

    public int getOrder() {
        return 0;
    }
  1. LegacyMetaServerProvider,读取apollo-env.properties的文件内容
  2. getMetaServerAddress方法中,优先从System property、env中获取,如果没有则再从apollo-env.properties中获取内容
public class LegacyMetaServerProvider implements MetaServerProvider {
    public static final int ORDER = 2147483646;
    private static final Map<Env, String> domains = new HashMap();

    public LegacyMetaServerProvider() {
        this.initialize();
    }

    private void initialize() {
        Properties prop = new Properties();
        prop = ResourceUtils.readConfigFile("apollo-env.properties", prop);
        domains.put(Env.LOCAL, this.getMetaServerAddress(prop, "local_meta", "local.meta"));
        domains.put(Env.DEV, this.getMetaServerAddress(prop, "dev_meta", "dev.meta"));
        domains.put(Env.FAT, this.getMetaServerAddress(prop, "fat_meta", "fat.meta"));
        domains.put(Env.UAT, this.getMetaServerAddress(prop, "uat_meta", "uat.meta"));
        domains.put(Env.LPT, this.getMetaServerAddress(prop, "lpt_meta", "lpt.meta"));
        domains.put(Env.PRO, this.getMetaServerAddress(prop, "pro_meta", "pro.meta"));
    }

    private String getMetaServerAddress(Properties prop, String sourceName, String propName) {
        String metaAddress = System.getProperty(sourceName);
        if (Strings.isNullOrEmpty(metaAddress)) {
            metaAddress = System.getenv(sourceName.toUpperCase());
        }

        if (Strings.isNullOrEmpty(metaAddress)) {
            metaAddress = prop.getProperty(propName);
        }

        return metaAddress;
    }

    public String getMetaServerAddress(Env targetEnv) {
        String metaServerAddress = (String)domains.get(targetEnv);
        return metaServerAddress == null ? null : metaServerAddress.trim();
    }