《微服务:简述微服务架构中的配置中心选型》
Github : https://github.com/ctripcorp/apollo
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
如果想要深入了解,可以到github上参见Apollo配置中心,官网的介绍很详细。本文主要讲述Spring Boot 2.0 整合Apollo配置中心。
Apollo服务端使用的是官网提供的 Quick Start(针对本地测试使用)。
Apollo服务端共需要两个数据库:ApolloPortalDB和ApolloConfigDB,官网把数据库、表的创建和样例数据都分别准备了sql文件(在下载的 Quick Start 安装包的sql目录下),只需要导入数据库即可。
select 'Id', 'AppId', 'Name' from ApolloPortalDB.App;
Id | AppId | Name |
---|---|---|
1 | SampleApp | Sample App |
select ‘NamespaceId’, ’Key‘, ’Value‘, ‘Comment’ from ApolloConfigDB.Item;
NamespaceId | Key | Value | Comment |
---|---|---|---|
1 | timeout | 100 | sample |
Apollo服务端需要知道如何连接到前面创建的数据库,所以需要编辑 demo.sh,修改ApolloPortalDB和ApolloConfigDB相关的数据库连接串信息。
#apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=用户名
apollo_config_db_password=密码(如果没有密码,留空即可)
# apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=用户名
apollo_portal_db_password=密码(如果没有密码,留空即可)
Quick Start脚本会在本地启动3个服务,分别使用8070, 8080, 8090端口,请确保这3个端口当前没有被使用。例如,在Linux/Mac下,可以通过命令【lsof -i:8080】检查;在windows下,可以通过命令【netstat -aon|findstr “8080”】检查:
在Quick Start目录下执行命令【./demo.sh start】。当看到如下输出后,就说明启动成功了!
==== starting service ====
Service logging file is ./service/apollo-service.log
Started [10768]
Waiting for config service startup.......
Config service started. You may visit http://localhost:8080 for service status now!
Waiting for admin service startup....
Admin service started
==== starting portal ====
Portal logging file is ./portal/apollo-portal.log
Started [10846]
Waiting for portal startup......
Portal started. You can visit http://localhost:8070 now!
如果启动遇到了异常,可以分别查看service和portal目录下的log文件排查问题。
注: 在启动apollo-configservice的过程中会在日志中输出eureka注册失败的信息,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。
浏览器访问http://localhost:8070。Quick Start集成了Spring Security简单认证,更多信息可以参考Portal 实现用户登录功能。输入用户名apollo,密码admin登录。
配置中心中包含一个默认的项目SampleApp。
点击SampleApp进入配置界面,可以看到当前有一个配置timeout=100。如果提示系统出错,请重试或联系系统负责人,请稍后几秒钟重试一下,因为通过Eureka注册的服务有一个刷新的延时。
我们的客户端使用apollo需要新增相关的项目配置。
点击提交,创建完成 。
应用ID:这个ID是应用的唯一标识
应用名称:应用的名称,会展示在配置中心的首页上
如果配置做了修改之后,发现配置更改错误,这个时候可以使用回滚功能,回到上一个配置。
Apollo客户端基于Spring Boot 2.0搭建,开发工具是InteIIij IDEA。新建一个项目,项目名称为apollo-client
com.ctrip.framework.apollo
apollo-client
1.1.1
# 应用ID(在Apollo服务端新增项目添加的应用ID)
app.id=testclient
# apollo-configservice地址
apollo.meta=http://127.0.0.1:8080开启Apollo客户端
在项目的启动类上添加@EnableApolloConfig注解
新增一个测试接口
@RequestMapping("/index")
public String hello(){
return "hello man";
}
上面我们简单的搭建了客户端,成功的使用服务端配置。Apollo为我们提供的使用方式有很多种,下面只介绍Spring Boot 2.0环境下的使用方式。
Spring应用通常会使用Placeholder来注入配置,使用的格式形如【$ {someKey:someDefaultValue}】,如${timeout:100}。冒号前面的是key,冒号后面的是默认值(建议在实际使用时尽量给出默认值,以免由于key没有定义导致运行时错误)。
Apollo从v0.10.0开始的版本支持placeholder在运行时自动更新。如果需要关闭placeholder在运行时自动更新功能,可以通过以下两种方式关闭:
/**
* Java Config方式
**/
@Configuration
public class JavaConfigBean {
@Value("${timeout:20}")
private int timeout;
public int getTimeout() {
return timeout;
}
}
@Autowired
JavaConfigBean javaConfigBean;
@RequestMapping("/index1")
public String hello1(){
return javaConfigBean.getTimeout()+"";
}
Spring Boot提供了@ConfigurationProperties把配置注入到bean对象中。Apollo也支持这种方式,下面的例子会把redis.cache.expireSeconds和redis.cache.commandTimeout分别注入到SampleRedisConfig的expireSeconds和commandTimeout字段中。
redis.cache.commandTimeout=3000
redis.cache.expireSeconds=20
/**
* ConfigurationProperties使用方式
**/
@Configuration
@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;
}
public int getExpireSeconds() {
return expireSeconds;
}
public int getCommandTimeout() {
return commandTimeout;
}
}
@Autowired
SampleRedisConfig sampleRedisConfig;
@RequestMapping("/index2")
public String hello2(){
return sampleRedisConfig.getCommandTimeout()+"--"+sampleRedisConfig.getExpireSeconds();
}
Apollo同时还增加了几个新的Annotation来简化在Spring环境中的使用。
@ApolloConfig
private Config config;
@RequestMapping("/index3")
public String hello3(){
Set propertyNames = config.getPropertyNames();
propertyNames.forEach(key -> {
System.err.println(key+"="+config.getIntProperty(key,0));
});
return propertyNames.toString();
}
redis.cache.commandTimeout=3000
redis.cache.expireSeconds=20
server.port=800
timeout=200
@ApolloConfigChangeListener
private void someOnChange(ConfigChangeEvent changeEvent) {
//update injected value of batch if it is changed in Apollo
if (changeEvent.isChanged("timeout")) {
System.out.println(config.getIntProperty("timeout", 0));
}
}
/**
* 用户
**/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
服务端新增配置
jsonBeanProperty=[ { “username”: “john”, “password”: “1234” }, { “username”: “simon”, “password”: “222132” } ]
客户端获取配置
@ApolloJsonValue("${jsonBeanProperty:[]}")
private List anotherJsonBeans;
@RequestMapping("/index4")
public void hello4(){
anotherJsonBeans.forEach(item -> {
System.err.println(item.getUsername()+"--"+item.getPassword());
});
}
参考: