配管信息管理工具之Apollo

实际项目中配置信息管理非常重要,配置信息中又可分为静态配置信息和动态配置信息,静态配置信息指服务运行过程中无需修改的一类信息,例如:连接数据库的用户、密码等;动态信息指服务运行过程中可能需要修改的一类信息,例如请求超时时间,消息队列长度,日志级别,限流熔断阀值等。具体如下图所示:

服务运行过程中有这么多配置信息需要管理,而且为了支持动态信息配置,还需要能实时刷新配置信息并发送给客户端应用。此篇博客介绍的Apollo就是一款国内开源的、支持企业级的配置信息管理平台。在学习Apollo时,首先需要了解下Apollo中所涉及的一些术语概念。

应用Application:Apollo上可创建多个应用,一个应用就对应到实际项目中某一个具体的服务,应用有一个唯一标识符appId。appId非常关键,客户端在对接Apollo时也需要用到appId。如果是Java的客户端,那么需要在文件中定义和Apollo服务上相同的appId。Java->classpath:/META-INF/app.properties -> appid.

环境Enviroment:对于一个组织,都会构建不同的测试环境用于不同阶段的测试工作,例如Dev,ST,UAT等,或者构建专门的性能测试环境。另外还包括生产环境。Apollo支持对于不同的环境配置不同的信息,故在Apollo上创建配置信息时可以选择环境。Apollo上默认分了DEV/FAT/UAT/PROD四种类型的环境。

集群Cluster:相同的一个应用,为了高可用,可能部署在不同的集群下,Apollo支持对于不同的集群配置不同的配置信息。

命名空间namespace:同一个应用,可以划分不同类型的配置信息,创建namespace,将配置信息放到不同的namespace中,例如数据库的配置可以放一个namespace,服务框架的配置可以放一个namespace,业务参数调整的配置可以放一个namespace,划分namespace主要是针对配置信息很多的应用,可以更加清晰的分类管理配置信息。namespace还分为private namespace和public namespace,对于private类型,只能被所在应用使用,对于public类型,可以被所有应用使用,当然public namespace下的配置的key也需要具备全局唯一性的数学。注意:这里的namespace中k8s中的namespace不是同一个概念。

配置项Item:具体的配置信息,由key和value组成一个配置项,支持properties/json/xml/yaml文件格式的配置信息。上面介绍了一些配置过程中的概念,接下来看看Apollo中的关键服务。

Config Service:提供获取配置信息接口,配置信息推送接口,服务Apollo客户端
Admin Service:提供配置信息管理接口,配置信息修改、发布接口,服务Portal
Portal:配置信息管理界面,也就是Apollo服务的前端界面,通过Meta server获取Admin Service服务列表,支持客户端的软负载
Client:Apollo客户端,获取应用的配置信息,实时刷新,通过Meta server获取Config Service的服务列表,支持客户端软负载
Eureka:Apollo集成了Netflix开源的服务注册发现组件Eureka,Config Service和Admin Service会注册到服务注册中心,另外,Eureka与Config Service部署在一个进程内
Meta Server:

  • Portal通过域名访问Meta Server获取Admin Service服务列表
  • Client通过域名访问Meta Server获 取Config Service服务列表
  • 相当于一个Eureka Proxy逻辑角色,和Config Service住在一起部署

了解了各个服务模块的作用,接下来看看为什么Apollo会这样设计,官网发布了Apollo的服务端架构图,下图是改造后的服务架构图,更易于理解,该图来源于bobo微课。

上图中可以先忽略掉Meta Server和Nginx LB部分,Portal作为Apollo的前端,有独立的数据库,管理员通过Portal进行配置信息的增删该查操作,该过程中实际是调用Admin Service提供的接口完成的。一个应用要接入Apollo,那么就是Apollo的客户端Client,应用通过Config Service获取管理员配置的信息。因为Admin Service和Config Service使用同一个数据库Config DB,故Config Service要获取在Apollo服务端界面配置的信息,非常容易。另外,为什么要引入Eureka呢?因为配置信息是非常关键的信息,一旦无法获取,服务就无法正常运行,所以Apollo在设计上非常注重配置信息管理平台的高可用。所有的组件都采用多实例部署方式,所以需要引入Eureka做服务注册和发现。那为什么还有Meta Server呢?因为Eureka原生只支持Java客户端接入,对于其他语言还没有成熟的客户端接入框架,而Apollo需要支持不同语言的客户端接入,所以对Eureka又进行了一层封装,所以演变出了Meta server。因为Meta server本身也是多实例部署,所以需要引入负载均衡的能力,这里就采用了携程内部的负载均衡Nginx LB。以上就是Apollo各个服务间的调用关系。

为了保证修改的信息能及时通知到客户端,Apollo采用了实时推送的设计,如下图左图所示。在Admin Service和Config Service间,具体是如何发送Release Message的呢?因为Admin Service和Config Service共用同一个DB,所以,当有配置信息发布时,Admin Service写入数据库表,Config Service定时读取数据库表信息来做到消息的同步。

