Apollo架构篇 - 分布式配置中心Apollo

Posted 等後那场雪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Apollo架构篇 - 分布式配置中心Apollo相关的知识,希望对你有一定的参考价值。

简介

Apollo(阿波罗)是一款可靠的分布式配置管理中心,诞生于携程框架研发部,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。Apollo 采用 CP 架构。

配置中心原理

客户端向服务端发起一个获取配置信息的 Http 请求并将服务端返回的配置信息保存到本地磁盘与缓存中。

之后每隔 5 分钟发起一次获取配置信息的 Http 请求。这是一个 fallback 机制,为了防止推送机制失效导致配置不更新。可以在运行时指定系统参数 apollo.refreshInterval 来覆盖,单位为分钟。一般情况下,服务端都会返回 304 - Not Modified。

服务端接收到请求后,会保持住 60 秒。如果 60 秒之内有客户端关注的配置发生变化,则将客户端请求返回并告知客户端有配置发生变化的 namespace 信息。如果 60 秒之内没有客户端关注的配置发生变化,则返回 Http 状态码 304 给客户端。因为考虑到会有数万客户端向服务端发起长轮询,因此服务端使用了 Spring 的 DeferredResult 来处理客户端的长轮询请求。

客户端向服务端发起获取对应 namespace 的最新配置的 Http 请求。

ReleaseMessage 表的 Message 字段记录 appid + cluster + namespace 三个维度的信息。服务端通过对比客户端传递的 notificationId 与数据库的 ReleaseMessage 表中相同 appid+cluster+namespace 维度下的最大的主键 id 是否相等,来判断命名空间是否有配置发生了变化。

功能

Apollo 支持如下功能:

  • 统一管理不同环境、不同集群的配置

    Apollo 提供了一个统一界面集中式管理不同环境、不同集群、不同命名空间的配置。

    同一份代码部署在不同的集群,可以有不同的配置。

    通过命名空间可以很方便支持多个不同应用共享同一份配置,同时还允许应用对共享配置进行覆盖。

  • 配置修改实时生效(热发布)

    用户在 Apollo 修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序。

  • 版本发布管理

    所有的配置发布都有版本概念,从而可以方便地支持配置的回滚。

  • 灰度发布

    支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有的应用实例。

  • 权限管理、发布审核、操作审计

    应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。

    所有的操作都有审计日志,可以方便地追踪问题。

  • 客户端配置信息监控

    可以在界面上方便地看到配置在哪些实例使用。

  • 提供Java和.Net原生客户端

    提供了Java和.Net原生客户端,方便应用集成。

    支持 Spring Placeholder,Annotation 和 Spring Boot 的 ConfigurationProperties,方便应用使用。

    同时提供了 Http 接口,非 Java 和 .Net 应用也可以方便地使用。

  • 提供开放平台API

    Apollo 自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。不过 Apollo 出于通用性考虑,不会对配置的修改做过多限制,只要符合基本的格式就能保存,不会针对不同的配置值进行针对性的校验。

  • 部署简单

    目前唯一的外部依赖是 mysql,所以部署非常简单。

    Apollo 还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数。

总体设计

Apollo 的总体设计如下:

Config Service 提供配置的读取、推送等功能,服务对象是 Apollo 客户端。

Admin Service 提供配置的修改、发布等功能,服务对象是 Apollo Portal(管理界面)。

Config ServiceAdmin Service 都是多实例、无状态部署,所以需要将自己注册到 Eureka 中并保持心跳。

Meta Server 在 Eureka 之上架了一层,用于封装 Eureka 的服务发现接口。

Client 通过域名访问 Meta Server 获取 Config Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Client 侧会做 load balance、错误重试。

Portal 通过域名访问 Meta Server 获取 Admin Service 服务列表(IP+Port),而后直接通过 IP+Port 访问服务,同时在 Portal 侧会做 load balance、错误重试。

为了简化部署,实际上会把 Config ServiceEurekaMeta Server 三个逻辑角色部署在同一个 jvm 进程中。

核心概念

Apollo 支持四个维度管理 key-value 格式的配置 - application(应用)、environment(环境)、cluster(集群)、namespace(命名空间)。

1、application(应用)

实际使用配置的应用,Apollo 客户端在运行时需要知道当前应用是谁,从而可以去获取对应的配置。

每个应用都需要唯一的身份标识,即 appId。

2、environment(环境)

配置对应的环境,Apollo 客户端在运行时需要知道当前应用处于哪个环境,从而可以去获取应用的配置。

