2020年了還不知道配置中心?


Hi,好久不見,我是你們的walking在這個高燃BGM下又和大家見面了😜。今天分享一個分布式、微服務架構中重要的一個組件:配置中心。

也許你們現在的項目不是分布式、微服務架構,沒有用到配置中心,也沒聽說過配置中心,對它完全很陌生,那你就很有必要閱讀本文了。

本文將從

1)什么是配置、什么是配置中心以及配置中心的誕生講起,

2)然后簡單介紹幾個不錯的開源配置中心產品,

3)接着會重點介紹攜程開源的分布式配置中心Apollo的架構以及基本的搭建與使用。

 

好了廢話不多說,開始...🌝🌝

 

什么是配置?

眾所周知,應用程序在啟動和運行的時候會去讀取一些「配置信息」,比如:數據庫連接參數、啟動參數、接口的超時時間、應用程序的端口等。「配置」,基本上伴隨着應用程序的整個生命周期。

不知道大家有沒有注意,配置其實它有一些比較明顯的特點,如下:

1、獨立於程序的只讀變量

1)配置是獨立於應用程序的,並且同一個程序在不同的配置下會有不同的行為;

2)其次,配置對於程序是只讀的,程序通過讀取配置來改變自己的行為,程序不應該去改變配置

2、伴隨應用的整個生命周期

1)配置貫穿於應用的整個生命周期,包括啟動和運行。應用在啟動時通過讀取配置來初始化,在運行時根據配置調整行為。比如:啟動時需要讀取服務的端口號、系統在運行過程中需要讀取定時策略執行定時任務、根據配置調整日志級別等。

3、多種加載方式

1)常見的加載方式有程序內部硬編碼、配置文件、環境變量、啟動參數、基於數據庫等,最常用的是配置文件和環境變量這兩種方式。

4、需要治理

1)權限控制:由於配置能改變程序的行為,不正確的配置甚至會造成災難,所以對配置的修改必須有比較完善的權限控制體系來治理;

2)對不同環境、集群進行配置管理:同一個程序在不同的環境(開發,測試,預生產、生產)、不同的集群(如不同的數據中心、服務器在全國很多地方都有機房部署集群的情況)經常需要有不同的配置,所以需要有完善的環境、集群配置管理的能力。

5、支持持久化

1)配置信息需要支持持久化,永久保存,重啟后依然能夠找到之前的配置,配置一旦生成就永久存在除非人為的刪除。

 

我們最常用的就是在項目的resources下創建各種properties文件放我們的各種配置

 

配置中心的誕生

 

配置中心的誕生和項目架構的演進有着密切的聯系。傳統單體應用存在一些潛在缺陷,如隨着規模的擴大,部署效率降低,團隊協作效率差,系統可靠性變差,維護困難,新功能上線周期長等,所以迫切需要一種新的架構去解決這些問題,而微服務( microservices )架構正是當下一種流行的解決方案。

不過,解決一個問題的同時,往往會面臨很多新的問題,所以微服務化的過程中伴隨着很多的挑戰,其中一個挑戰就是有關服務(應用)配置的。

1)當系統從一個單體應用,被拆分成分布式系統上一個個服務節點后,配置文件也必須跟着遷移(分割),這樣配置就分散了,各個服務都有自己的配置,隨着項目需求的不斷壯大發展,配置會越來越多,到最后繁瑣的配置文件會讓你越來越崩潰,稍不注意出個錯配置錯了就得修改配置重新打包部署,特別麻煩。

2)在集群部署的情況下,如果新版本的配置會給系統帶來很大的影響,我們往往會選擇灰度發布,即先發布部分服務器,進行測試,穩定后再將配置同步到所有服務器,如果說還用傳統的方式,那么我們就需要將配置文件一個個的修改然后重啟服務,雖然不需要我們開發自己去做,有運維,那也挺煩人的,運維發布完了,我們還得檢查他改的是不是正確,費時費力。

3)而且在系統不斷的迭代的過程中有些配置在多個服務之間都是相同或相近的,就會有很大的冗余。

所以在分布式、微服務這種大環境下,傳統的項目配置方式的弊端就慢慢的凸顯出來了,這個問題變得非常棘手,亟待一種管理配置、治理配置的解決方案。這時,配置中心就應運而生了。

 

什么是配置中心

 

配置中心,顧名思義,將配置中心化,說白了就是將配置從應用中抽取出來,統一管理,優雅的解決了配置的動態變更、權限管理、持久化、運維成本等問題。應用程序自身只是從配置中心拿到自己想要的配置,既不需要去添加管理配置的接口,也不需要自己去實現配置的持久化,更不需要去關心配置何時變化。配置與應用程序隔離開,單獨管理配置。

