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

背景

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

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

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

    携程的开源项目 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()));
    }
  }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容