3、cluster(集群)

一个应用下不同实例的分组。比如典型的可以按照数据中心分,把上海机房的应用实例分为一个集群,把北京机房的应用实例分为另一个集群。

对于不同的集群,同一个配置可以有不一样的值。

集群默认是通过读取机器上的配置(server.properties 中的 idc 属性)指定的,不过也支持运行时通过 System Property 指定。

4、namespace(命名空间)

一个应用下不同配置的分组,可以简单把命名空间类比为文件,不同类型的配置存放在不同的文件中。

应用可以直接读取到公共组件的配置。

应用也可以通过继承公共组件的配置来对公共组件的配置做出调整。

Namespace

什么是Namespace

Namespace 是配置项的集合,类似于一个配置文件的概念。

Apollo 在创建项目的时候,都会默认创建一个 application 的 namespace。对于 90% 的应用来说,application 的 namespace 已经满足日常配置的使用场景了。

客户端获取 application namespace 的代码如下:

Config config = ConfigService.getAppConfig();

客户端获取非 application namespace 的代码如下:

Config config = ConfigService.getConfig(namespace);

Namespace的格式

支持 properties、xml、yml、yaml、json 等,默认是 properties。

Namespace的获取权限

Namespace 的获取权限分为两种 - private(私有的)、public(公共的)。

获取权限是相对于 Apollo 客户端而言的。

private权限

private 权限的 namespace,只能被所属的应用获取到。一个应用尝试获取其它应用的 private 权限的 namespace,Apollo 会报 404 异常。

public权限

public 权限的 namespace,能被所有应用获取到。

Namespace的类型

Namespace 的类型有三种:私有类型、公共类型、关联类型(继承类型)。

私有类型

私有类型的 namespace 具有 private 权限。

公共类型

公共类型的 namespace 具有 public 权限。公共类型的 namespace 名称必须全局唯一。

使用场景:

  • 部门级别共享的配置
  • 小组级别共享的配置
  • 几个项目之间共享的配置
  • 中间件客户端的配置

关联类型

关联类型也称为继承类型,具有 private 权限。关联类型的 namespace 继承于指定的公共类型 namespace,用于覆盖公共 namespace 的某些配置。

例如公共的 namespace 有两个配置项

k1 = v1
k2 = v2

然后应用 a 有一个关联类型的 namespace 关联了此公共的 namespace,并且覆盖了配置项 k1,新值为 v3。那么在应用 a 实际运行时,获取到的公共 namespace 的配置为:

k1 = v3
k2 = v2

使用场景:

  • 对于默认的公共配置可以动态调整。

可用性

场景影响降级原因
某台 Config Service 下线无影响Config Service 无状态,客户端重连其它 Config Service
所有 Config Service 下线客户端无法读取最新配置,Portal 无影响客户端重启时,可以读取本地缓存配置文件
某台 Admin Service 下线无影响Admin Service 无状态,Portal 重连其它 Admin Service
所有 Admin Service 下线客户端无影响,Portal 无法更新配置
某台 Portal 下线无影响Portal 域名通过 slb 绑定多台服务器,重试后指向可用的服务器
全部 Portal 下线客户端无影响,Portal 无法更新配置
某个数据中心下线无影响多数据中心部署,数据完全同步,Meta Server/Portal 域名通过 slb 自动切换到其它存活的数据中心

源码分析 - 客户端

一、ApolloApplicationContextInitializer

实现了 ApplicationContextInitializer 接口。ApplicationContextInitializer 参考文章

在 Spring 容器初始化时,ApplicationContextInitializer 接口的所有实现类都会被实例化。在 Spring 容器刷新之前,调用 ApplicationContextInitializer 接口的所有实现类的 initialize 方法,可以对上下文做一些操作。

@Override
public void initialize(ConfigurableApplicationContext context) 
  ConfigurableEnvironment environment = context.getEnvironment();

  // 判断 apollo.bootstrap.enabled 属性值,默认 false
  if (!environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false)) 
    logger.debug("Apollo bootstrap config is not enabled for context , see property: $", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
    return;
  
  logger.debug("Apollo bootstrap config is enabled for context ", context);

  initialize(environment);

接着看下 initialize 的重载方法的内部逻辑。

protected void initialize(ConfigurableEnvironment environment) 

  if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) 
    return;
  

  String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
  logger.debug("Apollo bootstrap namespaces: ", namespaces);
  List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

  CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
  for (String namespace : namespaceList) 
    // 获取指定命名空间的配置信息
    Config config = ConfigService.getConfig(namespace);

    composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
  

  environment.getPropertySources().addFirst(composite);

