背景
隨着程序功能的日益複雜,程序的配置日益增多:各類功能的開關、參數的配置、服務器的地址等等。html
對程序配置的指望值也愈來愈高:配置修改後實時生效,灰度發佈,分環境、分集羣管理配置,完善的權限、審覈機制等等。java
在這樣的大環境下,傳統的經過配置文件、數據庫等方式已經愈來愈沒法知足開發人員對配置管理的需求。mysql
Apollo 配置中心應運而生!Apollo - 一個可靠的配置管理系統。git
Apollo 介紹
Apollo(阿波羅)是攜程框架部門研發的分佈式配置中心,可以集中化管理應用不一樣環境、不一樣集羣的配置,配置修改後可以實時推送到應用端,而且具有規範的權限、流程治理等特性,適用於微服務配置管理場景。服務端基於 Spring Boot 和 Spring Cloud 開發,打包後能夠直接運行,不須要額外安裝 Tomcat 等應用容器。github
Apollo 支持 4 個維度管理 Key-Value 格式的配置:web
- application (應用)
- environment (環境)
- cluster (集羣)
- namespace (命名空間 Namespace 是配置項的集合,相似於一個配置文件的概念)
同時,Apollo 基於開源模式開發,開源地址:https://github.com/ctripcorp/apollospring
官網文檔:https://github.com/ctripcorp/apollo/wiki/Quick-Startsql
演示環境(Demo):shell
- http://106.54.227.205/
- 帳號/密碼:apollo/admin
上圖是Apollo配置中心中一個項目的配置首頁數據庫
- 在頁面左上方的環境列表模塊展現了全部的環境和集羣,用戶能夠隨時切換。
- 頁面中央展現了兩個namespace(application和FX.apollo)的配置信息,默認按照表格模式展現、編輯。用戶也能夠切換到文本模式,以文件形式查看、編輯。
- 頁面上能夠方便地進行發佈、回滾、灰度、受權、查看更改歷史和發佈歷史等操做。
Apollo 核心概念
- application (應用)
- 這個很好理解,就是實際使用配置的應用,Apollo客戶端在運行時須要知道當前應用是誰,從而能夠去獲取對應的配置
- 每一個應用都須要有惟一的身份標識 -- appId,咱們認爲應用身份是跟着代碼走的,因此須要在代碼中配置,具體信息請參見Java客戶端使用指南。
- environment (環境)
- 配置對應的環境,Apollo客戶端在運行時須要知道當前應用處於哪一個環境,從而能夠去獲取應用的配置
- 咱們認爲環境和代碼無關,同一份代碼部署在不一樣的環境就應該可以獲取到不一樣環境的配置
- 因此環境默認是經過讀取機器上的配置(server.properties中的env屬性)指定的,不過爲了開發方便,咱們也支持運行時經過System Property等指定,具體信息請參見Java客戶端使用指南。
- cluster (集羣)
- 一個應用下不一樣實例的分組,好比典型的能夠按照數據中心分,把上海機房的應用實例分爲一個集羣,把北京機房的應用實例分爲另外一個集羣。
- 對不一樣的cluster,同一個配置能夠有不同的值,如zookeeper地址。
- 集羣默認是經過讀取機器上的配置(server.properties中的idc屬性)指定的,不過也支持運行時經過System Property指定,具體信息請參見Java客戶端使用指南。
- namespace (命名空間)
- 一個應用下不一樣配置的分組,能夠簡單地把namespace類比爲文件,不一樣類型的配置存放在不一樣的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等
- 應用能夠直接讀取到公共組件的配置namespace,如DAL,RPC等
- 應用也能夠經過繼承公共組件的配置namespace來對公共組件的配置作調整,如DAL的初始數據庫鏈接數
Apollo 特性
- 統一管理不一樣環境、不一樣集羣的配置
- Apollo提供了一個統一界面集中式管理不一樣環境(environment)、不一樣集羣(cluster)、不一樣命名空間(namespace)的配置。
- 同一份代碼部署在不一樣的集羣,能夠有不一樣的配置,好比zk的地址等
- 經過命名空間(namespace)能夠很方便的支持多個不一樣應用共享同一份配置,同時還容許應用對共享的配置進行覆蓋
- 配置界面支持多語言(中文,English)
- 配置修改實時生效(熱發佈)
- 用戶在Apollo修改完配置併發布後,客戶端能實時(1秒)接收到最新的配置,並通知到應用程序。
- 版本發佈管理
- 全部的配置發佈都有版本概念,從而能夠方便的支持配置的回滾。
- 灰度發佈
- 支持配置的灰度發佈,好比點了發佈後,只對部分應用實例生效,等觀察一段時間沒問題後再推給全部應用實例。
- 權限管理、發佈審覈、操做審計
- 應用和配置的管理都有完善的權限管理機制,對配置的管理還分爲了編輯和發佈兩個環節,從而減小人爲的錯誤。
- 全部的操做都有審計日誌,能夠方便的追蹤問題。
- 客戶端配置信息監控
- 能夠方便的看到配置在被哪些實例使用
- 提供Java和.Net原生客戶端
- 提供了Java和.Net的原生客戶端,方便應用集成
- 支持Spring Placeholder,Annotation和Spring Boot的ConfigurationProperties,方便應用使用(須要Spring 3.1.1+)
- 同時提供了Http接口,非Java和.Net應用也能夠方便的使用
- 提供開放平臺API
- Apollo自身提供了比較完善的統一配置管理界面,支持多環境、多數據中心配置管理、權限、流程治理等特性。
- 不過Apollo出於通用性考慮,對配置的修改不會作過多限制,只要符合基本的格式就可以保存。
- 在咱們的調研中發現,對於有些使用方,它們的配置可能會有比較複雜的格式,如xml, json,須要對格式作校驗。
- 還有一些使用方如DAL,不只有特定的格式,並且對輸入的值也須要進行校驗後方可保存,如檢查數據庫、用戶名和密碼是否匹配。
- 對於這類應用,Apollo支持應用方經過開放接口在Apollo進行配置的修改和發佈,而且具有完善的受權和權限控制
- 部署簡單
- 配置中心做爲基礎服務,可用性要求很是高,這就要求Apollo對外部依賴儘量地少
- 目前惟一的外部依賴是MySQL,因此部署很是簡單,只要安裝好Java和MySQL就可讓Apollo跑起來
- Apollo還提供了打包腳本,一鍵就能夠生成全部須要的安裝包,而且支持自定義運行時參數
Apollo 整體設計
官方文檔:https://github.com/ctripcorp/apollo/wiki/Apollo配置中心設計
架構模塊
上圖簡要描述了 Apollo 的整體設計,咱們能夠從下往上看:
- Config Service提供配置的讀取、推送等功能,服務對象是Apollo客戶端(咱們本身的微服務應用)
- Admin Service提供配置的修改、發佈等功能,服務對象是Apollo Portal(管理界面)
- Config Service和Admin Service都是多實例、無狀態部署,因此須要將本身註冊到Eureka中並保持心跳
- 在Eureka之上咱們架了一層Meta Server用於封裝Eureka的服務發現接口
- Client經過域名訪問Meta Server獲取Config Service服務列表(IP+Port),然後直接經過IP+Port訪問服務,同時在Client側會作load balance、錯誤重試
- Portal經過域名訪問Meta Server獲取Admin Service服務列表(IP+Port),然後直接經過IP+Port訪問服務,同時在Portal側會作load balance、錯誤重試
- 爲了簡化部署,咱們實際上會把Config Service、Eureka和Meta Server三個邏輯角色部署在同一個JVM進程中
1.3 各模塊概要介紹
1.3.1 Config Service
- 提供配置獲取接口
- 提供配置更新推送接口(基於Http long polling)
- 服務端使用Spring DeferredResult實現異步化,從而大大增長長鏈接數量
- 目前使用的tomcat embed默認配置是最多10000個鏈接(能夠調整),使用了4C8G的虛擬機實測能夠支撐10000個鏈接,因此知足需求(一個應用實例只會發起一個長鏈接)。
- 接口服務對象爲Apollo客戶端
1.3.2 Admin Service
- 提供配置管理接口
- 提供配置修改、發佈等接口
- 接口服務對象爲Portal
1.3.3 Meta Server
- Portal經過域名訪問Meta Server獲取Admin Service服務列表(IP+Port)
- Client經過域名訪問Meta Server獲取Config Service服務列表(IP+Port)
- Meta Server從Eureka獲取Config Service和Admin Service的服務信息,至關因而一個Eureka Client
- 增設一個Meta Server的角色主要是爲了封裝服務發現的細節,對Portal和Client而言,永遠經過一個Http接口獲取Admin Service和Config Service的服務信息,而不須要關心背後實際的服務註冊和發現組件
- Meta Server只是一個邏輯角色,在部署時和Config Service是在一個JVM進程中的,因此IP、端口和Config Service一致
1.3.4 Eureka
- 基於Eureka和Spring Cloud Netflix提供服務註冊和發現
- Config Service和Admin Service會向Eureka註冊服務,並保持心跳
- 爲了簡單起見,目前Eureka在部署時和Config Service是在一個JVM進程中的(經過Spring Cloud Netflix)
1.3.5 Portal
- 提供Web界面供用戶管理配置
- 經過Meta Server獲取Admin Service服務列表(IP+Port),經過IP+Port訪問服務
- 在Portal側作load balance、錯誤重試
1.3.6 Client
- Apollo提供的客戶端程序,爲應用提供配置獲取、實時更新等功能
- 經過Meta Server獲取Config Service服務列表(IP+Port),經過IP+Port訪問服務
- 在Client側作load balance、錯誤重試
服務端
上圖簡要描述了配置發佈的大體過程:
- 用戶在Portal操做配置發佈
- Portal調用Admin Service的接口操做發佈
- Admin Service發佈配置後,發送ReleaseMessage給各個Config Service
- Config Service收到ReleaseMessage後,通知對應的客戶端
客戶端
上圖簡要描述了Apollo客戶端的實現原理:
- 客戶端和服務端保持了一個長鏈接,從而能第一時間得到配置更新的推送。(經過Http Long Polling實現)
- 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。
- 這是一個fallback機制,爲了防止推送機制失效致使配置不更新
- 客戶端定時拉取會上報本地版本,因此通常狀況下,對於定時拉取的操做,服務端都會返回304 - Not Modified
- 定時頻率默認爲每5分鐘拉取一次,客戶端也能夠經過在運行時指定System Property:
apollo.refreshInterval
來覆蓋,單位爲分鐘。
- 客戶端從Apollo配置中心服務端獲取到應用的最新配置後,會保存在內存中
- 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份
- 在遇到服務不可用,或網絡不通的時候,依然能從本地恢復配置
- 應用程序能夠從Apollo客戶端獲取最新的配置、訂閱配置更新通知
環境準備
點擊連接觀看:Apollo 搭建服務端視頻(獲取更多請關注公衆號「哈嘍沃德先生」)
Java
- Apollo 服務端:1.8+
- Apollo 客戶端:1.7+
因爲須要同時啓動服務端和客戶端,因此建議安裝Java 1.8+。
MySQL
- 版本要求:5.6.5+
Apollo的表結構對timestamp
使用了多個default聲明,因此須要5.6.5以上版本。
下載Quick Start安裝包
Apollo 給咱們準備好了一個Quick Start安裝包,你們只須要下載到本地,就能夠直接使用,免去了編譯、打包過程。
安裝包共50M,若是訪問github網速不給力的話,能夠從百度網盤下載。
- 從Github下載
- checkout或下載apollo-build-scripts項目
- 因爲Quick Start項目比較大,因此放在了另外的repository,請注意項目地址
- 從百度網盤下載
- 經過https://github.com/ctripcorp/apollo/wiki/Quick-Start#13-下載quick-start安裝包頁面的網盤連接下載
- 下載到本地後,在本地解壓apollo-quick-start.zip
- 爲啥安裝包要58M這麼大?
- 由於這是一個能夠自啓動的jar包,裏面包含了全部依賴jar包以及一個內置的tomcat容器
安裝 Apollo
建立數據庫
Apollo 服務端共須要兩個數據庫:ApolloPortalDB
和ApolloConfigDB
,咱們把數據庫、表的建立和樣例數據都分別準備了 sql 文件,只須要導入數據庫便可。
注意:若是你本地已經建立過Apollo數據庫,請注意備份數據。咱們準備的sql文件會清空Apollo相關的表。
建立 ApolloPortalDB 數據庫
經過各類MySQL客戶端導入sql/apolloportaldb.sql便可。
建立 ApolloConfigDB 數據庫
經過各類MySQL客戶端導入sql/apolloconfigdb.sql便可。
配置數據庫鏈接信息
Apollo 服務端須要知道如何鏈接到你前面建立的數據庫,因此須要編輯demo.sh,修改 ApolloPortalDB 和 ApolloConfigDB 相關的數據庫鏈接串信息。
注意:填入的用戶須要具有對 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=密碼(若是沒有密碼,留空便可)
注意:不要修改 demo.sh 的其它部分
搭建服務端
確保端口未被佔用
Quick Start腳本會在本地啓動3個服務,分別使用8070, 8080, 8090端口,請確保這3個端口當前沒有被使用。
執行啓動腳本
./demo.sh start
Apollo 提供的腳本文件爲 .sh
文件,若是你的安裝環境是在 Linux 系統下直接運行以上命令便可,若是你想在 Windows 環境下運行該腳本,先安裝 Git 而後在 demo.sh
所在目錄下鼠標右鍵點擊 Git Bash Here
,而後再經過以上命令運行腳本便可。
當看到以下輸出後,就說明啓動成功了!
==== 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,輸入用戶名 apollo,密碼 admin 後登陸。
登陸成功後,首頁以下,Apollo 還提供了一個 SampleApp
樣本案例供咱們學習使用。
建立項目
點擊對應按鈕建立項目。
這裏先經過默認的樣例部門演示(後面我會講如何添加部門),AppId 對應客戶端配置文件中 app.id。
建立成功以下圖。
客戶端接入服務端
點擊連接觀看:Apollo 客戶端接入服務端視頻(獲取更多請關注公衆號「哈嘍沃德先生」)
下面經過最經常使用、便捷的方式,即基於 Spring Boot 的集成方式來接入服務端。
apollo-demo
聚合工程。Spring Boot 2.2.4.RELEASE
order-service
:訂單服務,端口9090
order-service02
:訂單服務,端口9091
添加依賴
<!-- https://mvnrepository.com/artifact/com.ctrip.framework.apollo/apollo-client --> <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>1.6.0</version> </dependency>
配置文件
order-service
和 order-service02
的配置信息除端口外一致。
server: port: 9090 # 端口 spring: application: name: order-service # 應用名稱 # apollo 相關配置 app: id: order-service # 與 Apollo 配置中心中的 AppId 一致 apollo: meta: http://localhost:8080 # Apollo 中的 Eureka 註冊中心地址 #cluster: # 指定 Apollo 集羣,相同集羣實例使用對應集羣的配置 #cacheDir: # 配置緩存目錄,網絡不可用時任然可提供配置服務 bootstrap: enable: true # 啓用 apollo env: DEV # 指定環境 # 自定義配置 name: order-service-dev mysql: host: localhost port: 3306 username: root password: root
配置文件實體類
order-service
和 order-service02
的配置文件實體類代碼一致。
package com.example.config; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component @Data @NoArgsConstructor @AllArgsConstructor public class ConfigProperties { @Value("${name}") private String name; @Value("${mysql.host}") private String mysqlHost; @Value("${mysql.port}") private Integer mysqlPort; @Value("${mysql.username}") private String mysqlUsername; @Value("${mysql.password}") private String mysqlPassword; }
控制層
order-service
和 order-service02
的控制層代碼一致。
package com.example.controller; import com.example.config.ConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class ConfigController { @Autowired private ConfigProperties configProperties; @Value("${name}") private String name; @GetMapping("/name") public String getName() { return configProperties.getName(); } @GetMapping("/mysql") public Map<Object, Object> getMySQLProperties() { // JDK9中的新特性,快速建立只讀集合。 return Map.of("host", configProperties.getMysqlHost(), "port", configProperties.getMysqlPort(), "username", configProperties.getMysqlUsername(), "password", configProperties.getMysqlPassword()); } }
啓動類
啓動類須要添加 @EnableApolloConfig
註解。
order-service
和 order-service02
的啓動類代碼一致。
package com.example; import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @EnableApolloConfig @SpringBootApplication public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } }
測試
修改配置信息前
訪問:http://localhost:9090/name 和 http://localhost:9091/name 結果以下:
訪問:http://localhost:9090/mysql 和 http://localhost:9091/mysql 結果以下:
新增配置信息
進入項目後點擊右上角的 新增配置
。
添加配置項 name
、mysql.username
、mysql.password
。
發佈配置信息
將剛纔添加的配置信息批量發佈至應用。
修改配置信息後
控制檯打印信息以下:
c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: order-service-dev-2.0, key: name, beanName: configController, field: com.example.controller.ConfigController.name c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.password, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlPassword c.f.a.s.p.AutoUpdateConfigChangeListener : Auto update apollo changed value successfully, new value: root123, key: mysql.username, beanName: configProperties, field: com.example.config.ConfigProperties.mysqlUsername
訪問:http://localhost:9090/name 和 http://localhost:9091/name 結果以下:
訪問:http://localhost:9090/mysql 和 http://localhost:9091/mysql 結果以下:
以上只是 Apollo 的入門教程,後面咱們會學習 Apollo 的更多高級玩法,好比多環境部署,高可用環境搭建等等。
下一篇咱們講解 Apollo 部門管理、用戶管理、配置管理、集羣管理,記得關注噢~
本文采用 知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議
。
你們能夠經過 分類
查看更多關於 Spring Cloud
的文章。
🤗 您的點贊
和轉發
是對我最大的支持。
📢 掃碼關注 哈嘍沃德先生
「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~