背景
? ? 隨著程序功能的日益復(fù)雜妹蔽,系統(tǒng)的配置參數(shù)越來越多,應(yīng)用系統(tǒng)的配置信息變的越來越難以高效管理蔗喂。這里有兩個(gè)原因:
? ? 微服務(wù)的流行。隨著企業(yè)的發(fā)展高帖,用戶量的增長缰儿,應(yīng)用服務(wù)越來越多。這里的多有兩個(gè)方面:一方面是應(yīng)用服務(wù)的種類越來越多散址,另一方便是單一應(yīng)用服務(wù)走向集群部署乖阵。種類越來越多導(dǎo)致了應(yīng)用系統(tǒng)的配置參數(shù)分散在各個(gè)微服務(wù)中,如果配置信息發(fā)生變化预麸,則需要去修改對應(yīng)的應(yīng)用服務(wù)配置文件甚至需要去重啟服務(wù)瞪浸,這樣維護(hù)起來繁瑣吏祸,更重要的是容易出錯(cuò)。單一應(yīng)用服務(wù)走向集群部署贡翘,同樣當(dāng)配置信息發(fā)生變化時(shí),需要依次去修改集群中所有該服務(wù)的配置信息鸣驱,這樣會導(dǎo)致同一件事情重復(fù)的做泛鸟,就不符合互聯(lián)網(wǎng)要解決的根本問題:提升生產(chǎn)效率踊东,解放生產(chǎn)力。
? ? 企業(yè)級技術(shù)架構(gòu)的升級闸翅。企業(yè)在發(fā)展的各個(gè)階段再芋,需要形成與之相匹配相適應(yīng)的技術(shù)戰(zhàn)略布局用來服務(wù)于當(dāng)前的業(yè)務(wù)發(fā)展。在這個(gè)不斷變革升級的過程中缎脾,需要形成協(xié)同遗菠,高效,統(tǒng)一豁遭,管控有力的企業(yè)級具有戰(zhàn)略性的技術(shù)體系贺拣。因此捂蕴,分布式配置中心作為企業(yè)技術(shù)體系戰(zhàn)略布局中的重要一環(huán)闪幽,在滿足以往業(yè)務(wù)在迭代中要分環(huán)境,可用的硬性要求的同時(shí)溉知,還需要形成新的生命力腕够。這個(gè)新的生命力就是:安全可靠帚湘,權(quán)限可控,維護(hù)簡單捅厂,能夠熱修改底挫。
? ? 攜程的開源項(xiàng)目 apollo 無疑是一把提供企業(yè)分布式配置中心解決方案的利器脸侥。今天我們就來分享一下基于 攜程開源項(xiàng)目 apollo 的分布式配置中心解決方案睁枕。
需求分析
? ?在應(yīng)用服務(wù)的開發(fā)過程中為了軟件的快速迭代,我們通常會隔離出3套環(huán)境:dev(開發(fā)環(huán)境)外遇,fat(測試環(huán)境)跳仿,pro(生產(chǎn)環(huán)境)。不同的軟件環(huán)境對系統(tǒng)的穩(wěn)定性和可靠性的要求不同妄辩,通常我們對生產(chǎn)環(huán)境的穩(wěn)定性和可靠性要求是苛刻的山上,而開發(fā)和測試環(huán)境則相對略低。本著在能滿足要求的情況下哮伟,最低成本的原則我們來分配資源來搭建環(huán)境。
資源分配
機(jī)器 IP | 環(huán)境 | 部署應(yīng)用 | 數(shù)據(jù)庫 |
---|---|---|---|
apollo01(192.168.0.111) | pro (生產(chǎn)環(huán)境) | protal | ProtalDB |
apollo02(192.168.0.112) | pro (生產(chǎn)環(huán)境) | config,admin | ConfigDB_Pro |
apollo03(192.168.0.113) | pro (生產(chǎn)環(huán)境) | config,admin | ConfigDB_Pro |
apollo04(192.168.0.114) | dev (開發(fā)環(huán)境) | config,admin | ConfigDB_Dev |
apollo05(192.168.0.115) | fat (測試環(huán)境) | config,admin | ConfigDB_Fat |
說明:
1.apollo依賴于注冊中心eureka池凄,這里還需要搭建 dev修赞,fat桑阶,pro 三套環(huán)境的注冊中心。注冊中心可以搭建高單機(jī)版或高可用版割择,這個(gè)并非本次演示重點(diǎn)萎河,這里不再做具體搭建說明虐杯。
2.apollo依賴與mysql數(shù)據(jù)庫,這里也不再做具體搭建說明擎椰。通常生產(chǎn)環(huán)境建議使用高可用版本的數(shù)據(jù)庫达舒。
環(huán)境搭建
1.初始化數(shù)據(jù)庫:
ProtalDB數(shù)據(jù)庫執(zhí)行腳本:apolloportaldb.sql
ConfigDB_Pro, ConfigDB_Dev, ConfigDB_Fat 數(shù)據(jù)庫執(zhí)行sql腳本:apolloconfigdb.sql
注意:數(shù)據(jù)庫名要和 sql 腳本中的庫名保持一致巩搏。
2.下載 apollo 源碼 并打包:
3.將對應(yīng)的zip包分別上傳到對應(yīng)的服務(wù)器目錄下:
機(jī)器 | 環(huán)境 | 上傳zip | zip上傳位置 |
---|---|---|---|
apollo01 | pro (生產(chǎn)環(huán)境) | apollo-protal-1.7.0.zip | /opt/apollo/protal |
apollo02 | pro (生產(chǎn)環(huán)境) | apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip | /opt/apollo/configservice 和 /opt/apollo/adminservice |
apollo03 | pro (生產(chǎn)環(huán)境) | apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip | /opt/apollo/configservice 和 /opt/apollo/adminservice |
apollo04 | dev (開發(fā)環(huán)境) | apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip | /opt/apollo/configservice 和 /opt/apollo/adminservice |
apollo05 | fat (測試環(huán)境) | apollo-adminservice-1.7.0.zip 和 apollo-configservice-1.7.0.zip | /opt/apollo/configservice 和 /opt/apollo/adminservice |
4.注冊中心:
環(huán)境 | key | Value |
---|---|---|
pro (生產(chǎn)環(huán)境) | eureka.service.url | http://1.1.1.1:8080/eureka/ |
dev (開發(fā)環(huán)境) | eureka.service.url | http://1.1.1.2:8080/eureka/ |
fat (測試環(huán)境) | eureka.service.url | http://1.1.1.3:8080/eureka/ |
5.修改相應(yīng)的配置文件:
配置apollo-portal的meta service信息:
? ? Apollo Portal需要在不同的環(huán)境訪問不同的meta service(apollo-configservice)地址丰辣,所以我們需要在配置中提供這些信息禽捆。默認(rèn)情況下,meta service和config service是部署在同一個(gè)JVM進(jìn)程得湘,所以meta service的地址就是config service的地址顿仇。
修改完的效果如下:
dev.meta = http://apollo02:8080,http://apollo03:8080
fat.meta = http://apollo04:8080
pro.meta = http://apollo05:8080
配置apollo-adminservice的數(shù)據(jù)庫連接信息:
解壓apollo-adminservice-x.x.x-github.zip
打開config目錄下的application-github.properties文件
填寫正確的ApolloConfigDB數(shù)據(jù)庫連接串信息摆马,注意用戶名和密碼后面不要有空格!
改完的效果如下:
# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloConfigDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd
注:由于ApolloConfigDB在每個(gè)環(huán)境都有部署囤采,所以對不同的環(huán)境admin-service需要配置對應(yīng)環(huán)境的數(shù)據(jù)庫參數(shù)
配置apollo-configservice的數(shù)據(jù)庫連接信息:
解壓apollo-adminservice-x.x.x-github.zip
用程序員專用編輯器(如vim惩淳,notepad++思犁,sublime等)打開config目錄下的application-github.properties文件
填寫正確的ApolloConfigDB數(shù)據(jù)庫連接串信息,注意用戶名和密碼后面不要有空格!
修改完的效果如下:
# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloConfigDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd
注:由于ApolloConfigDB在每個(gè)環(huán)境都有部署棉磨,所以對不同的環(huán)境admin-service需要配置對應(yīng)環(huán)境的數(shù)據(jù)庫參數(shù)
配置apollo-portal的數(shù)據(jù)庫連接信息:
解壓apollo-portal-x.x.x-github.zip
打開config目錄下的application-github.properties文件
填寫正確的ApolloAdminDB數(shù)據(jù)庫連接串信息学辱,注意用戶名和密碼后面不要有空格!
修改完的效果如下:
# DataSource
spring.datasource.url = jdbc:mysql://localhost:3306/ApolloAdminDB?useSSL=false&characterEncoding=utf8
spring.datasource.username = someuser
spring.datasource.password = somepwd
調(diào)整ApolloConfigDB配置:
? ? 不管是apollo-configservice還是apollo-adminservice都需要向eureka服務(wù)注冊策泣,所以需要配置eureka服務(wù)地址。 按照目前的實(shí)現(xiàn)统抬,apollo-configservice本身就是一個(gè)eureka服務(wù)任洞,所以只需要填入apollo-configservice的地址即可发侵,如有多個(gè),用逗號分隔(注意不要忘了/eureka/后綴)盅弛。
在PRO環(huán)境的ApolloConfigDB.ServerConfig表中設(shè)置eureka.service.url為:
http://1.1.1.1:8080/eureka/
在DEV環(huán)境的ApolloConfigDB.ServerConfig表中設(shè)置eureka.service.url為:
http://1.1.1.2:8080/eureka/
在FAT環(huán)境的ApolloConfigDB.ServerConfig表中設(shè)置eureka.service.url為:
http://1.1.1.3:8080/eureka/
6.啟動各個(gè)服務(wù):
啟動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
使用說明
系統(tǒng)啟動成功后可以訪問 apollo 配置中心的管理頁面:http://apollo01:8070
注意:需要將 url 中的 apollo01 替換成你部署 protal 的機(jī)器IP
首次登陸的用戶名是: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配置文件慧邮,因?yàn)槲疫@里有dev(開發(fā)環(huán)境)舟陆,fat(測試環(huán)境)秦躯,pro(生產(chǎn)環(huán)境) 三套環(huán)境,具體配置如下:
基礎(chǔ)配置文件:application.yml
spring:
profiles:
## 使用 pro 環(huán)境
active: pro
app:
id: message
apollo:
bootstrap:
enabled: true
## 添加使用到的 namespace
namespaces: application,grobal
eagerLoad:
enabled: true
autoUpdateInjectedSpringProperties: true
dev(開發(fā)環(huán)境): application-dev.yml
apollo:
meta: http://192.168.0.114:8080
fat(測試環(huán)境):application-fat.yml
apollo:
meta: http://192.168.0.115:8080
fat(生產(chǎn)環(huán)境):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.注冊監(jiān)聽器,監(jiān)聽apollo中屬性配置的變化:
@ApolloConfigChangeListener 監(jiān)聽的是默認(rèn)的 namespace毡琉,如果需要監(jiān)聽指定的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()));
}
}
}