二、ConfigService

public static Config getConfig(String namespace) 
  return s_instance.getManager().getConfig(namespace);

三、DefaultConfigManager

@Override
public Config getConfig(String namespace) 
  Config config = m_configs.get(namespace);

  if (config == null) 
    synchronized (this) 
      config = m_configs.get(namespace);

      if (config == null) 
        ConfigFactory factory = m_factoryManager.getFactory(namespace);

        config = factory.create(namespace);
        m_configs.put(namespace, config);
      
    
  

  return config;

使用双重检查锁,创建 Config 实例。

四、DefaultConfigFactory

@Override
public Config create(String namespace) 
  ConfigFileFormat format = determineFileFormat(namespace);
  // 如果配置文件是 yaml、yml 格式的
  if (ConfigFileFormat.isPropertiesCompatible(format)) 
    return new DefaultConfig(namespace, createPropertiesCompatibleFileConfigRepository(namespace, format));
  
  return new DefaultConfig(namespace, createLocalConfigRepository(namespace));

createLocalConfigRespository

LocalFileConfigRepository createLocalConfigRepository(String namespace) 
  // 判断环境是否是 LOCAL
  if (m_configUtil.isInLocalMode()) 
    logger.warn(
        "==== Apollo is in local mode! Won't pull configs from remote server for namespace  ! ====",
        namespace);
    return new LocalFileConfigRepository(namespace);
  
  return new LocalFileConfigRepository(namespace, createRemoteConfigRepository(namespace));

createRemoteConfigRepository

RemoteConfigRepository createRemoteConfigRepository(String namespace) 
  return new RemoteConfigRepository(namespace);

接下来分别分析 LocalFileConfigRepository、RemoteConfigRepository。

五、RemoteConfigRepository

先看下 RemoteConfigRepository 构造器方法。

public RemoteConfigRepository(String namespace) 
  m_namespace = namespace;
  m_configCache = new AtomicReference<>();
  m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
  m_httpUtil = ApolloInjector.getInstance(HttpUtil.class);
  m_serviceLocator = ApolloInjector.getInstance(ConfigServiceLocator.class);
  remoteConfigLongPollService = ApolloInjector.getInstance(RemoteConfigLongPollService.class);
  m_longPollServiceDto = new AtomicReference<>();
  m_remoteMessages = new AtomicReference<>();
  m_loadConfigRateLimiter = RateLimiter.create(m_configUtil.getLoadConfigQPS());
  m_configNeedForceRefresh = new AtomicBoolean(true);
  m_loadConfigFailSchedulePolicy = new ExponentialSchedulePolicy(m_configUtil.getOnErrorRetryInterval(),
      m_configUtil.getOnErrorRetryInterval() * 8);
  // 客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中,同时持久化到磁盘中
  // 应用程序可以从客户端获取最新的配置、订阅配置更新通知
  this.trySync();
  // 客户端定时调度处理
  this.schedulePeriodicRefresh();
  // 客户端长轮询处理
  this.scheduleLongPollingRefresh();

1、trySync 方法

首先看下 trySync 方法的处理逻辑。

protected boolean trySync() 
  try 
    sync();
    return true;
   catch (Throwable ex) 
    Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
    logger.warn("Sync config failed, will retry. Repository , reason: ", this.getClass(), ExceptionUtil
            .getDetailMessage(ex));
  
  return false;

进入 sync 方法内部一窥究竟。

@Override
protected synchronized void sync() 
  Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "syncRemoteConfig");

  try 
    // 从 m_configCache 缓存中获取本地配置
    ApolloConfig previous = m_configCache.get();
    // 从服务端加载远程配置
    ApolloConfig current = loadApolloConfig();

    // 如果本地配置与远程配置不一致,即远程配置发生了变化
    if (previous != current) 
      logger.debug("Remote Config refreshed!");
      // 更新 m_configCache 缓存
      m_configCache.set(current);
      // 回调所有 RepositoryChangeListener 的 onRepositoryChange 方法
      this.fireRepositoryChange(m_namespace, this.getConfig());
    

    if (current != null) 
      Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
          current.getReleaseKey());
    

    transaction.setStatus(Transaction.SUCCESS);
   catch (Throwable ex) 
    transaction.setStatus(ex);
    throw ex;
   finally 
    transaction.complete();
  