總得來說,配置中心就是一種統一管理各種應用配置的基礎服務組件。

在系統架構中,配置中心是整個微服務基礎架構體系中的一個組件,如下圖,它的功能看上去並不起眼,無非就是配置的管理和存取,但它是整個微服務架構中不可或缺的一環。

 

集中管理配置,那么就要將應用的配置作為一個單獨的服務抽離出來了,同理也需要解決新的問題,比如:版本管理(為了支持回滾),權限管理等。

總結一下,在傳統巨型單體應用紛紛轉向細粒度微服務架構的歷史進程中,配置中心是微服務化不可缺少的一個系統組件,在這種背景下中心化的配置服務即配置中心應運而生,一個合格的配置中心需要滿足一下功能:

  • 對配置項的讀取和修改提供簡單易用的API和操作界面

  • 添加新配置要夠簡單、直接、方便

  • 支持不同管理員對配置的修改的不同權限,以把控風險

  • 可以查看配置修改的歷史記錄,以便回滾

  • 不同部署環境相互隔離,互不影響

主流配置中心簡介

目前市面上用的比較多的配置中心有:(按開源時間排序)

簡介

1、Disconf

2014年7月百度開源的配置管理中心,專注於各種「分布式系統配置管理」的「通用組件」和「通用平台」, 提供統一的「配置管理服務」。目前已經不再維護更新。

https://github.com/knightliao/disconf

2、Spring Cloud Config

2014年9月開源,Spring Cloud 生態組件,可以和Spring Cloud體系無縫整合。

https://github.com/spring-cloud/spring-cloud-config

3、Apollo

2016年5月,攜程開源的配置管理中心,能夠集中化管理應用不同環境、不同集群的配置,配置修改后能夠實時推送到應用端,並且具備規范的權限、流程治理等特性,適用於微服務配置管理場景。

https://github.com/ctripcorp/apollo

4、Nacos

2018年6月,阿里開源的配置中心,也可以做DNS和RPC的服務發現。

https://github.com/alibaba/nacos

 

功能特性對比

由於Disconf不再維護,下面主要對比一下Spring Cloud Config、Apollo和Nacos。

 

功能點

Spring Cloud Config

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

Apollo特性

基於配置的特殊性,所以Apollo從設計之初就立志於成為一個有治理能力的配置發布平台,目前提供了以下的特性:

1、統一管理不同環境、不同集群的配置

  • Apollo提供了一個統一界面集中式管理不同環境(environment)、不同集群(cluster)、不同命名空間(namespace)的配置。

  • 同一份代碼部署在不同的集群,可以有不同的配置,比如zookeeper的地址等

  • 通過命名空間(namespace)可以很方便地支持多個不同應用共享同一份配置,同時還允許應用對共享的配置進行覆蓋

2、配置修改實時生效(熱發布)

  • 用戶在Apollo修改完配置並發布后,客戶端能實時(1秒)接收到最新的配置,並通知到應用程序

3、版本發布管理

  • 所有的配置發布都有版本概念,從而可以方便地支持配置的回滾

4、灰度發布

  • 支持配置的灰度發布,比如點了發布后,只對部分應用實例生效,等觀察一段時間沒問題后再推給所有應用實例

5、權限管理、發布審核、操作審計

  • 應用和配置的管理都有完善的權限管理機制,對配置的管理還分為了編輯和發布兩個環節,從而減少人為的錯誤。

  • 所有的操作都有審計日志,可以方便地追蹤問題

6、客戶端配置信息監控

  • 可以在界面上方便地看到配置在被哪些實例使用

7、提供Java和.Net原生客戶端

  • 提供了Java和.Net的原生客戶端,方便應用集成

  • 支持Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便應用使用(需要Spring 3.1.1+)

  • 同時提供了Http接口,非Java和.Net應用也可以方便地使用

8、提供開放平台API

  • Apollo自身提供了比較完善的統一配置管理界面,支持多環境、多數據中心配置管理、權限、流程治理等特性。不過Apollo出於通用性考慮,不會對配置的修改做過多限制,只要符合基本的格式就能保存,不會針對不同的配置值進行針對性的校驗,如數據庫用戶名、密碼,Redis服務地址等

  • 對於這類應用配置,Apollo支持應用方通過開放平台API在Apollo進行配置的修改和發布,並且具備完善的授權和權限控制

 

執行流程

先來看一個簡單的工作流程圖,如下:

 

操作流程如下:

1、管理員通過可視化操作界面在Apollo配置中心修改、發布配置