上面是服务端架构的介绍,接下来看看客户端的设计,为了保证客户端能获取到配置信息,设计上有两个点考虑的非常周到,如下图所示,当配置信息有更新时,Apollo服务端会主动推送信息给客户端,初此之外,客户端还会定期从服务端拉取配置信息,这样就保证了,如果某次主动推送失效,那么客户端仍然能够获取到配置信息。另外,客户端获取到配置信息后,除了存放在内存缓存中使用外,还会同步信息到本地的缓存文件中,这样就保证了因为某些故障导致内存缓存中丢失配置信息的情况下,仍然可以从本地缓存文件中获取到配置信息。从客户端和服务端的设计可以看到,Apollo的设计非常注重高可用。

前面都是概念类介绍,接下来通过实际例子演示Apollo是如何工作的。Demo地址

为了演示客户端如果获取、使用Apollo上的配置信息,首先需要部署Apollo服务,如果是本地部署,那么可以下载apollo-quick-start,修改demo.sh脚本里面的数据库连接信息,执行demo.sh start,即可在本地快速部署Apollo服务。快速部署方式不支持修改服务端口地址,所以采用快速部署方式需要保证Apollo占用的8080,8070,8090端口都是空闲状态。如果要修改服务的端口,可从官网下载Admin-server,Config-service,Portal-service的zip压缩包,解压后每个包里面都有scripts目录,修改scripts目录下startup.sh脚本里面的端口。另外,每个包里面都有config目录,修改config目录下application-github.properties文件,里面涉及数据库连接信息,修改成本地的数据库信息。对于Poral服务,config目录下还需修改apollo-env.properties文件,修改Meta-Server的地址。

上面是服务端的部署,更多生产集群的部署建议可参考官网文档,接下来看看Apollo如何与Java客户端集成,与客户端集成主要分为两种方式,第一种是API方式,第二种是Spring或者Spring-boot集成。API方式最简单,先来看看API方式获取配置信息,样例代码如下所示:

Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
String someKey = "someKeyFromDefaultNamespace";
String someDefaultValue = "someDefaultValueForTheKey";
String value = config.getProperty(someKey, someDefaultValue);

如果关心配置信息的改变,那么可以添加监听事件,例如数据库配置信息改变后,需要重现建立数据库连接等,这部分逻辑就可以存放在eventListener中。

Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
config.addChangeListener(new ConfigChangeListener() {
    @Override
    public void onChange(ConfigChangeEvent changeEvent) {
        System.out.println("Changes for namespace " + changeEvent.getNamespace());
        for (String key : changeEvent.changedKeys()) {
            ConfigChange change = changeEvent.getChange(key);
            System.out.println(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()));
        }
    }
});

以下是Demo中案例代码,可以看到,可以获取应用指定namespace下的配置信息,还可以指定获取某种格式的配置信息。另外,demo中添加的Listener打印了一些变更信息的日志。

 客户端集成后,启动客户端,在Apollo服务上配置信息后,即可在客户端获取到,结果如下所示

除了上面API方式接入外,再来看看Spring-boot集成方式。与spring-boot集成,除了在META-INF目录下的app.properties文件中定义app.id=xxx外.这里的appId需要和Apollo服务器上定义的名称一致。还需在application.yml文件中定义meta server的地址,默认meta server是启动在8080端口,如果本地服务部署时修改了端口,客户端接入时也需要配置正确的端口信息。另外,在application.yml文件中还可以定义bootstrap值等。当apollo.bootstrap.enabled = true默认注入application这个namespace,当然也可以自定义多个namespace值。

因为Spring Boot提供了@ConfigurationProperties把配置注入到bean对象中,所以可以按如下方式把redis.cache.expireSeconds和redis.cache.commandTimeout分别注入到SampleRedisConfig的expireSeconds和commandTimeout字段中。

@ConfigurationProperties(prefix = "redis.cache")
public class SampleRedisConfig {
  private int expireSeconds;
  private int commandTimeout;

  public void setExpireSeconds(int expireSeconds) {
    this.expireSeconds = expireSeconds;
  }

  public void setCommandTimeout(int commandTimeout) {
    this.commandTimeout = commandTimeout;
  }
}
@Configuration
@EnableApolloConfig
public class AppConfig {
  @Bean
  public SampleRedisConfig sampleRedisConfig() {
    return new SampleRedisConfig();
  }
}

需要注意的是,@ConfigurationProperties如果需要在Apollo配置变化时自动更新注入的值,需要配合使用EnvironmentChangeEvent或RefreshScope。接下来看看如何通过RefreshScope实现自动更新的效果。定义的配置信息对象如下所示,@ConditionalOnProperty来控制@Configuration是否生效.redis.cache.enabled=true时,@Configuration才生效。

调用refreshScope.refresh(className)来刷新指定的class对象,@ApolloConfigChangeListener用来自动注册ConfigChangeListener,这样当Apollo服务端有配置信息刷新时,会通知到客户端,即执行onChange()中的逻辑,onChange()里面又调用了refreshScope.refresh(),通过这样的方式实现当有配置信息更新时能及时更新客户端的配置对象。

在程序入口的地方通过ApplicationContext获取配置对象Bean,从而获取到配置信息。

以上就是Spring boot集成Apollo的方式,实际客户端集成Apollo的方式还有很多种写法,更多信息可以参考官网给出的例子

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taoli-qiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值