客户端从 Apollo 服务端获取到应用的最新配置后,会更新本地缓存。

2、schedulePeriodicRefresh 方法

初始延迟 5 分钟,之后每隔 5 分钟重复调度一次 trySync 方法。

private void schedulePeriodicRefresh() 
  logger.debug("Schedule periodic refresh with interval:  ",
      m_configUtil.getRefreshInterval(), m_configUtil.getRefreshIntervalTimeUnit());
  // 默认初始延迟5分钟,之后每隔5分钟重复调度一次
  // 可以通过 apollo.refreshInterval 属性修改默认值
  m_executorService.scheduleAtFixedRate(
      new Runnable() 
        @Override
        public void run() 
          Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
          logger.debug("refresh config for namespace: ", m_namespace);
          trySync();
          Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
        
      , m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
      m_configUtil.getRefreshIntervalTimeUnit());

客户端定时从服务端拉取应用的最新配置。

3、scheduleLongPollingRefresh 方法

客户端向服务端发起长轮询请求。实际上客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。

private void scheduleLongPollingRefresh() 
  // 交给 RemoteConfigLongPollService 处理
  remoteConfigLongPollService.submit(m_namespace, this);

接着看下 RemoteConfigLongPollService 的 submit 方法是如何处理的。

public boolean submit(String namespace, RemoteConfigRepository remoteConfigRepository) 
  // 更新 m_longPollNamespaces 缓存
  boolean added = m_longPollNamespaces.put(namespace, remoteConfigRepository);
  // 更新 m_notifications 缓存
  m_notifications.putIfAbsent(namespace, INIT_NOTIFICATION_ID);
  if (!m_longPollStarted.get()) 
    // 执行 startLongPolling 方法
    startLongPolling();
  
  return added;

接着看下 startLongPolling 方法的处理逻辑。

private void startLongPolling() 
  if (!m_longPollStarted.compareAndSet(false, true)) 
    return;
  
  try 
    final String appId = m_configUtil.getAppId();
    final String cluster = m_configUtil.getCluster();
    final String dataCenter = m_configUtil.getDataCenter();
    final String secret = m_configUtil.getAccessKeySecret();
    // 默认 2000 毫秒
    final long longPollingInitialDelayInMills = m_configUtil.getLongPollingInitialDelayInMills();
    m_longPollingService.submit(new Runnable() 
      @Override
      public void run() 
        if (longPollingInitialDelayInMills > 0) 
          try 
            logger.debug("Long polling will start in  ms.", longPollingInitialDelayInMills);
            TimeUnit.MILLISECONDS.sleep(longPollingInitialDelayInMills);
           catch (InterruptedException e) 
            //ignore
          
        
        // 执行 doLongPollingRefresh 方法
        doLongPollingRefresh(appId, cluster, dataCenter, secret);
      
    );
   catch (Throwable ex) 
    m_longPollStarted.set(false);
    ApolloConfigException exception =
        new ApolloConfigException("Schedule long polling refresh failed", ex);
    Tracer.logError(exception);
    logger.warn(ExceptionUtil.getDetailMessage(exception));
  

接着看下 doLongPollingRefresh 方法的处理逻辑。

private void doLongPollingRefresh(String appId, String cluster, String dataCenter, String secret) 
  final Random random = new Random();
  ServiceDTO lastServiceDto = null;
  while (!m_longPollingStopped.get() && !Thread.currentThread().isInterrupted()) 
    // 限流判断
    if (!m_longPollRateLimiter.tryAcquire(5, TimeUnit.SECONDS)) 
      try 
        TimeUnit.SECONDS.sleep(5);
       catch (InterruptedException e) 
      
    
    Transaction transaction = Tracer.newTransaction("Apollo.ConfigService", "pollNotification");
    String url = null;
    try 
        
                

引言

目中配置文件比较繁杂,而且不同环境的不同配置修改相对频繁,每次发布都需要对应修改配置,如果配置出现错误,需要重新打包发布,时间成本较高,因此需要做统一的分布式配置中心,能做到自动更新配置文件信息,解决以上问题。

常用的分布式配置中心框架:

  • Disconf(依赖于zookpeer)
  • Zookpeer(保证配置文件信息实时更新 -事件通知)
  • diamond(阿里巴巴研发)
  • Nacos(阿里巴巴研发)
  • Apollo阿波罗(携程研发)
  • Redis
  • xxl-conf

大型互联网公司自己内部都有自己独立分布式配置中心、独立RPC、独立分布式各种解决方案。

