Apollo(阿波羅)是攜程框架部門研發的分布式配置中心,能夠集中化管理應用不同環境、不同集群的配置,配置修改后能夠實時推送到應用端,并且具備規范的權限、流程治理等特性,適用于微服務配置管理場景。
服務端基于Spring Boot和Spring Cloud開發,打包后可以直接運行,不需要額外安裝Tomcat等應用容器。
Java客戶端不依賴任何框架,能夠運行于所有Java運行時環境,同時對Spring/Spring Boot環境也有較好的支持。
.Net客戶端不依賴任何框架,能夠運行于所有.Net運行時環境。
如果想要深入了解,可以到github上參見Apollo配置中心,官網的介紹很詳細。本章主要講述Spring Boot 2.0 整合Apollo配置中心。
一、Apollo配置中心服務端(來源于官網)
本文的重點在于Apollo在客戶端的使用,所以Apollo服務端使用的是官網提供的 Quick Start(針對本地測試使用),后續文章會專門講述Apollo服務端在分布式環境下的部署。
1.1 準備工作
Java
Apollo服務端要求Java 1.8+,客戶端要求Java 1.7+,筆者本地使用的是Java 1.8。MySQL
Apollo的表結構對timestamp使用了多個default聲明,所以需要5.6.5以上版本。筆者本地使用的是8.0.13版本下載 Quick Start
官網為我們準備了 Quick Start 安裝包。大家只需要下載到本地,就可以直接使用,免去了編譯、打包過程。大家可以到github下載,也可以通過百度云盤下載
1.2 安裝步驟
1.2.1 創建數據庫
Apollo服務端共需要兩個數據庫:ApolloPortalDB和ApolloConfigDB,官網把數據庫、表的創建和樣例數據都分別準備了sql文件(在下載的 Quick Start 安裝包的sql目錄下),只需要導入數據庫即可。
1.2.1.1 創建ApolloPortalDB
通過各種Mysql客戶端(Navicat,DataGrip等)導入sql/apolloportaldb.sql
即可
下面以MySQL原生客戶端為例:
source /your_local_path/sql/apolloportaldb.sql
導入成功后,可以通過執行以下sql語句來驗證:
select `Id`, `AppId`, `Name` from ApolloPortalDB.App;
Id | AppId | Name |
---|---|---|
1 | SampleApp | Sample App |
通過各種Mysql客戶端(Navicat,DataGrip等)導入sql/apolloconfigdb.sql
即可
下面以MySQL原生客戶端為例:
source /your_local_path/sql/apolloconfigdb.sql
導入成功后,可以通過執行以下sql語句來驗證:
select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item;
NamespaceId Key Value Comment
1 timeout 100 sample
1.2.2 配置數據庫連接信息
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=密碼(如果沒有密碼,留空即可)
1.3 啟動Apollo配置中心
1.3.1 確保端口未被占用
Quick Start腳本會在本地啟動3個服務,分別使用8070, 8080, 8090端口,請確保這3個端口當前沒有被使用。例如,在Linux/Mac下,可以通過如下命令檢查:
lsof -i:8080
在windows下,可以通過如下命令檢查:
netstat -aon|findstr "8080"
1.3.2 執行啟動腳本
在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!
1.3.3 異常排查
如果啟動遇到了異常,可以分別查看service和portal目錄下的log文件排查問題。
注: 在啟動apollo-configservice的過程中會在日志中輸出eureka注冊失敗的信息,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused。需要注意的是,這個是預期的情況,因為apollo-configservice需要向Meta Server(它自己)注冊服務,但是因為在啟動過程中,自己還沒起來,所以會報這個錯。后面會進行重試的動作,所以等自己服務起來后就會注冊正常了。
1.4 使用Apollo配置中心
1.4.1 查看樣例配置
瀏覽器訪問http://localhost:8070
Quick Start集成了Spring Security簡單認證,更多信息可以參考Portal 實現用戶登錄功能
輸入用戶名apollo,密碼admin登錄
配置中心中包含一個默認的項目SampleApp
點擊SampleApp進入配置界面,可以看到當前有一個配置timeout=100
如果提示系統出錯,請重試或聯系系統負責人,請稍后幾秒鐘重試一下,因為通過Eureka注冊的服務有一個刷新的延時。
1.4.2 新增項目配置
我們的客戶端使用apollo需要新增相關的項目配置。
點擊新建項目
點擊提交,創建完成
應用ID:這個ID是應用的唯一標識
應用名稱:應用的名稱,會展示在配置中心的首頁上
新增配置信息
點擊新增配置,填寫配置信息
點擊提交,此時配置還未生效。
發布配置
點擊發布,配置立刻生效
回滾
如果配置做了修改之后,發現配置更改錯誤,這個時候可以使用回滾功能,回到上一個配置
二、Apollo配置中心客戶端
我們客戶端基于Spring Boot 2.0搭建,開發工具是InteIIij IDEA。新建一個項目,項目名稱為apollo-client
2.1 客戶端搭建
添加Apollo客戶端依賴
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>1.1.1</version>
</dependency>
添加配置信息
# 應用ID(在Apollo服務端新增項目添加的應用ID)
app.id=testclient
# apollo-configservice地址
apollo.meta=http://127.0.0.1:808
在項目的啟動類上添加@EnableApolloConfig
注解
新增一個測試接口
@RequestMapping("/index")
public String hello(){
return "hello man";
}
啟動服務測試
在Apollo配置中心中,我們對該項目有一條配置server.port = 9000
,啟動服務,訪問http://localhost:9000/index
,返回hello man
。證明,客戶端是從服務端獲取的配置。
2.2 客戶端用法
在上一節,我們簡單的搭建了客戶端,成功的使用服務端配置。Apollo為我們提供的使用方式有很多種,下面只介紹Spring Boot 2.0環境下的使用方式。
2.2.1 Spring Placeholder的使用
Spring應用通常會使用Placeholder來注入配置,使用的格式形如${someKey:someDefaultValue}
,如${timeout:100}
。冒號前面的是key,冒號后面的是默認值(建議在實際使用時盡量給出默認值,以免由于key沒有定義導致運行時錯誤)。Apollo從v0.10.0開始的版本支持placeholder在運行時自動更新。如果需要關閉placeholder在運行時自動更新功能,可以通過以下兩種方式關閉:
通過設置System Property apollo.autoUpdateInjectedSpringProperties
,如啟動時傳入-Dapollo.autoUpdateInjectedSpringProperties=false
通過設置META-INF/app.properties中的apollo.autoUpdateInjectedSpringProperties=false
2.2.1.1 Java Config使用方式
新建配置類JavaConfigBean
如下:
/**
* Java Config方式
*
* @author simon
* @create 2018-11-02 15:00
**/
@Configuration
public class JavaConfigBean {
@Value("${timeout:20}")
private int timeout;
public int getTimeout() {
return timeout;
}
}
新增訪問端點
//1.Java Config方式
@Autowired
JavaConfigBean javaConfigBean;
@RequestMapping("/index1")
public String hello1(){
return javaConfigBean.getTimeout()+"";
}
測試
瀏覽器訪問http://127.0.0.1:8080/index1
,正確返回配置的值
2.2.1.2 ConfigurationProperties使用方式
Spring Boot提供了@ConfigurationProperties
把配置注入到bean對象中。Apollo也支持這種方式,下面的例子會把redis.cache.expireSeconds
和redis.cache.commandTimeout
分別注入到SampleRedisConfig
的expireSeconds
和commandTimeout
字段中。
新增配置類SampleRedisConfig
如下:
/**
* ConfigurationProperties使用方式
*
* @author simon
* @create 2018-11-02 9:30
**/
@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;
}
}
新增訪問端點
//2. ConfigurationProperties使用方式
@Autowired
SampleRedisConfig sampleRedisConfig;
@RequestMapping("/index2")
public String hello2(){
return sampleRedisConfig.getCommandTimeout()+"--"+sampleRedisConfig.getExpireSeconds();
}
測試
瀏覽器訪問http://127.0.0.1:8080/index2
,正確返回配置的值
注: @ConfigurationProperties
如果需要在Apollo配置變化時自動更新注入的值,需要配合使用EnvironmentChangeEvent
或RefreshScope
。這個我會在后續文章中詳細描述。
2.2.2 Spring Annotation支持
Apollo同時還增加了幾個新的Annotation來簡化在Spring環境中的使用。
@ApolloConfig
用來自動注入Config對象
@ApolloConfigChangeListener
用來自動注冊ConfigChangeListener
@ApolloJsonValue
用來把配置的json字符串自動注入為對象
2.2.2.1 @ApolloConfig
的使用
新增訪問端點
// 3. @ApolloConfig使用
@ApolloConfig
private Config config;
@RequestMapping("/index3")
public String hello3(){
Set <String> propertyNames = config.getPropertyNames();
propertyNames.forEach(key -> {
System.err.println(key+"="+config.getIntProperty(key,0));
});
return propertyNames.toString();
}
測試
瀏覽器訪問http://127.0.0.1:8080/index3
,正確打印配置的值
redis.cache.commandTimeout=3000
redis.cache.expireSeconds=20
server.port=800
timeout=200
@ApolloConfigChangeListener
的使用
新增以下代碼
@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));
}
}
測試
在Apollo服務端修改timeout
配置的值為300
,發布后,控制臺打印300
2.2.2.3 @ApolloJsonValue
的使用
新增User如下:
/**
* 用戶
*
* @author simon
* @create 2018-11-02 16:41
**/
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" } ]
客戶端獲取配置
//4. @ApolloJsonValue使用
@ApolloJsonValue("${jsonBeanProperty:[]}")
private List<User> anotherJsonBeans;
@RequestMapping("/index4")
public void hello4(){
anotherJsonBeans.forEach(item -> {
System.err.println(item.getUsername()+"--"+item.getPassword());
});
}
測試
瀏覽器訪問http://127.0.0.1:8080/index4
,正確打印配置的值
源碼下載
https://github.com/simondongji/SpringCloudProject/tree/master/apollo