一、相关概念:
- Portal Service:管理界面,从eureka获取admin service地址下发配置,允许不同环境用一套portal
- Config Service:配置读取、推送,服务于client,需要注册到eureka
- Admin Service:配置修改,发布,服务于portal,需要注册到eureka
- portalDB表:用户、权限、角色等数据
- configDB表:配置数据,给Admin和Config Service使用
- Meta Server:Meta Service给eureka做了http包装,Meta Server = config service + eureka放在了同一个进程
- 配置分级:app - env - cluster - namespace
- app对应一个业务项目
- env代表环境,apollo预设好了如开发环境dev、测试环境fat、正式环境pro等
- cluster这个不一定用得到,代表项目某个环境会有多个集群的场景
- namespace代表某个具体配置文件比如application.properties、xxx.properties
二、调用关系(github):
三、部署样例(github):
四、代码部分apollo-core(v2.1.0-SNAPSHOT):
- Core包主要是两个SPI和一个超时策略,SPI一个用于获取Meta Service地址,一个用于获取apollo基础参数配置,这里不用太关注业务,主要是了解SPI和超时策略的用法
- Meta Service发现:获取用户手动配置的meta服务器地址,提供途径有多种见github
- Meta Service内部有一个Eureka,Config Service和Admin Service启动时会注册上去,Client调用Config Service还有Portal调用Admin Service就要从Meta Service获取地址
/**
* meta服务发现的SPI接口
*/
public interface MetaServerProvider extends Ordered {
String getMetaServerAddress(Env targetEnv);
}
// core为各个环境提供服务,所以缓存了各个环境的meta地址
// client包实现只连用户指定环境,只需要唯一的meta地址,后面会写到
public class LegacyMetaServerProvider implements MetaServerProvider {
...
private static final Map<Env, String> domains = new HashMap<>();
...
// 初始化时取各个环境meta地址,存入domains,get方法比较简单略过了
private void initialize() {
Properties prop = new Properties();
prop = ResourceUtils.readConfigFile("apollo-env.properties", prop);
domains.put(Env.LOCAL, getMetaServerAddress(prop, "local_meta", "local.meta"));
domains.put(Env.DEV, getMetaServerAddress(prop, "dev_meta", "dev.meta"));
domains.put(Env.FAT, getMetaServerAddress(prop, "fat_meta", "fat.meta"));
domains.put(Env.UAT, getMetaServerAddress(prop, "uat_meta", "uat.meta"));
domains.put(Env.LPT, getMetaServerAddress(prop, "lpt_meta", "lpt.meta"));
domains.put(Env.PRO, getMetaServerAddress(prop, "pro_meta", "pro.meta"));
}
// 从各个源获找对应数据,顺序如下
private String getMetaServerAddress(Properties prop, String sourceName, String propName) {
// 1\. 从System Property获取
String metaAddress = System.getProperty(sourceName);
if (Strings.isNullOrEmpty(metaAddress)) {
// 2\. 从env获取
metaAddress = System.getenv(sourceName.toUpperCase());
}
if (Strings.isNullOrEmpty(metaAddress)) {
// 3\. 从apollo-env.properties配置文件获取
metaAddress = prop.getProperty(propName);
}
return metaAddress;
}
...
}
/**
* 根据env查询meta服务地址,这里允许用户指定多个meta服务地址用逗号分割
* 每分钟会随机取其中一个模拟负载均衡,比较简单就没贴了,但最好还是nginx
*/
public class MetaDomainConsts {
// 环境 -> meta地址
private static final Map<Env, String> metaServerAddressCache = Maps.newConcurrentMap();
// meta地址 -> 其中某个地址
private static final Map<String, String> selectedMetaServerAddressCache = Maps.newConcurrentMap();
...
// 按SPI顺序获取环境和meta对应地址,存入metaServerAddressCache
private static void initMetaServerAddress(Env env) {
if (metaServerProviders == null) {
synchronized (LOCK) {
if (metaServerProviders == null) {
metaServerProviders = initMetaServerProviders();
}
}
}
String metaAddress = null;
for (MetaServerProvider provider : metaServerProviders) {
metaAddress = provider.getMetaServerAddress(env);
if (!Strings.isNullOrEmpty(metaAddress)) {
logger.info("Located meta server address {} for env {} from {}", metaAddress, env,
provider.getClass().getName());
break;
}
}
if (Strings.isNullOrEmpty(metaAddress)) {
// Fallback to default meta address
metaAddress = DEFAULT_META_URL;
logger.warn(
"Meta server address fallback to {} for env {}, because it is not available in all MetaServerProviders",
metaAddress, env);
}
metaServerAddressCache.put(env, metaAddress.trim());
}
// 根据Order给meta SPI排序
private static List<MetaServerProvider> initMetaServerProviders() {
Iterator<MetaServerProvider> metaServerProviderIterator = ServiceBootstrap
.loadAll(MetaServerProvider.class);
List<MetaServerProvider> metaServerProviders = Lists.newArrayList(metaServerProviderIterator);
metaServerProviders.sort(Comparator.comparingInt(Ordered::getOrder));
return metaServerProviders;
}
...
}
- Provider Manager SPI:和上面类似,Provider Manager可以获取三种配置分别是app、net、server,常见的如appId、env都包括在这,获取配置途径也类似就不贴代码了
- 指数超时策略:很多框架都有类似的策略,通过失败的次数指数性递增超时时间
// 计算超时时间,从0开始递增,每次失败后<< 1直到设定上线,成功后重置,用于Client查询请求重试等
public class ExponentialSchedulePolicy implements SchedulePolicy {
@Override
public long fail() {
long delayTime = lastDelayTime;
if (delayTime == 0) {
delayTime = delayTimeLowerBound;
} else {
delayTime = Math.min(lastDelayTime << 1, delayTimeUpperBound);
}
lastDelayTime = delayTime;
return delayTime;
}
@Override
public void success() {
lastDelayTime = 0;
}
}
五、相关知识点