学习分布式配置中心之前,需要明确以下两个中心,区分好:

  • 注册中心:主要解决rpc服务治理
  • 分布式配置中心:主要解决分布式配置文件管理

本文主要以Apollo分布式配置中心为主题来讲解。

1.Apollo简介

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

1.1 Apollo特点

  • 统一管理不同环境、不同集群的配置: 所有的配置发布都有版本概念,从而可以方便的支持配置的回滚。
  • 配置修改实时生效(热发布): 用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序
  • 灰度发布:支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例。
  • 权限管理、发布审核、操作审计: 应用和配置的管理都有完善的权限管理机制,对配置的管理还分为了编辑和发布两个环节,从而减少人为的错误。所有的操作都有审计日志,可以方便的追踪问题。
  • Apollo还提供了Java和.Net的原生客户端,方便应用集成支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便应用使用(需要Spring 3.1.1+)同时提供了Http接口,非Java和.Net应用也可以方便的使用。
  • 提供开放平台API:Apollo自身提供了比较完善的统一配置管理界面,支持多环境、多数据中心配置管理、权限、流程治理等特性。不过Apollo出于通用性考虑,对配置的修改不会做过多限制,只要符合基本的格式就能够保存。对于有些使用方,它们的配置可能会有比较复杂的格式,如xml, json,需要对格式做校验。还有一些使用方如DAL,不仅有特定的格式,而且对输入的值也需要进行校验后方可保存,如检查数据库、用户名和密码是否匹配。对于这类应用,Apollo支持应用方通过开放接口在Apollo进行配置的修改和发布,并且具备完善的授权和权限控制。
  • 部署简单 :配置中心作为基础服务,可用性要求非常高,这就要求Apollo对外部依赖尽可能地少,目前唯一的外部依赖是MySQL,所以部署非常简单,只要安装好Java和MySQL就可以让Apollo跑起来,Apollo还提供了打包脚本,一键就可以生成所有需要的安装包,并且支持自定义运行时参数。

1.2 Apollo整体架构原理

2.Apollo配置中心搭建

Apollo配置中心的搭建分为两步:Apollo数据库设计和Apollo服务的搭建。

2.1 版本要求

Java

Apollo服务端:1.8+
Apollo客户端:1.7+

MySQL

版本要求:5.6.5+
虚拟机内存:必须是2G以上,不然启动不了项目

Apollo的表结构对timestamp使用了多个default声明,所以需要5.6.5以上版本。

2.2 Apollo数据库设计

Apollo服务端共需要两个数据库:ApolloPortalDBApolloConfigDB,我们把数据库、表的创建和样例数据都分别准备了sql文件,只需要导入数据库即可。

ApolloPortalDB下载地址(apolloportaldb.sql):https://github.com/nobodyiam/apollo-build-scripts/blob/master/sql/apolloportaldb.sql

ApolloConfigDB下载地址(apolloconfigdb.sql):https://github.com/nobodyiam/apollo-build-scripts/blob/master/sql/apolloconfigdb.sql


调整ApolloPortalDB.ServerConfig表:

字段解析,可以根据实际情况设置:

  1. apollo.portal.envs - 可支持的环境列表:默认值是dev,如果portal需要管理多个环境的话,以逗号分隔即可(大小写不敏感),例如:DEV,FAT,UAT,PRO。
  2. organizations - 部门列表:Portal中新建的App都需要选择部门,所以需要在这里配置可选的部门信息。
  3. superAdmin - Portal超级管理员: 默认值apollo(默认用户),多个账号以英文逗号分隔(,)。
    consumer.token.salt - consumer token salt: 如果会使用开放平台API的话,可以设置一个token salt。如果不使用,可以忽略。
  4. wiki.address: portal上“帮助”链接的地址,默认是Apollo github的wiki首页,可自行设置。
  5. admin.createPrivateNamespace.switch: 是否允许项目管理员创建private namespace。设置为true允许创建,设置为false则项目管理员在页面上看不到创建private namespace的选项。

3.调整ApolloConfigDB.ServerConfig