2、應用程序通過Apollo客戶端從配置中心拉取配置信息

管理員通過Apollo配置中心修改或發布配置后,會有兩種機制來保證應用程序獲取最新配置:

1)Apollo配置中心主動向客戶端推送最新的配置;

2)Apollo客戶端會定時從Apollo配置中心拉取最新的配置。

通過以上推、拉的兩種機制共同來保證應用程序能及時獲取到配置。

 

Apollo的安裝部署

下載地址:https://github.com/ctripcorp/apollo/releases

 

兩種下載方式,一是直接下載上圖的畫框的三個分別是adminservice、configservice、portal;二是下載source code源碼自己打包,zip 和 tar.gz 都可以。這里使用的第二種方式。

解釋一下為啥第一種下載方式會有三個包,這其實和Apollo的架構有關。portal 是一個可視化界面會調用 adminservice 進行配置管理和發布,可以認為是 adminservice 的 client 端;configservice 和 Apollo 的 client 保持長連接,configservice 服務於 Apollo client 進行配置獲取,configservice 和 client 通過一種推拉結合的方式,實現配置實時更新的同時,保證配置更新不丟失。所以不管是第一種方式還是第二種方式,我們都需要運行三個jar程序。

采用第二種方式,下載好源碼並解壓后倒導入idea,如下

然后找到scripts文件夾並打開,有一個 sql 文件夾,里面有兩個 SQL 文件

在mysql里執行這兩個SQL 文件,成功后會創建兩個數據庫

 

然后注意到 scripts 目錄下有兩個文件,build.bat 和 build.sh 文件,分別是Windows 和 linux用的。

需要打開對應的文件更改一些參數,如數據庫配置參數URL,用戶名密碼,改成你自己的。

下面這些是Erueka的不同環境的地址,我們就本地dev的不用改了

set dev_meta="http://localhost:8080" set fat_meta="http://someIp:8080" set uat_meta="http://anotherIp:8080" set pro_meta="http://yetAnotherIp:8080"

然后,運行就直接就是用maven打包了,打完之后在各自的target目錄下即可看到


 

我是把這三個jar復制出來,放到一個專門的目錄,方便運行。

然后,依次啟動三個jar

java -jar apollo-configservice-1.6.1.jar
java -jar apollo-adminservice-1.6.1.jar
java -jar apollo-portal-1.6.1.jar

都啟動后,訪問http://localhost:8070/ 進入Apollo的配置管理中心,可以創建項目(walking是我自己創建的),稍后再講


然后訪問 http://localhost:8080/ 出現如下畫面,Apollo安裝部署成功。

 

創建項目

點擊創建項目,選擇部門,Apollo默認的有兩個樣例,當然你可以在右上角管理員工具里添加,稍后再說。

然后填寫AppId,這是全局唯一的,客戶端調用的時候要用這個。應用名稱就隨意寫了。

應用負責人,默認的是Apollo,也可以在右上角管理員工具里增加。

項目管理員,可以額外添加該項目的管理員

 

 

然后提交就行了。

然后就會進入到該項目,默認會有一個application 的命名空間


可以添加命名空間

我們可以點擊新增配置來添加參數

新添加的參數是未發布狀態,可點擊發布按鈕使其生效


然后就發布成功了。

 

Apollo工作原理

下圖是Apollo架構模塊的概覽

apollo架構圖 by walking

 

各模塊職責

上圖簡要描述了Apollo的總體設計,我們可以從下往上看:

  • Config Service提供配置的讀取、推送等功能,服務對象是Apollo客戶端

  • Admin Service提供配置的修改、發布等功能,服務對象是Apollo Portal(管理界面)

  • Eureka提供服務注冊和發現,為了簡單起見,目前Eureka在部署時和Config Service是在一個JVM進程中的

  • 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. Apollo啟動后,Config/Admin Service會自動注冊到Eureka服務注冊中心,並定期發送保活心跳。

  2. Apollo Client和Portal管理端通過配置的Meta Server的域名地址經由Software Load Balancer(軟件負載均衡器)進行負載均衡后分配到某一個Meta Server

  3. Meta Server從Eureka獲取Config Service和Admin Service的服務信息,相當於是一個Eureka Client

  4. Meta Server獲取Config Service和Admin Service(IP+Port)失敗后會進行重試

  5. 獲取到正確的Config Service和Admin Service的服務信息后,Apollo Client通過Config Service為應用提供配置獲取、實時更新等功能;Apollo Portal管理端通過Admin Service提供配置新增、修改、發布等功能

核心概念

application (應用)

