Spring Cloud

携程 apollo 分布式配置中心解决方案

2020-04-10  本文已影响0人  恐惧是万敌之首

背景

    随着程序功能的日益复杂,系统的配置参数越来越多,应用系统的配置信息变的越来越难以高效管理。这里有两个原因:

    微服务的流行。随着企业的发展,用户量的增长,应用服务越来越多。这里的多有两个方面:一方面是应用服务的种类越来越多,另一方便是单一应用服务走向集群部署。种类越来越多导致了应用系统的配置参数分散在各个微服务中,如果配置信息发生变化,则需要去修改对应的应用服务配置文件甚至需要去重启服务,这样维护起来繁琐,更重要的是容易出错。单一应用服务走向集群部署,同样当配置信息发生变化时,需要依次去修改集群中所有该服务的配置信息,这样会导致同一件事情重复的做,就不符合互联网要解决的根本问题:提升生产效率,解放生产力。

    企业级技术架构的升级。企业在发展的各个阶段,需要形成与之相匹配相适应的技术战略布局用来服务于当前的业务发展。在这个不断变革升级的过程中,需要形成协同,高效,统一,管控有力的企业级具有战略性的技术体系。因此,分布式配置中心作为企业技术体系战略布局中的重要一环,在满足以往业务在迭代中要分环境,可用的硬性要求的同时,还需要形成新的生命力。这个新的生命力就是:安全可靠,权限可控,维护简单,能够热修改。

    携程的开源项目 apollo 无疑是一把提供企业分布式配置中心解决方案的利器。今天我们就来分享一下基于 携程开源项目 apollo 的分布式配置中心解决方案。

需求分析

   在应用服务的开发过程中为了软件的快速迭代,我们通常会隔离出3套环境:dev(开发环境),fat(测试环境),pro(生产环境)。不同的软件环境对系统的稳定性和可靠性的要求不同,通常我们对生产环境的稳定性和可靠性要求是苛刻的,而开发和测试环境则相对略低。本着在能满足要求的情况下,最低成本的原则我们来分配资源来搭建环境。

资源分配

机器 IP 环境 部署应用 数据库
apollo01(192.168.0.111) pro (生产环境) protal ProtalDB
apollo02(192.168.0.112) pro (生产环境) config,admin ConfigDB_Pro
apollo03(192.168.0.113) pro (生产环境) config,admin ConfigDB_Pro
apollo04(192.168.0.114) dev (开发环境) config,admin ConfigDB_Dev
apollo05(192.168.0.115) fat (测试环境) config,admin ConfigDB_Fat

说明:
1.apollo依赖于注册中心eureka,这里还需要搭建 dev,fat,pro 三套环境的注册中心。注册中心可以搭建高单机版或高可用版,这个并非本次演示重点,这里不再做具体搭建说明。
2.apollo依赖与mysql数据库,这里也不再做具体搭建说明。通常生产环境建议使用高可用版本的数据库。

apollo 分布式配置中心拓扑图

环境搭建

1.初始化数据库:

ProtalDB数据库执行脚本:apolloportaldb.sql
ConfigDB_Pro, ConfigDB_Dev, ConfigDB_Fat 数据库执行sql脚本:apolloconfigdb.sql
注意:数据库名要和 sql 脚本中的库名保持一致。

2.下载 apollo 源码 并打包:
apollo打包成功
3.将对应的zip包分别上传到对应的服务器目录下:
机器 环境 上传zip zip上传位置
apollo01 pro (生产环境) apollo-protal-1.7.0.zip /opt/apollo/protal
apollo02 pro (生产环境) apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip /opt/apollo/configservice 和 /opt/apollo/adminservice
apollo03 pro (生产环境) apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip /opt/apollo/configservice 和 /opt/apollo/adminservice
apollo04 dev (开发环境) apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip /opt/apollo/configservice 和 /opt/apollo/adminservice
apollo05 fat (测试环境) apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip /opt/apollo/configservice 和 /opt/apollo/adminservice
4.注册中心:
环境 key Value
pro (生产环境) eureka.service.url http://1.1.1.1:8080/eureka/
dev (开发环境) eureka.service.url http://1.1.1.2:8080/eureka/
fat (测试环境) eureka.service.url http://1.1.1.3:8080/eureka/
5.修改相应的配置文件:
配置apollo-portal的meta service信息:

    Apollo Portal需要在不同的环境访问不同的meta service(apollo-configservice)地址,所以我们需要在配置中提供这些信息。默认情况下,meta service和config service是部署在同一个JVM进程,所以meta service的地址就是config service的地址。
修改完的效果如下:

dev.meta = http://apollo02:8080,http://apollo03:8080
fat.meta = http://apollo04:8080
pro.meta = http://apollo05:8080
配置apollo-adminservice的数据库连接信息:

解压apollo-adminservice-x.x.x-github.zip
打开config目录下的application-github.properties文件
填写正确的ApolloConfigDB数据库连接串信息,注意用户名和密码后面不要有空格!
改完的效果如下:

# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloConfigDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd

注:由于ApolloConfigDB在每个环境都有部署,所以对不同的环境admin-service需要配置对应环境的数据库参数

配置apollo-configservice的数据库连接信息:

解压apollo-adminservice-x.x.x-github.zip
用程序员专用编辑器(如vim,notepad++,sublime等)打开config目录下的application-github.properties文件
填写正确的ApolloConfigDB数据库连接串信息,注意用户名和密码后面不要有空格!
修改完的效果如下:

# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloConfigDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd

注:由于ApolloConfigDB在每个环境都有部署,所以对不同的环境admin-service需要配置对应环境的数据库参数

配置apollo-portal的数据库连接信息:

解压apollo-portal-x.x.x-github.zip
打开config目录下的application-github.properties文件
填写正确的ApolloAdminDB数据库连接串信息,注意用户名和密码后面不要有空格!
修改完的效果如下:

# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloAdminDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd
调整ApolloConfigDB配置:

    不管是apollo-configservice还是apollo-adminservice都需要向eureka服务注册,所以需要配置eureka服务地址。 按照目前的实现,apollo-configservice本身就是一个eureka服务,所以只需要填入apollo-configservice的地址即可,如有多个,用逗号分隔(注意不要忘了/eureka/后缀)。

在PRO环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为:

http://1.1.1.1:8080/eureka/

在DEV环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为:

http://1.1.1.2:8080/eureka/

在FAT环境的ApolloConfigDB.ServerConfig表中设置eureka.service.url为:

http://1.1.1.3:8080/eureka/
6.启动各个服务:

启动apollo01上的protal

bash /opt/apollo/protal/scripts/startup.sh

启动apollo02,apollo03,apollo04,apollo05上的 configservice

bash /opt/apollo/configservice/scripts/startup.sh

启动apollo02,apollo03,apollo04,apollo05上的 adminservice

bash /opt/apollo/adminservice/scripts/startup.sh

使用说明

系统启动成功后可以访问 apollo 配置中心的管理页面:http://apollo01:8070
注意:需要将 url 中的 apollo01 替换成你部署 protal 的机器IP

apollo 配置中心的管理页面
首次登陆的用户名是:apollo 密码:admin
Apollo使用指南

基于springboot的apollo客户端使用方式

1.引入 apollo 客户端依赖包

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

2.在springboot的启动程序上添加开启 @EnableApolloConfig 注解:

@SpringBootApplication
@EnableApolloConfig
public class ApolloApplication {

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

3.修改yml配置文件,因为我这里有dev(开发环境),fat(测试环境),pro(生产环境) 三套环境,具体配置如下:
基础配置文件:application.yml

spring:
  profiles:
    ## 使用 pro 环境
    active: pro

app:
  id: message

apollo:
  bootstrap:
    enabled: true
    ## 添加使用到的 namespace
    namespaces: application,grobal
    eagerLoad:
      enabled: true
  autoUpdateInjectedSpringProperties: true

dev(开发环境): application-dev.yml

apollo:
  meta: http://192.168.0.114:8080

fat(测试环境):application-fat.yml

apollo:
  meta: http://192.168.0.115:8080

fat(生产环境):application-pro.yml 高可用配置

apollo:
  meta: http://192.168.0.112:8080,http://192.168.0.113:8080

4.使用@Value注解引入apollo中配置的属性:

@RestController
@RequestMapping("/message")
public class ApolloController {

  @Value("${environmet}")
  private String environment;

  @Value("${grobal.user.name}")
  private String name;

  @Value("${grobal.user.phone}")
  private String phone;

  @Value("${grobal.user.id}")
  private String id;

  @Value("${user.alias:UNDEFINED}")
  private String alias;

    @GetMapping("/getInfo")
  public String getInfo() {
    return "ApolloController{" +
            "environment='" + environment + '\'' +
            ", name='" + name + '\'' +
            ", phone='" + phone + '\'' +
            ", id='" + id + '\'' +
            ", alias='" + alias + '\'' +
            '}';
  }


  public String getEnvironment() {
    return environment;
  }

  public void setEnvironment(String environment) {
    this.environment = environment;
  }

 public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getPhone() {
    return phone;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getAlias() {
    return alias;
  }

  public void setAlias(String alias) {
    this.alias = alias;
  }

}

5.注册监听器,监听apollo中属性配置的变化:
@ApolloConfigChangeListener 监听的是默认的 namespace,如果需要监听指定的namespace 需要加入指定。

@Component
public class ApolloChangeListener {
  
  @ApolloConfigChangeListener("application")
  private void someChangeHandler1(ConfigChangeEvent changeEvent) {
    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()));
    }
  }

  @ApolloConfigChangeListener("grobal")
  private void someChangeHandler2(ConfigChangeEvent changeEvent) {
    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()));
    }
  }
}

上一篇 下一篇

猜你喜欢

热点阅读