字段解析,可以根据实际情况设置:

  1. eureka.service.url - Eureka服务Url: apollo-configservice和apollo-adminservice都需要向eureka服务注册,需要配置eureka服务地址。默认apollo-configservice本身就是一个eureka服务,所以只需要填入apollo-configservice的地址即可(本文以此讲解)。如果希望将Config
    Service和Admin Service注册到公司统一的Eureka上,需修改Config Service。

  2. namespace.lock.switch - 一次发布只能有一个人修改开关:用于发布审核,这是一个功能开关,如果配置为true的话,那么一次配置发布只能是一个人修改,另一个发布。

  3. config-service.cache.enabled - 是否开启配置缓存:这是一个功能开关,如果配置为true的话,config
    service会缓存加载过的配置信息,从而加快后续配置获取性能。(默认为false,开启前请先评估总配置大小并调整config
    service内存配置。)

2.3.Apollo服务搭建

下载Quick Start安装包:https://github.com/nobodyiam/apollo-build-scripts,安装包有58M,所以下载挺慢的,因为是一个可以自启动的jar包,里面包含了所有依赖jar包以及一个内置的tomcat容器。

1.上传到部署的服务器(我的服务器地址是:192.168.10.130):

2.解压并修改demo.sh文件:

yum -y install unzip
unzip apollo-build-scripts-master.zip 
cd apollo-build-scripts-master
vi demo.sh 



关闭防火墙

 systemctl stop firewalld

3.启动项目:

因为是启动3个项目,所以要耐心等候。

./demo.sh start


等了大概2分钟,3个服务终于全部跑起来了,可以看到占内存1.2G,挺好资源的:

3.登录注册中心(ConfigService)和Apollo门户(Portal)看看:

登录注册中心:http://192.168.10.130:8080/,可以看到adminserviceconfigservice都注册上了。

登录Apollo门户(http://192.168.10.130:8070/signin),登录账号:apollo,密码:admin

可以看到apollo的主界面是这样的:

3.SpringBoot客户端代码测试

在没有引入Apollo之前,每次修改配置信息都要重启服务器,需要先在配置文件里配置参数,例如(application.yml):

server:
  port: 8090

spring:
  application:
    name: apollo-test

login:
  userName: bruce
  passWord: 123

Controller:

package com.bruce.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController 

    @Value("$login.userName")
    private String userName;

    @Value("$login.passWord")
    private String passWord;

    @RequestMapping("/getLoginMsg")
    public String setString() 
        return userName + " ---- " + passWord;
    

浏览器请求:http://localhost:8090/getLoginMsg,如果需要修改userName和passWord,那么就需要重启服务器。

这个时候分布式配置中心概念就出来了,百花齐放,本文的Apollo分布式配置中心亦如此。下面看看客户端如何集成Apollo分布式配置中心:

1.首先配置文件不需要写配置内容了,删去:

server:
  port: 8090

spring:
  application:
    name: apollo-test

#login:
#  userName: bruce
#  passWord: 123

2.Java代码当然也需要修改,不然代码编译的时候会报错,设置个默认值default。

package com.bruce.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController 

    @Value("$login.userName:default")
    private String userName;

    @Value("$login.passWord:default")
    private String passWord;

    @RequestMapping("/getLoginMsg")
    public String setString() 
        return userName + " ---- " + passWord;
    



3.添加依赖

<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-client</artifactId>
    <version>1.5.0</version>
</dependency>

<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-core</artifactId>
    <version>1.5.0</version>
</dependency>

4.启动类添加注解:

@EnableApolloConfig
@SpringBootApplication
@EnableApolloConfig
public class App 

    public static void main(String[] args) 
        SpringApplication.run(App.class,args);
    

5.新建app.properties,并配置apollo信息(src/main/resources/META-INF/app.properties):

#apollo中ApplicationId
app.id=apollo-test
# Eureka Server
apollo.meta=http://192.168.10.130:8080

6.在Apollo平台新建任务


7.添加配置信息:


8.启动客户端项目

9.发布

点击发布:

可以看到已发布:

浏览器输入(http://localhost:8090/getLoginMsg),可以看到配置信息被修改了:

样就实现了没有重启服务器来修改配置文件。

最后附上Apollo配置中心设计原理官方链接:https://github.com/ctripcorp/apollo/wiki/Apollo%E9%85%8D%E7%BD%AE%E4%B8%AD%E5%BF%83%E8%AE%BE%E8%AE%A1

4.总结

以上是关于Apollo架构篇 - 分布式配置中心Apollo的主要内容,如果未能解决你的问题,请参考以下文章

Apollo - 分布式配置中心

Apollo配置中心如何实现配置热发布

Apollo分布式配置中心入门学习

微服务架构~携程Apollo配置中心架构剖析

使用Docker搭建Apollo分布式配置中心

Apollo分布式配置中心部署以及使用