這個很好理解,就是實際使用配置的應用,Apollo客戶端在運行時需要知道當前應用是誰,從而可以去獲取對應的配置

關鍵字:appId

 

environment (環境)

配置對應的環境,Apollo客戶端在運行時需要知道當前應用處於哪個環境,從而可以去獲取應用的配置

關鍵字:env

 

cluster (集群)

一個應用下不同實例的分組,比如典型的可以按照數據中心分,把上海機房的應用實例分為一個集群,把北京機房的應用實例分為另一個集群。

關鍵字:cluster

 

namespace (命名空間)

一個應用下不同配置的分組,可以簡單地把namespace類比為文件,不同類型的配置存放在不同的文件中,如數據庫配置文件,RPC配置文件,應用自身的配置文件等

關鍵字:namespaces

它們的關系如下圖所示:

 

Spring Boot整合Apollo

 

Apollo搭建好之后,我們通過一個簡單的SpringBoot項目來去從它上面獲取配置,進行一個測試。

入門案例

1、添加Apollo client依賴

<dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-client</artifactId> <version>${apollo.client.version}</version> </dependency>

2、application.yml添加配置

server:
  port: 8761
app:
  id: walking #AppId是應用的身份信息,是配置中心獲取配置的一個重要信息
apollo:
  meta: http://localhost:8080 #eureka地址
  bootstrap:
    enabled: true #在應用啟動階段,向Spring容器注入被托管的application.properties文件的配置信息

3、啟動類上增加 @EnableApolloConfig 注解

@RestController public class ApolloController { @Value("${test_key}") String testApollo; @GetMapping("testApollo") public Map<String,Object> testApollo() { Map<String,Object> result = new HashMap<>(); result.put("testApollo",testApollo); return result; } }

4、訪問接口,拿到了配置value值

 

訪問多個命名空間

1、修改application.yml,增加 namespaces 選項,填寫命名空間名稱,application可以不用寫。

server:
  port: 8761 app: id: walking #AppId是應用的身份信息,是配置中心獲取配置的一個重要信息 apollo: meta: http://localhost:8080 #eureka地址 bootstrap: enabled: true #在應用啟動階段,向Spring容器注入被托管的application.properties文件的配置信息 namespaces: TEST1.datasource-mysql.config

2、獲取另外一個命名空間內的參數 mysql.url

@RestController public class ApolloController { @Value("${test_key}") String testApollo; @Value("${mysql.url}") String mysqlUrl; @GetMapping("testApollo") public Map<String,Object> testApollo() { Map<String,Object> result = new HashMap<String,Object>(); result.put("testApollo",testApollo); result.put("mysqlUrl",mysqlUrl); return result; } }

3、測試,拿到了

 

動態修改日志級別

日志模塊是每個項目中必須的,用來記錄程序運行中的相關信息。一般在開發環境下使用DEBUG級別的日志輸出,為了方便查看問題,而在線上一般都使用INFO或者ERROR級別的日志,主要記錄業務操作或者錯誤的日志。

那么問題來了,當線上環境出現問題希望輸出DEBUG日志信息輔助排查的時候怎么辦呢?以前確實是這么做的:修改配置文件,重新打包然后上傳重啟線上環境。很麻煩,也很慢。

所以我們想通過把日志級別參數部署到 Apollo 上,然后監聽參數變化后從 Apollo 上獲取日志級別,再修改日志級別,來達到熱更新的效果。

1、引入日志包,這里使用的是lombok,比較方便演示

<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> <scope>provided</scope> </dependency>

2、application.yml​​​​​​​

server:
  port: 8761 app: id: walking #AppId是應用的身份信息,是配置中心獲取配置的一個重要信息 apollo: meta: http://localhost:8080 bootstrap: enabled: true #在應用啟動階段,向Spring容器注入被托管的application.properties文件的配置信息。 namespaces: TEST1.datasource-mysql.config eagerLoad: enabled: true #將Apollo配置的加載提到初始化日志系統之前 logging: level: com: walking: controller: info #根據包名調整controller包的日志級別,為了后面演示在配置中心動態配置日志級別

3、增加了如下配置,用於將 Apollo 配置的加載提到初始化日志系統之前,以及配置com.walking.controller包的日志級別為 debug

   eagerLoad:
      enabled: true #將Apollo配置的加載提到初始化日志系統之前 logging: level: com: walking: controller: debug #根據包名調整controller包的日志級別,為了后面演示在配置中心動態配置日志級別 

4、然后增加這樣一個參數用於部署日志級別

