Apollo(1) 基本概念和Core包

一、相关概念:
  1. Portal Service:管理界面,从eureka获取admin service地址下发配置,允许不同环境用一套portal
  2. Config Service:配置读取、推送,服务于client,需要注册到eureka
  3. Admin Service:配置修改,发布,服务于portal,需要注册到eureka
  4. portalDB表:用户、权限、角色等数据
  5. configDB表:配置数据,给Admin和Config Service使用
  6. Meta Server:Meta Service给eureka做了http包装,Meta Server = config service + eureka放在了同一个进程
  7. 配置分级:app - env - cluster - namespace
  • app对应一个业务项目
  • env代表环境,apollo预设好了如开发环境dev、测试环境fat、正式环境pro等
  • cluster这个不一定用得到,代表项目某个环境会有多个集群的场景
  • namespace代表某个具体配置文件比如application.properties、xxx.properties
二、调用关系(github):
  1. image
三、部署样例(github):
  1. image
四、代码部分apollo-core(v2.1.0-SNAPSHOT):
  1. Core包主要是两个SPI和一个超时策略,SPI一个用于获取Meta Service地址,一个用于获取apollo基础参数配置,这里不用太关注业务,主要是了解SPI和超时策略的用法
  2. Meta Service发现:获取用户手动配置的meta服务器地址,提供途径有多种见github
  3. 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;
    }
    ...
}
  1. Provider Manager SPI:和上面类似,Provider Manager可以获取三种配置分别是app、net、server,常见的如appId、env都包括在这,获取配置途径也类似就不贴代码了
  2. 指数超时策略:很多框架都有类似的策略,通过失败的次数指数性递增超时时间
// 计算超时时间,从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;
  }
}
五、相关知识点
  • SPI:https://blog.csdn.net/lemon89/article/details/79189475
    • 类似于依赖注入,统一的接口,不同包里的实现通过META-INF/services下的文件配置声明,可加载多个实现按需获取,提升了扩展性可插拔,在apollo里比如core包提供接口和默认实现,各个服务里有具体实现,使用时通过规划优先级获取
  • spring.factory:https://blog.csdn.net/qq_43522770/article/details/117284372
    • spring.factory和SPI很相似,后面的Client包也会用到,SPI是Java机制需要手动Load,spring.factory是spring的机制自动检索加载并成为Bean
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容

  • 配置中心 配置中心简介 说到配置中心, 大家可能都不陌生。我们携程现在用的qconfig, 就是一个典型的配置中心...
    窝牛狂奔阅读 7,944评论 0 3
  • 1.Apollo简介 Apollo 是由携程开发的,开源的分布式配置中心。Apollo支持不同环境,不同集群下的配...
    meicuosjiushiwo阅读 1,712评论 0 1
  • 1.引言 Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配...
    圣杰阅读 1,252评论 0 4
  • Apollo(阿波罗)是携程开源的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,支持配置热发布并实时...
    云时代的运维开发阅读 1,144评论 0 1
  • 传统应用配置问题 静态配置传统应用的配置,都是静态配置,写在配置文件中,运行时无法动态修改,如果修改之后,就需要重...
    一生逍遥一生阅读 3,431评论 0 1