一、相關(guān)概念:
- Portal Service:管理界面,從eureka獲取admin service地址下發(fā)配置,允許不同環(huán)境用一套portal
- Config Service:配置讀取、推送狭郑,服務(wù)于client腹暖,需要注冊(cè)到eureka
- Admin Service:配置修改汇在,發(fā)布,服務(wù)于portal脏答,需要注冊(cè)到eureka
- portalDB表:用戶糕殉、權(quán)限、角色等數(shù)據(jù)
- configDB表:配置數(shù)據(jù)殖告,給Admin和Config Service使用
- Meta Server:Meta Service給eureka做了http包裝阿蝶,Meta Server = config service + eureka放在了同一個(gè)進(jìn)程
- 配置分級(jí):app - env - cluster - namespace
- app對(duì)應(yīng)一個(gè)業(yè)務(wù)項(xiàng)目
- env代表環(huán)境,apollo預(yù)設(shè)好了如開(kāi)發(fā)環(huán)境dev黄绩、測(cè)試環(huán)境fat羡洁、正式環(huán)境pro等
- cluster這個(gè)不一定用得到,代表項(xiàng)目某個(gè)環(huán)境會(huì)有多個(gè)集群的場(chǎng)景
- namespace代表某個(gè)具體配置文件比如application.properties爽丹、xxx.properties
二筑煮、調(diào)用關(guān)系(github):
三、部署樣例(github):
四粤蝎、代碼部分apollo-core(v2.1.0-SNAPSHOT):
- Core包主要是兩個(gè)SPI和一個(gè)超時(shí)策略真仲,SPI一個(gè)用于獲取Meta Service地址,一個(gè)用于獲取apollo基礎(chǔ)參數(shù)配置初澎,這里不用太關(guān)注業(yè)務(wù)秸应,主要是了解SPI和超時(shí)策略的用法
- Meta Service發(fā)現(xiàn):獲取用戶手動(dòng)配置的meta服務(wù)器地址,提供途徑有多種見(jiàn)github
- Meta Service內(nèi)部有一個(gè)Eureka,Config Service和Admin Service啟動(dòng)時(shí)會(huì)注冊(cè)上去软啼,Client調(diào)用Config Service還有Portal調(diào)用Admin Service就要從Meta Service獲取地址
/**
* meta服務(wù)發(fā)現(xiàn)的SPI接口
*/
public interface MetaServerProvider extends Ordered {
String getMetaServerAddress(Env targetEnv);
}
// core為各個(gè)環(huán)境提供服務(wù)桑谍,所以緩存了各個(gè)環(huán)境的meta地址
// client包實(shí)現(xiàn)只連用戶指定環(huán)境,只需要唯一的meta地址祸挪,后面會(huì)寫(xiě)到
public class LegacyMetaServerProvider implements MetaServerProvider {
...
private static final Map<Env, String> domains = new HashMap<>();
...
// 初始化時(shí)取各個(gè)環(huán)境meta地址霉囚,存入domains,get方法比較簡(jiǎn)單略過(guò)了
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"));
}
// 從各個(gè)源獲找對(duì)應(yīng)數(shù)據(jù)匕积,順序如下
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;
}
...
}
/**
* 根據(jù)env查詢meta服務(wù)地址盈罐,這里允許用戶指定多個(gè)meta服務(wù)地址用逗號(hào)分割
* 每分鐘會(huì)隨機(jī)取其中一個(gè)模擬負(fù)載均衡,比較簡(jiǎn)單就沒(méi)貼了闪唆,但最好還是nginx
*/
public class MetaDomainConsts {
// 環(huán)境 -> meta地址
private static final Map<Env, String> metaServerAddressCache = Maps.newConcurrentMap();
// meta地址 -> 其中某個(gè)地址
private static final Map<String, String> selectedMetaServerAddressCache = Maps.newConcurrentMap();
...
// 按SPI順序獲取環(huán)境和meta對(duì)應(yīng)地址盅粪,存入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());
}
// 根據(jù)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,常見(jiàn)的如appId帆调、env都包括在這奠骄,獲取配置途徑也類似就不貼代碼了
- 指數(shù)超時(shí)策略:很多框架都有類似的策略,通過(guò)失敗的次數(shù)指數(shù)性遞增超時(shí)時(shí)間
// 計(jì)算超時(shí)時(shí)間番刊,從0開(kāi)始遞增含鳞,每次失敗后<< 1直到設(shè)定上線,成功后重置芹务,用于Client查詢請(qǐng)求重試等
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;
}
}
五蝉绷、相關(guān)知識(shí)點(diǎn)