5、然后通過@ApolloConfigChangeListener注解監聽配置的變化,監聽到配置變化時重新設置該目錄的日志級別​​​​​​​

@Slf4j @Configuration public class LoggerConfig { private static final String LOGGER_TAG = "logging.level."; @Autowired private LoggingSystem loggingSystem; /* * 將Apollo服務端的中的配置注入這個類中。 */ @ApolloConfig private Config config; /* * @ApolloConfigChangeListener * value 指定監聽哪個 namespace,默認是 application * interestedKeys 指定監聽哪些key 如interestedKeys={"abc","123"}, * interestedKeyPrefixes 指定監聽的key(模糊匹配) * 監聽配置中心配置的更新事件,若該事件發生,則調用refreshLoggingLevels方法,處理該事件。 * ConfigChangeEvent參數:可以獲取被修改配置項的key集合,以及被修改配置項的新值、舊值和修改類型等信息。 */ @ApolloConfigChangeListener(value="application",interestedKeyPrefixes = {LOGGER_TAG}) private void configChangeListter(ConfigChangeEvent changeEvent) { refreshLoggingLevels(changeEvent); } private void refreshLoggingLevels(ConfigChangeEvent changeEvent) { log.info("apollo config changed,LoggerConfig 更新日志級別"); Set<String> keyNames = changeEvent.changedKeys(); for (String key : keyNames) { if (StringUtils.containsIgnoreCase(key, LOGGER_TAG)) { ConfigChange change = changeEvent.getChange(key); String newLevel = change.getNewValue(); String oldLevel = change.getOldValue(); LogLevel level = LogLevel.valueOf(newLevel.toUpperCase()); String packageName = key.replace(LOGGER_TAG, ""); loggingSystem.setLogLevel(packageName, level); log.info("package logLevel changed==>key:{},package:{},oldLevel:{},newLevel:{},changeType:{}", key, packageName, oldLevel,newLevel,change.getChangeType()); } } log.info("apollo config changed,LoggerConfig 更新完畢"); } } 

6、我們在 controller 的接口里增加如下代碼,便於觀察日志級別是否改變

​​​​​​​

log.debug("debug..."); log.info("info..."); log.error("error..."); log.warn("warn...");

 

7、首先啟動spring boot項目,我們訪問測試接口,查看日志。


正是我們在 application.yml 上設置的 debug 日志級別。

8、接下來我們在 Apollo 上修改日志級別為 warn,然后發布。

日志里已經看到我們的應用已經監聽到配置的變化,並更新了日志級別

9、訪問一下測試接口,可看到已經生效了。

這就實現了日志級別的熱修改。

如我們可以完全依賴 Apollo 上的日志級別,就可以直接把 application.yml 的日志級別去掉了。

但是,我們需要修改一下我們的 LoggerConfig 類了,因為目前這個類的修改日志級別的功能是在監聽到 Apollo 的配置變化了之后修改的,當我們的應用剛部署時是沒有修改配置的,所以就沒有觸發執行這個設置日志級別的方法。

我們只需要增加一個init方法即可,並在其上添加@PostConstruct 注解即可,這個注解就是初始化時就調用該方法,和 init method 作用一樣,這樣在我們的系統啟動時就可以從 Apollo 上拿到配置參數從而設置日志級別。

@PostConstruct //加載這個類時就執行 private void init(){ log.info("初始化日志級別..."); Set<String> keyNames = config.getPropertyNames(); for (String key : keyNames) { if (StringUtils.containsIgnoreCase(key, LOGGER_TAG)) { String strLevel = config.getProperty(key, "info"); LogLevel level = LogLevel.valueOf(strLevel.toUpperCase()); String packageName = key.replace(LOGGER_TAG, ""); loggingSystem.setLogLevel(packageName, level); log.info("init package logLevel==>key:{},package:{},level:{}", key, packageName, strLevel); } } log.info("初始化日志級別完畢"); }

啟動


測試一下是不是warn級別

訪問測試接口,可以看到正是 warn級別。

 

再去修改為info,客戶端應用監聽到修改並重新設置了日志級別

 

訪問測試接口,已修改為info級別。

 

Apollo還支持,灰度發布、分環境、分集群配置,篇幅的原因關於Apollo應用的介紹今天就先到這里啦。

覺得有幫助的話請點贊吧❤~

 

熱文:

Redis的各種數據類型到底能玩出什么花兒?

Redis分布式鎖實戰

什么是消息隊列啊?

聯合索引在B+樹上的存儲結構及數據查找方式

 

嘮叨~

歡迎關注公眾號,編程大道,之前整理的 redis 和 MQ 的知識點思維導圖分享給大家


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM