赞
踩
应用程序在启动和运行的时候, 往往需要读取一些配置信息, 配置基本上伴随着应用程序的整个生命周期, 比如: 数据库连接参数, 启动参数等
配置主要有一下几个特点:
传统单体应用存在一些潜在缺陷, 如随着规模的扩大, 部署效率降低, 团队协作效率差, 系统可靠性变差, 维护困难, 新功能上线周期长等, 所以迫切需要一种新的架构去解决这些问题, 而微服务(microservices)架构正是当下一种流行的解法
不过, 解决一个问题的同时, 往往会诞生出很多新的问题, 所以微服务化的过程中伴随着很多的挑战, 其中一个挑战就是有关服务(应用)配置的. 当系统从一个单体应用, 被拆分成分布式系统上一个个服务节点后, 配置文件也必须跟着迁移, 分割, 这样配置就分散了, 不仅如此, 分散中还包含着冗余, 如下图
配置中心将配置从应用中剥离出来, 统一管理, 优雅的解决了配置的动态变更, 持久化, 运维成本等问题
应用自身既不需要去添加管理配置接口, 也不需要自己去实现配置的持久化, 更不需要引入"定时任务"以便降低运维成本
总的来说, 配置中心就是一种统一管理各种应用配置的基础服务组件
在系统架构中, 配置中心时整个微服务基础架构体系中的一个组件, 如下图, 它的功能看上去不起眼, 无非就是配置的管理和存取, 但它是整个微服务架构中不可或缺的一环
集中管理配置,那么就要将应用的配置作为一个单独的服务抽离出来了,同理也需要解决新的问题,比如:版本管理(为了支持回滚),权限管理等。
总结一下,在传统巨型单体应用纷纷转向细粒度微服务架构的历史进程中,配置中心是微服务化不可缺少的一个系统组件,在这种背景下中心化的配置服务即配置中心应运而生,一个合格的配置中心需要满足:
配置项容易读取和修改
添加新配置简单直接
支持对配置的修改的检视以把控风险
可以查看配置修改的历史记录
不同部署环境支持隔离
目前市面上用的比较多的配置中心有:(按开源时间排序)
功能点 | SpringCloudConfig | Apollo | Nacos |
---|---|---|---|
配置实时推送 | 支持(Spring Cloud Bus) | 支持(HTTP长轮询1s内) | 支持(HTTP长轮询1s内) |
版本管理 | 支持(Git) | 支持 | 支持 |
配置回滚 | 支持(Git) | 支持 | 支持 |
灰度发布 | 支持 | 支持 | 不支持 |
权限管理 | 支持(依赖Git) | 支持 | 不支持 |
多集群 | 支持 | 支持 | 支持 |
多环境 | 支持 | 支持 | 支持 |
监听查询 | 支持 | 支持 | 支持 |
多语言 | 只支持Java | 主流语言, 提供了Open Api | 主流语言, 提供了Open Api |
配置格式校验 | 不支持 | 支持 | 支持 |
单机读(QPS) | 7(限流所致) | 9000 | 15000 |
单机写(QPS) | 5(限流所致) | 1100 | 1800 |
3节点读(QPS) | 21(限流所致) | 27000 | 45000 |
3节点写(QPS) | 5(限流所致) | 3300 | 5600 |
总的来看,Apollo和Nacos相对于Spring Cloud Config的生态支持更广,在配置管理流程上做的更好。Apollo相对于Nacos在配置管理做的更加全面,Nacos则使用起来相对比较简洁,在对性能要求比较高的大规模场景更适合。但对于一个开源项目的选型,项目上的人力投入(迭代进度、文档的完整性)、社区的活跃度(issue的数量和解决速度、Contributor数量、社群的交流频次等),这些因素也比较关键,考虑到Nacos开源时间不长和社区活跃度,所以从目前来看Apollo应该是最合适的配置中心选型。
Apollo - A reliable configuration management system
https://github.com/ctripcorp/apollo
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用的不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
Apollo包括服务端和客户端两部分:
服务端基于Spring Boot和Spring Cloud开发,打包后可以直接运行,不需要额外安装Tomcat等应用容器。
Java客户端不依赖任何框架,能够运行于所有Java运行时环境,同时对Spring/Spring Boot环境也有较好的支持。
基于配置的特殊性,所以Apollo从设计之初就立志于成为一个有治理能力的配置发布平台,目前提供了以下的特性:
操作流程如下:
用户通过Apollo配置中心修改或发布配置后,会有两种机制来保证应用程序来获取最新配置:一种是Apollo配置中心会向客户端推送最新的配置;另外一种是Apollo客户端会定时从Apollo配置中心拉取最新的配置,通过以上两种机制共同来保证应用程序能及时获取到配置。
Java
由于需要同时运行服务端和客户端,所以建议安装Java 1.8+。
MySQL
Apollo的表结构对timestamp使用了多个default声明,所以需要5.6.5以上版本。
Apollo服务端共需要两个数据库:ApolloPortalDB
和ApolloConfigDB
,ApolloPortalDB只需要在生产环境部署一个即可,而ApolloConfigDB需要在每个环境部署一套。
soure apollo/ApolloPortalDB_initialization.sql
select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limit 1;
注: ApolloPortalDB只需要在生产环境部署一个即可
source apollo/ApolloConfigDB__initialization.sql
select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;
java -Xms256m -Xmx256m -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-configservice-1.3.0.jar
java -Xms256m -Xmx256m -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-adminservice-1.3.0.jar
java -Xms256m -Xmx256m -Ddev_meta=http://localhost:8080/ -Dserver.port=8070 -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-portal-1.3.0.jar
echo
set url="localhost:3306"
set username="root"
set password="123"
start "configService" java -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-configservice.log -jar .\apollo-configservice-1.3.0.jar
start "adminService" java -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-adminservice.log -jar .\apollo-adminservice-1.3.0.jar
start "ApolloPortal" java -Xms256m -Xmx256m -Dapollo_profile=github,auth -Ddev_meta=http://localhost:8080/ -Dserver.port=8070 -Dspring.datasource.url=jdbc:mysql://%url%/ApolloPortalDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-portal.log -jar .\apollo-portal-1.3.0.jar
运行runApollo.bat即可启动Apollo 待启动成功后,访问管理页面(localhost:8070) apollo/admin
ApolloPortalDB
和ApolloConfigDB
pro环境只需要创建ApolloConfigDB
vim apollo-env.properties
dev.meta是第二台服务器dev的ip加8080端口, pro.meta是第三台服务器pro的ip加8080端口,其余环境暂时没有用到
vim application-github.properties
修改portal对应的mysql地址username 和password
进入PortalDB数据库 修改表格(ServerConfig)
进入到第二台服务器中,首先进入configservice的config目录
vim application-github.properties
修改成dev环境的ApolloConfigDB的数据库配置
再进入adminservice的config目录, 修改application-github.properties的数据库配置
进入到第三台服务器, 修改configservice和adminservice的mysql数据库配置
<dependencies> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.28</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build>
编写main方法获取配置
import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; public class GetConfigTest { //VM Options: //-Dapp.id=applo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080 public static void main(String[] args) { Config config = ConfigService.getAppConfig(); String someKey = "sms.enable"; //获取配置信息, 第一个参数: 配置的key, 第二个参数: 拿不到的话配置的默认值 //如果这样写会报警告, 找不到app.id,没有与Apollo内的项目绑定,暂时先通过VM-Options的方式加入 String value = config.getProperty(someKey,null); System.out.println("sms.enable: " + value); } }
修改sms.enable的配置, 再启动main方法, 发现sms.enable输出已经改变了
修改代码, 让主方法一直取配置
import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; import java.time.LocalDateTime; public class GetConfigTest { //VM Options: //-Dapp.id=applo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080 public static void main(String[] args) throws InterruptedException { Config config = ConfigService.getAppConfig(); while(true){ Thread.sleep(1000); String someKey = "sms.enable"; //获取配置信息, 第一个参数: 配置的key, 第二个参数: 拿不到的话配置的默认值 //如果这样写会报警告, 找不到app.id,没有与Apollo内的项目绑定,暂时先通过VM-Options的方式加入 String value = config.getProperty(someKey,null); //打印带时间的日志 System.out.printf("now: %s , sms.enable: %s%n " , LocalDateTime.now().toString(), value); } } }
启动main方法后, 去Apollo管理页面修改sms.enable的值, 发现配置可以修改并自动生效
上图简要描述了Apollo的总体设计,我们可以从下往上看:
apollo默认的部门有两个. 要增加自己的部门, 可在系统参数中修改:
进入系统参数设置
输入key查询以存在的部门设置: organizations
修改value值来添加新部门, 下面添加一个微服务部门
退出后重新登录, 可以使之生效
apollo默认提供一个超级管理员: apollo, 可以自行添加用户
如果想要删除整个项目, 点击右上角的"管理员工具–> 删除应用, 集群"
首先查询出要删除的项目, 点击"删除应用"
下边在account-service项目中进行配置
点击右侧修改按钮
点击右侧删除按钮
Namespace作为配置的分类,可当成一个配置文件。
以添加rocketmq配置为例,添加“spring-rocketmq” Namespace配置rocketmq相关信息。
创建成功后, 会跳转到针对这个namespace的权限分配页面, 可以对某些用户进行授权
想要获取到指定namespace下的配置, 需要修改java代码
import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; import java.time.LocalDateTime; public class GetConfigTest { //VM Options: //-Dapp.id=account-service -Denv=DEV -Ddev_meta=http://localhost:8080 public static void main(String[] args) throws InterruptedException { //读取默认namespace的配置信息 //Config config = ConfigService.getAppConfig(); //指定namespace获取config对象 Config config = ConfigService.getConfig("spring-rocketmq"); while(true){ Thread.sleep(1000); String someKey = "rocketmq.name-server"; //获取配置信息, 第一个参数: 配置的key, 第二个参数: 拿不到的话配置的默认值 //如果这样写会报警告, 找不到app.id,没有与Apollo内的项目绑定,暂时先通过VM-Options的方式加入 String value = config.getProperty(someKey,null); System.out.printf("now: %s , sms.enable: %s%n " , LocalDateTime.now().toString(), value); } } }
在项目开发中,有一些配置可能是通用的,我们可以通过把这些通用的配置放到公共的Namespace中,这样其他项目要使用时可以直接添加需要的Namespace
3. 添加配置项并发布
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
server.tomcat.remote_ip_header = x-forwarded-for
server.tomcat.protocol_header = x-forwarded-proto
server.use-forward-headers = true
server.servlet.context-path = /
在有些情况下,应用有需求对不同的集群做不同的配置,比如部署在A机房的应用连接的RocketMQ服务器地址和部署在B机房的应用连接的RocketMQ服务器地址不一样。另外在项目开发过程中,也可为不同的开发人员创建不同的集群来满足开发人员的自定义配置。
同步集群的配置是指在同一个应用中拷贝某个环境下的集群的配置到目标环境下的目标集群。
读取某个集群的配置,需要启动应用时指定具体的应用、环境和集群。
-Dapp.id=应用名称
-Denv=环境名称
-Dapollo.cluster=集群名称
-D环境_meta=meta地址
-Dapp.id=account-service -Denv=DEV -Dapollo.cluster=SHAJQ -Ddev_meta=http://localhost:8080
在配置中心中,一个重要的功能就是配置发布后实时推送到客户端。下面我们简要看一下这块是怎么设计实现的。
上图简要描述了配置发布的主要过程:
Admin Service在配置发布后,需要通知所有的Config Service有配置发布,从而Config Service可以通知对应的客户端来拉取最新的配置。
从概念上来看,这是一个典型的消息使用场景,Admin Service作为producer(生产者)发出消息,各个Config Service作为consumer(消费者)消费消息。通过一个消息队列组件(Message Queue)就能很好的实现Admin Service和Config Service的解耦。
在实现上,考虑到Apollo的实际使用场景,以及为了尽可能减少外部依赖,我们没有采用外部的消息中间件,而是通过数据库实现了一个简单的消息队列。
具体实现方式如下:
SELECT * FROM ApolloConfigDB.ReleaseMessage
3. Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器
然后调用消息监听类的handleMessage方法:NotificationControllerV2
4. NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端
上一节中简要描述了NotificationControllerV2是如何得知有配置发布的,那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?
实现方式如下:
除了之前介绍的客户端和服务端保持一个长连接,从而能第一时间获得配置更新的推送外,客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
在微服务架构模式下,项目往往会切分成多个微服务,下面将以万信金融P2P项目为例演示如何在项目中使用。
万信金融是一款面向互联网大众提供的理财服务和个人消费信贷服务的金融平台,依托大数据风控技术,为用户提供方便、快捷、安心的P2P金融服务。本项目包括交易平台和业务支撑两个部分,交易平台主要实现理财服务,包括:借钱、出借等模块,业务支撑包括:标的管理、对账管理、风控管理等模块。项目采用先进的互联网技术进行研发,保证了P2P双方交易的安全性、快捷性及稳定性。
本章节仅仅是为了演示配置中心,所以摘取了部分微服务,如下:
用户中心服务(consumer-service):为借款人和投资人提供用户账户管理服务,包括:注册、开户、充值、提现等
UAA认证服务(uaa-service):为用户中心的用户提供认证服务
统一账户服务(account-service):对借款人和投资人的登录平台账号进行管理,包括:注册账号、账号权限管理等
交易中心(transaction-service):负责P2P平台用户发标和投标功能
下面以集成统一账户服务(account-service)为例
参考account-service、transaction-service、uaa-service、consumer-service工程,手动创建这几个微服务。
每个工程必须添加依赖:
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.0</version>
</dependency>
下边是account-service依赖,其它工程参考“资料”下的“微服务”。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.pbteach</groupId> <artifactId>account-service</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.1.0</version> </dependency> </dependencies> </project>
app:
id: account-service
apollo:
bootstrap:
enabled: true
# namespace中间以,分隔
namespaces: application,micro_service.spring-boot-http,spring-rocketmq,micro_service.spring-boot-druid
env 需要通过VMOPtions来指定
cacheDir, cluster, meta, 都可以通过yml文件的方式配置, 但是env需要通过指定VMOptions来配置
server: port: 19082 servlet: context-path: /gbmp/bdmgmt spring: application: name: account-service apollo: bootstrap: enabled: true namespaces: application,yuecloud.mybatis,yuecloud.management,yuecloud.logging,yuecloud.pagehelper,yuecloud.datasource,yuecloud.jackson,yuecloud.redis,yuecloud.eureka,yuecloud.securityoauth2 cacheDir: /opt/data/apollo-config cluster: DEFAULT meta: http://localhost:8080 app: id: account-service
在主启动类上加入注解@EnableApolloConfig
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableApolloConfig
public class AccountMain {
public static void main(String[] args) {
SpringApplication.run(AccountMain.class,args);
}
}
将自己的配置信息拷贝到apollo中
在Controller上获取配置@Value方式,在Apollo上点击发布会自动刷新
@ConfigurationProperties(prefix=“dev”)配置文件方式, 需要加@RefreshScope才会自动刷新
首先需要加入pom的jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
再写一个配置类,配置刷新监控类ConfigRefresh
可以把其他微服务的配置文件都放到Apollo上
当一个项目要上线部署到生产环境时,项目的配置比如数据库连接、RocketMQ地址等都会发生变化,这时候就需要通过Apollo为生产环境添加自己的配置。
在企业中常用的部署方案为:Apollo-adminservice和Apollo-configservice两个服务分别在线上环境(pro),仿真环境(uat)和开发环境(dev)各部署一套,Apollo-portal做为管理端只部署一套,统一管理上述三套环境。
同步完成后,切换到pro环境,修改生产环境rocketmq地址后发布配置
-Denv=pro -Dapollo.cacheDir=/opt/data/apollo-config -Dapollo.cluster=DEFAULT
灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。
apollo-quickstart项目有两个客户端:
启动apollo-quickstart项目的GrayTest类输出timeout的值
public class GrayTest {
// VM options:
// -Dapp.id=apollo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080
public static void main(String[] args) throws InterruptedException {
Config config = ConfigService.getAppConfig();
String someKey = "timeout";
while (true) {
String value = config.getProperty(someKey, null);
System.out.printf("now: %s, timeout: %s%n", LocalDateTime.now().toString(), value);
Thread.sleep(3000L);
}
}
}
如果灰度的配置测试下来比较理想,符合预期,那么就可以操作全量发布。
全量发布的效果是:
如果灰度版本不理想或者不需要了,可以点击放弃灰度
点击主版本的发布历史按钮,可以看到当前namespace的主版本以及灰度版本的发布历史
现在在203节点上通过修改配置文件的方式, 改成DEV环境下的集群, 202和203应该共用一套mysql的ConfigServiceDB
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。