美文网首页配置中心Apollo
Apollo(1) 基本概念和Core包

Apollo(1) 基本概念和Core包

作者: Oliver_Li | 来源:发表于2022-12-07 21:24 被阅读0次
一、相关概念:
  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

相关文章

网友评论

    本文标题:Apollo(1) 基本概念和Core包

    本文链接:https://www.haomeiwen.com/subject/yxkofdtx.html