Apollo分佈式設定中心

2020-08-13 14:30:39

概覽

什麼是設定

  應用程式在啓動和執行的時候, 往往需要讀取一些設定資訊, 設定基本上伴隨着應用程式的整個生命週期, 比如: 數據庫連線參數, 啓動參數等

設定主要有一下幾個特點:

  • 設定是獨立於程式的只讀變數
    • 設定首先是獨立於程式的, 同一份程式在不同的設定下會有不同的行爲
    • 其次, 設定對於程式是隻讀的, 程式通過讀取設定來改變自己的行爲, 但是程式不應該去改變設定
  • 設定伴隨應用的整個生命週期
    • 設定貫穿於應用的整個生命週期, 應用在啓動時通過讀取設定來初始化, 在執行時根據設定調整行爲. 比如: 啓動時需要讀取服務的埠號, 系統在執行過程中需要讀取定時策略執行定時任務等.
  • 設定可以有多種載入方式
    • 常見的有程式內部寫死. 組態檔, 環境變數, 啓動參數, 基於數據庫等
  • 設定需要治理
    • 許可權控制: 由於設定能改變程式的行爲, 不正確的設定甚至能引起災難, 所以對設定的修改必須有比較完善的許可權控制
    • 不同環境, 叢集設定管理: 同一份程式在不同的環境(開發, 測試, 生成), 不同的叢集(如不同的數據中心)經常需要有不同的設定, 所以需要有完善的環境, 叢集設定管理

什麼是設定中心

  傳統單體應用存在一些潛在缺陷, 如隨着規模的擴大, 部署效率降低, 團隊共同作業效率差, 系統可靠性變差, 維護困難, 新功能上線週期長等, 所以迫切需要一種新的架構去解決這些問題, 而微服務(microservices)架構正是當下一種流行的解法
  不過, 解決一個問題的同時, 往往會誕生出很多新的問題, 所以微服務化的過程中伴隨着很多的挑戰, 其中一個挑戰就是有關服務(應用)設定的. 當系統從一個單體應用, 被拆分成分佈式系統上一個個服務節點後, 組態檔也必須跟着遷移, 分割, 這樣設定就分散了, 不僅如此, 分散中還包含着冗餘, 如下圖
在这里插入图片描述
設定中心將設定從應用中剝離出來, 統一管理, 優雅的解決了設定的動態變更, 持久化, 運維成本等問題
應用自身既不需要去新增管理設定介面, 也不需要自己去實現設定的持久化, 更不需要引入"定時任務"以便降低運維成本
  總的來說, 設定中心就是一種統一管理各種應用設定的基礎服務元件
  在系統架構中, 設定中心時整個微服務基礎架構體系中的一個元件, 如下圖, 它的功能看上去不起眼, 無非就是設定的管理和存取, 但它是整個微服務架構中不可或缺的一環
在这里插入图片描述
  集中管理設定,那麼就要將應用的設定作爲一個單獨的服務抽離出來了,同理也需要解決新的問題,比如:版本管理(爲了支援回滾),許可權管理等。
  總結一下,在傳統巨型單體應用紛紛轉向細粒度微服務架構的歷史進程中,設定中心是微服務化不可缺少的一個系統元件,在這種背景下中心化的設定服務即設定中心應運而生,一個合格的設定中心需要滿足:

  • 設定項容易讀取和修改

  • 新增新設定簡單直接

  • 支援對設定的修改的檢視以把控風險

  • 可以檢視設定修改的歷史記錄

  • 不同部署環境支援隔離

Apollo簡介

主流設定中心

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

  1. Disconf: 2014年7月百度開源的設定管理中心,專注於各種「分佈式系統設定管理」的「通用元件」和「通用平臺」, 提供統一的「設定管理服務」。目前已經不再維護更新
  2. Spring Cloud Config: 2014年9月開源,Spring Cloud 生態元件,可以和Spring Cloud體系無縫整合。
  3. Apollo: 2016年5月,攜程開源的設定管理中心,能夠集中化管理應用不同環境、不同叢集的設定,設定修改後能夠實時推播到應用端,並且具備規範的許可權、流程治理等特性,適用於微服務設定管理場景。Apollo設定中心github
  4. Nacos: 2018年6月,阿裡開源的設定中心,也可以做DNS和RPC的服務發現。Nacos

功能特性對比

功能點 SpringCloudConfig 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 - A reliable configuration management system
https://github.com/ctripcorp/apollo

Apollo(阿波羅)是攜程框架部門研發的分佈式設定中心,能夠集中化管理應用的不同環境、不同叢集的設定,設定修改後能夠實時推播到應用端,並且具備規範的許可權、流程治理等特性,適用於微服務設定管理場景。

Apollo包括伺服器端和用戶端兩部分:

伺服器端基於Spring Boot和Spring Cloud開發,打包後可以直接執行,不需要額外安裝Tomcat等應用容器。

Java用戶端不依賴任何框架,能夠執行於所有Java執行時環境,同時對Spring/Spring Boot環境也有較好的支援。

Apollo特性

基於設定的特殊性,所以Apollo從設計之初就立志於成爲一個有治理能力的設定發佈平臺,目前提供了以下的特性:

  • 統一管理不同環境、不同叢集的設定
    • Apollo提供了一個統一介面集中式管理不同環境(environment)、不同叢集(cluster)、不同名稱空間(namespace)的設定。
    • 同一份程式碼部署在不同的叢集,可以有不同的設定,比如zookeeper的地址等
    • 通過名稱空間(namespace)可以很方便地支援多個不同應用共用同一份設定,同時還允許應用對共用的設定進行覆蓋
  • 設定修改實時生效(熱發佈)
    • 使用者在Apollo修改完設定併發布後,用戶端能實時(1秒)接收到最新的設定,並通知到應用程式
  • 版本發佈管理
    • 所有的設定發佈都有版本概念,從而可以方便地支援設定的回滾
  • 灰度發佈
    • 支援設定的灰度發佈,比如點了發佈後,只對部分應用範例生效,等觀察一段時間沒問題後再推給所有應用範例
  • 許可權管理、發佈審覈、操作審計
    • 應用和設定的管理都有完善的許可權管理機制 機製,對設定的管理還分爲了編輯和發佈兩個環節,從而減少人爲的錯誤。
    • 所有的操作都有審計日誌,可以方便地追蹤問題
  • 用戶端設定資訊監控
    • 可以在介面上方便地看到設定在被哪些範例使用
  • 提供Java和.Net原生用戶端
    • 提供了Java和.Net的原生用戶端,方便應用整合
    • 支援Spring Placeholder, Annotation和Spring Boot的ConfigurationProperties,方便應用使用(需要Spring 3.1.1+)
    • 同時提供了Http介面,非Java和.Net應用也可以方便地使用
  • 提供開放平臺API
    • Apollo自身提供了比較完善的統一設定管理介面,支援多環境、多數據中心設定管理、許可權、流程治理等特性。不過Apollo出於通用性考慮,不會對設定的修改做過多限制,只要符合基本的格式就能儲存,不會針對不同的設定值進行鍼對性的校驗,如數據庫使用者名稱、密碼,Redis服務地址等
    • 對於這類應用設定,Apollo支援應用方通過開放平臺API在Apollo進行設定的修改和發佈,並且具備完善的授權和許可權控制

Apollo快速入門

執行流程

在这里插入图片描述
操作流程如下:

  1. 在Apollo設定中心修改設定
  2. 應用程式通過Apollo用戶端從設定中心拉取設定資訊

使用者通過Apollo設定中心修改或發佈設定後,會有兩種機制 機製來保證應用程式來獲取最新設定:一種是Apollo設定中心會向用戶端推播最新的設定;另外一種是Apollo用戶端會定時從Apollo設定中心拉取最新的設定,通過以上兩種機制 機製共同來保證應用程式能及時獲取到設定。

Windows安裝Apollo

執行時環境

Java

  • Apollo伺服器端:1.8+
  • Apollo用戶端:1.7+

由於需要同時執行伺服器端和用戶端,所以建議安裝Java 1.8+。

MySQL

  • 版本要求:5.6.5+

Apollo的表結構對timestamp使用了多個default宣告,所以需要5.6.5以上版本。

下載設定

  1. 存取Apollo的官方主頁獲取安裝包(本次使用1.3版本):https://github.com/ctripcorp/apollo/tags
  2. 開啓1.3發佈鏈接,下載必須的安裝包:https://github.com/ctripcorp/apollo/releases/tag/v1.3.0
    在这里插入图片描述
  3. 解壓安裝包後將apollo-configservice-1.3.0.jar, apollo-adminservice-1.3.0.jar, apollo-portal-1.3.0.jar放置於apollo目錄下

建立數據庫

Apollo伺服器端共需要兩個數據庫:ApolloPortalDBApolloConfigDB,ApolloPortalDB只需要在生產環境部署一個即可,而ApolloConfigDB需要在每個環境部署一套。

  1. 建立ApolloPortalDB,sql指令碼下載地址:https://github.com/ctripcorp/apollo/blob/v1.3.0/scripts/db/migration/configdb/V1.0.0__initialization.sql
    以MySQL原生用戶端爲例:soure apollo/ApolloPortalDB_initialization.sql
  2. 驗證ApolloPortalDB
    匯入成功後, 可以通過執行以下SQL語句來驗證
    select `Id`, `Key`, `Value`, `Comment` from `ApolloPortalDB`.`ServerConfig` limit 1;

注: ApolloPortalDB只需要在生產環境部署一個即可

  1. 建立ApolloConfigDB,sql指令碼下載地址:https://github.com/ctripcorp/apollo/blob/v1.3.0/scripts/db/migration/configdb/V1.0.0__initialization.sql
    以MySQL原生用戶端爲例:
    source apollo/ApolloConfigDB__initialization.sql
  2. 驗證ApolloConfigDB
    select `Id`, `Key`, `Value`, `Comment` from `ApolloConfigDB`.`ServerConfig` limit 1;

啓動Apollo

  1. 確保埠未被佔用
    Apollo預設會啓動3個服務,分別使用8070, 8080, 8090埠,請確保這3個埠當前沒有被使用
  2. 啓動apollo-configservice,在apollo目錄下執行如下命令 可通過-Dserver.port=8080修改預設埠
    java -Xms256m -Xmx256m -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-configservice-1.3.0.jar
  3. 啓動apollo-adminservice 可通過-Dserver.port=8090修改預設埠
    java -Xms256m -Xmx256m -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-adminservice-1.3.0.jar
  4. 啓動apollo-portal 可通過-Dserver.port=8070修改預設埠
    java -Xms256m -Xmx256m -Ddev_meta=http://localhost:8080/ -Dserver.port=8070 -Dspring.datasource.url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8 -Dspring.datasource.username=root -Dspring.datasource.password=pbteach0430 -jar apollo-portal-1.3.0.jar
  5. 也可以使用提供的runApollo.bat快速啓動三個服務(修改數據庫連線地址,數據庫以及密碼)
 echo

   set url="localhost:3306"
   set username="root"
   set password="123"

   start "configService" java -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-configservice.log -jar .\apollo-configservice-1.3.0.jar
   start "adminService" java -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-adminservice.log -jar .\apollo-adminservice-1.3.0.jar
   start "ApolloPortal" java -Xms256m -Xmx256m -Dapollo_profile=github,auth -Ddev_meta=http://localhost:8080/ -Dserver.port=8070 -Dspring.datasource.url=jdbc:mysql://%url%/ApolloPortalDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-portal.log -jar .\apollo-portal-1.3.0.jar

執行runApollo.bat即可啓動Apollo 待啓動成功後,存取管理頁面(localhost:8070) apollo/admin
在这里插入图片描述

Linux安裝Apollo

  1. 下載三個壓縮包在这里插入图片描述
  2. 準備3台伺服器,一臺安裝portal 第二臺作爲DEV環境部署configservice和adminservice 第三臺是pro環境作爲生產, dev和pro環境都要部署configservice和adminservice
    portal是操作介面, 可以共用 一個
  3. 準備兩個mysql數據庫,一個能連線到dev環境,一個能連線到pro環境
    dev環境需要建立ApolloPortalDBApolloConfigDB pro環境只需要建立ApolloConfigDB
  4. 把portalservice壓縮包上傳至伺服器1 configservice和adminservice都分別上傳至dev 和perf環境的伺服器
  5. 解壓縮
  6. 修改設定
    進入到portal的資料夾, 進入到config目錄
    在这里插入图片描述
    vim apollo-env.properties
    在这里插入图片描述

dev.meta是第二臺伺服器dev的ip加8080埠, pro.meta是第三臺伺服器pro的ip加8080埠,其餘環境暫時沒有用到
vim application-github.properties
在这里插入图片描述

修改portal對應的mysql地址username 和password
進入PortalDB數據庫 修改表格(ServerConfig)在这里插入图片描述

進入到第二臺伺服器中,首先進入configservice的config目錄
vim application-github.properties
修改成dev環境的ApolloConfigDB的數據庫設定
再進入adminservice的config目錄, 修改application-github.properties的數據庫設定

進入到第三臺伺服器, 修改configservice和adminservice的mysql數據庫設定

  1. 啓動Apollo 順序, 先啓動dev和pro的configservice , adminservice 再啓動 portal
    先進入到script目錄 執行./startup.sh
  2. Apollo的執行日誌在/opt/logs目錄下
  3. 存取地址是portal所在的伺服器的ip加8070埠, 預設username: apollo 密碼: admin

程式碼實現

發佈設定

  1. 開啓apollo :新建專案apollo-quickstart
    在这里插入图片描述
  2. 新增設定項sms.enable
    在这里插入图片描述
    在这里插入图片描述
  3. 發佈設定項, 只有發佈過的設定纔會被用戶端獲取到, 此次發佈只會作用於當前環境dev
    在这里插入图片描述
    在这里插入图片描述

應用讀取設定

  1. 新建maven 工程 新建apollo-quickstart專案
<dependencies>
        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.28</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

編寫main方法獲取設定

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;

public class GetConfigTest {

    //VM Options:
    //-Dapp.id=applo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080
    public static void main(String[] args) {
        Config config = ConfigService.getAppConfig();
        String someKey = "sms.enable";
        //獲取設定資訊, 第一個參數: 設定的key, 第二個參數: 拿不到的話設定的預設值
        //如果這樣寫會報警告, 找不到app.id,沒有與Apollo內的專案系結,暫時先通過VM-Options的方式加入
        String value = config.getProperty(someKey,null);
        System.out.println("sms.enable: " + value);
    }
}

修改設定

修改sms.enable的設定, 再啓動main方法, 發現sms.enable輸出已經改變了

熱發佈

修改程式碼, 讓主方法一直取設定

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;

import java.time.LocalDateTime;

public class GetConfigTest {

    //VM Options:
    //-Dapp.id=applo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080
    public static void main(String[] args) throws InterruptedException {
        Config config = ConfigService.getAppConfig();

        while(true){
            Thread.sleep(1000);
            String someKey = "sms.enable";
            //獲取設定資訊, 第一個參數: 設定的key, 第二個參數: 拿不到的話設定的預設值
            //如果這樣寫會報警告, 找不到app.id,沒有與Apollo內的專案系結,暫時先通過VM-Options的方式加入
            String value = config.getProperty(someKey,null);
            //列印帶時間的日誌
            System.out.printf("now: %s , sms.enable: %s%n " , LocalDateTime.now().toString(), value);
        }
    }
}

啓動main方法後, 去Apollo管理頁面修改sms.enable的值, 發現設定可以修改並自動生效

Apollo應用

Apollo工作原理

在这里插入图片描述

各模組職責

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

  1. Config Service提供設定的讀取、推播等功能,服務物件是Apollo用戶端(用戶端讀取設定需要連線Config Server)
  2. Admin Service提供設定的修改、發佈等功能,服務物件是Apollo Portal(管理介面中對設定進行修改和發佈
  3. Eureka提供服務註冊和發現,爲了簡單起見,目前Eureka在部署時和Config Service是在一個JVM進程中的
  4. Config Service和Admin Service都是多範例、無狀態部署,所以需要將自己註冊到Eureka中並保持心跳
  5. 在Eureka之上架了一層Meta Server用於封裝Eureka的服務發現介面
  6. Client通過域名存取Meta Server獲取Config Service服務列表(IP+Port),而後直接通過IP+Port存取服務,同時在Client側會做load balance、錯誤重試
  7. Portal通過域名存取Meta Server獲取Admin Service服務列表(IP+Port),而後直接通過IP+Port存取服務,同時在Portal側會做load balance、錯誤重試
  8. 爲了簡化部署,我們實際上會把Config Service、Eureka和Meta Server三個邏輯角色部署在同一個JVM進程中

爲什麼使用Eureka

在这里插入图片描述

分佈執行流程

  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提供設定新增、修改、發佈等功能

核心概念

  1. application(應用)
    這個很好理解, 就是實際使用設定的應用, Apollo用戶端在執行時需要知道當前應用是誰, 從而可以去獲取對應的設定
    關鍵字: appId
  2. environment(環境)
    設定對應的環境, Apollo用戶端在執行時需要知道當前應用處於哪個環境, 從而可以去獲取應用的設定
    關鍵字: env
  3. cluster(叢集)
    一個應用下不同範例的分組, 比如典型的可以按照數據中心分, 把上海機房的應用範例分爲一個叢集, 把北京機房的應用範例分爲另一個叢集
    關鍵字: cluster
  4. namespace(名稱空間)
    一個應用下不同設定的分組, 可以簡單的把namespace類比爲檔案, 不同類型的設定存放在不同的檔案中, 如數據庫組態檔, RPC組態檔, 應用自身組態檔等
    關鍵字: namespaces
    他們的關係如下圖所示
    在这里插入图片描述

專案管理

基礎設定

部門管理

apollo預設的部門有兩個. 要增加自己的部門, 可在系統參數中修改:

  • 進入系統參數設定
    在这里插入图片描述

  • 輸入key查詢以存在的部門設定: organizations
    在这里插入图片描述

  • 修改value值來新增新部門, 下面 下麪新增一個微服務部門
    在这里插入图片描述

  • 退出後重新登錄, 可以使之生效

新增使用者

apollo預設提供一個超級管理員: apollo, 可以自行新增使用者

  • 新建使用者張三
    在这里插入图片描述
    在这里插入图片描述
    下面 下麪就可以建立專案時指定負責人了

建立專案

  • 在主頁點選建立專案 account-service
    在这里插入图片描述
    在这里插入图片描述
    以apollo使用者登錄可以看到所有的專案, 但是以zhangsan登錄, 只能看到分配給他的account-service

刪除專案

如果想要刪除整個專案, 點選右上角的"管理員工具–> 刪除應用, 叢集"
在这里插入图片描述
首先查詢出要刪除的專案, 點選"刪除應用"
在这里插入图片描述

在專案中給其他使用者授權

在这里插入图片描述
在这里插入图片描述

設定管理

下邊在account-service專案中進行設定

新增發布設定項

  1. key-value對新增設定
    在这里插入图片描述
    在这里插入图片描述
  2. 通過文字模式進行新增或修改 Apollo除了支援表格模式,逐個新增、修改設定外,還提供文字模式批次新增、修改。 這個對於從已有的properties檔案遷移尤其有用
    在这里插入图片描述

修改設定

點選右側修改按鈕
在这里插入图片描述
在这里插入图片描述

刪除設定

點選右側刪除按鈕
在这里插入图片描述

注意: 無論是新增,刪除, 修改 要想使其對專案生效都需要發佈

新增namespace

Namespace作爲設定的分類,可當成一個組態檔。
以新增rocketmq設定爲例,新增「spring-rocketmq」 Namespace設定rocketmq相關資訊。

  1. 新增專案私有Namespace:spring-rocketmq
    進入專案首頁,點選左下腳的「新增Namespace」,共包括兩項:關聯公共Namespace和建立Namespace,這裏選擇「建立Namespace」
    在这里插入图片描述
    在这里插入图片描述

建立成功後, 會跳轉到針對這個namespace的許可權分配頁面, 可以對某些使用者進行授權
在这里插入图片描述

在这里插入图片描述
想要獲取到指定namespace下的設定, 需要修改java程式碼

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;

import java.time.LocalDateTime;

public class GetConfigTest {

    //VM Options:
    //-Dapp.id=account-service -Denv=DEV -Ddev_meta=http://localhost:8080
    public static void main(String[] args) throws InterruptedException {
    	//讀取預設namespace的設定資訊
        //Config config = ConfigService.getAppConfig();
        
        //指定namespace獲取config物件
        Config config = ConfigService.getConfig("spring-rocketmq");

        while(true){
            Thread.sleep(1000);
            String someKey = "rocketmq.name-server";
            //獲取設定資訊, 第一個參數: 設定的key, 第二個參數: 拿不到的話設定的預設值
            //如果這樣寫會報警告, 找不到app.id,沒有與Apollo內的專案系結,暫時先通過VM-Options的方式加入
            String value = config.getProperty(someKey,null);
            System.out.printf("now: %s , sms.enable: %s%n " , LocalDateTime.now().toString(), value);
        }
    }
}

公共設定

新增公共namespace

在專案開發中,有一些設定可能是通用的,我們可以通過把這些通用的設定放到公共的Namespace中,這樣其他專案要使用時可以直接新增需要的Namespace

  1. 新建common-template專案(所有的設定都得依賴於專案進行儲存, 所以先建立一個, 用於儲存公共設定的專案)
    在这里插入图片描述
  2. 新增公共namespace: spring-boot-http

在这里插入图片描述
3. 新增設定項併發布

spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
server.tomcat.remote_ip_header = x-forwarded-for
server.tomcat.protocol_header = x-forwarded-proto
server.use-forward-headers = true
server.servlet.context-path = /

在这里插入图片描述

關聯公共namespace

  1. 開啓之前建立的account-service專案
  2. 點選左側的新增Namespace
    在这里插入图片描述
    點選右側按鈕, 覆蓋並修改公共設定爲自己想要的設定
    在这里插入图片描述
    在这里插入图片描述

叢集管理

在有些情況下,應用有需求對不同的叢集做不同的設定,比如部署在A機房的應用連線的RocketMQ伺服器地址和部署在B機房的應用連線的RocketMQ伺服器地址不一樣。另外在專案開發過程中,也可爲不同的開發人員建立不同的叢集來滿足開發人員的自定義設定。

建立叢集

  1. 點選頁面左側的「新增叢集」按鈕
  2. 輸入叢集名稱SHAJQ, 選擇環境並提交: 新增上海金橋數據中心爲例
    在这里插入图片描述
  3. 切換到新的叢集中, 發現新的叢集只有私有namespace而沒有設定項(沒有關聯的namespace 需要手動關聯)
    在这里插入图片描述

同步叢集設定

同步叢集的設定是指在同一個應用中拷貝某個環境下的叢集的設定到目標環境下的目標叢集。

  1. 從其他叢集同步已有設定到新叢集 先切換到原有叢集, 展開要同步的namespace 點選同步設定
    在这里插入图片描述
  2. 選擇同步到的新叢集, 再選擇同步設定,下一步
    在这里插入图片描述
  3. 同步完成後,切換到SHAJQ叢集,發佈設定

讀取設定

讀取某個叢集的設定,需要啓動應用時指定具體的應用、環境和叢集。
-Dapp.id=應用名稱

-Denv=環境名稱

-Dapollo.cluster=叢集名稱

-D環境_meta=meta地址

-Dapp.id=account-service -Denv=DEV -Dapollo.cluster=SHAJQ -Ddev_meta=http://localhost:8080

設定發佈原理

在設定中心中,一個重要的功能就是設定發佈後實時推播到用戶端。下面 下麪我們簡要看一下這塊是怎麼設計實現的。
在这里插入图片描述
上圖簡要描述了設定發佈的主要過程:

  1. 使用者在Portal操作設定發佈
  2. Portal呼叫Admin Service的介面操作發佈
  3. Admin Service發佈設定後,發送ReleaseMessage給各個Config Service(Apollo的設計是自己實現了訊息佇列, 爲了不與第三方進行整合)
  4. Config Service收到ReleaseMessage後,通知對應的用戶端

發送ReleaseMessage

Admin Service在設定發佈後,需要通知所有的Config Service有設定發佈,從而Config Service可以通知對應的用戶端來拉取最新的設定。

從概念上來看,這是一個典型的訊息使用場景,Admin Service作爲producer(生產者)發出訊息,各個Config Service作爲consumer(消費者)消費訊息。通過一個訊息佇列元件(Message Queue)就能很好的實現Admin Service和Config Service的解耦。

在實現上,考慮到Apollo的實際使用場景,以及爲了儘可能減少外部依賴,我們沒有採用外部的訊息中介軟體,而是通過數據庫實現了一個簡單的訊息佇列。

具體實現方式如下:

  1. Admin Service在設定發佈後會往ReleaseMessage表插入一條訊息記錄,訊息內容就是設定發佈的AppId+Cluster+Namespace
    SELECT * FROM ApolloConfigDB.ReleaseMessage
    在这里插入图片描述
    訊息發送類: DatabaseMessageSende 訊息發送類的程式碼
    在这里插入图片描述
  2. Config Service有一個執行緒會每秒掃描一次ReleaseMessage表,看看是否有新的訊息記錄
    訊息掃描類:ReleaseMessageScanner類

在这里插入图片描述
3. Config Service如果發現有新的訊息記錄,那麼就會通知到所有的訊息監聽器
在这里插入图片描述
然後呼叫訊息監聽類的handleMessage方法:NotificationControllerV2
在这里插入图片描述
4. NotificationControllerV2得到設定發佈的AppId+Cluster+Namespace後,會通知對應的用戶端

Config Service通知用戶端

上一節中簡要描述了NotificationControllerV2是如何得知有設定發佈的,那NotificationControllerV2在得知有設定發佈後是如何通知到用戶端的呢?

實現方式如下:

  1. 用戶端會發起一個Http請求到Config Service的notifications/v2介面NotificationControllerV2
    在这里插入图片描述
    用戶端發送請求類:RemoteConfigLongPollService
    在这里插入图片描述
  2. NotificationControllerV2不會立即返回結果,而是把請求掛起。考慮到會有數萬用戶端向伺服器端發起長連,因此在伺服器端使用了async servlet(Spring DeferredResult)來服務Http Long Polling請求。
  3. 如果在60秒內沒有該用戶端關心的設定發佈,那麼會返回Http狀態碼304給用戶端。
  4. 如果有該用戶端關心的設定發佈,NotificationControllerV2會呼叫DeferredResult的setResult方法,傳入有設定變化的namespace資訊,同時該請求會立即返回。用戶端從返回的結果中獲取到設定變化的namespace後,會立即請求Config Service獲取該namespace的最新設定。

用戶端讀取設計

除了之前介紹的用戶端和伺服器端保持一個長連線,從而能第一時間獲得設定更新的推播外,用戶端還會定時從Apollo設定中心伺服器端拉取應用的最新設定。

  • 這是一個備用機制 機製,爲了防止推播機制 機製失效導致設定不更新
  • 用戶端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,伺服器端都會返回304 - Not Modified
  • 定時頻率預設爲每5分鐘拉取一次,用戶端也可以通過在執行時指定System Property: apollo.refreshInterval來覆蓋,單位爲分鐘

Apollo應用於分佈式系統

在微服務架構模式下,專案往往會切分成多個微服務,下面 下麪將以萬信金融P2P專案爲例演示如何在專案中使用。

專案場景介紹

專案概述

萬信金融是一款面向網際網路大衆提供的理財服務和個人消費信貸服務的金融平臺,依託大數據風控技術,爲使用者提供方便、快捷、安心的P2P金融服務。本專案包括交易平臺和業務支撐兩個部分,交易平臺主要實現理財服務,包括:借錢、出借等模組,業務支撐包括:標的管理、對賬管理、風控管理等模組。專案採用先進的網際網路技術進行研發,保證了P2P雙方交易的安全性、快捷性及穩定性。

各微服務介紹

本章節僅僅是爲了演示設定中心,所以摘取了部分微服務,如下:

使用者中心服務(consumer-service):爲借款人和投資人提供使用者賬戶管理服務,包括:註冊、開戶、充值、提現等

UAA認證服務(uaa-service):爲使用者中心的使用者提供認證服務

統一賬戶服務(account-service):對借款人和投資人的登錄平臺賬號進行管理,包括:註冊賬號、賬號許可權管理等

交易中心(transaction-service):負責P2P平臺使用者發標和投標功能

SpringBoot應用整合

下面 下麪以整合統一賬戶服務(account-service)爲例

匯入工程

參考account-service、transaction-service、uaa-service、consumer-service工程,手動建立這幾個微服務。

每個工程必須新增依賴:

<dependency>
  <groupId>com.ctrip.framework.apollo</groupId>
  <artifactId>apollo-client</artifactId>
  <version>1.1.0</version>
</dependency>

下邊是account-service依賴,其它工程參考「資料」下的「微服務」。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.pbteach</groupId>
    <artifactId>account-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.1.0</version>
        </dependency>

    </dependencies>

</project>

必選設定

  1. AppId:application.yml中設定
app:
  id: account-service
  1. apollo.bootstrap
apollo:
  bootstrap: 
    enabled: true
    # namespace中間以,分隔
    namespaces: application,micro_service.spring-boot-http,spring-rocketmq,micro_service.spring-boot-druid
  1. env 需要通過VMOPtions來指定
    在这里插入图片描述

  2. cacheDir, cluster, meta, 都可以通過yml檔案的方式設定, 但是env需要通過指定VMOptions來設定

server:
  port: 19082
  servlet:
    context-path: /gbmp/bdmgmt
spring:
  application:
    name: account-service

apollo:
  bootstrap:
    enabled: true
    namespaces: application,yuecloud.mybatis,yuecloud.management,yuecloud.logging,yuecloud.pagehelper,yuecloud.datasource,yuecloud.jackson,yuecloud.redis,yuecloud.eureka,yuecloud.securityoauth2
  cacheDir: /opt/data/apollo-config
  cluster: DEFAULT
  meta: http://localhost:8080
app:
  id: account-service

啓用設定

在主啓動類上加入註解@EnableApolloConfig

import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableApolloConfig
public class AccountMain {
    public static void main(String[] args) {
        SpringApplication.run(AccountMain.class,args);
    }
}

應用設定

將自己的設定資訊拷貝到apollo中

讀取設定

在Controller上獲取設定@Value方式,在Apollo上點擊發佈會自動重新整理
@ConfigurationProperties(prefix=「dev」)組態檔方式, 需要加@RefreshScope纔會自動重新整理
首先需要加入pom的jar包

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

再寫一個設定類,設定重新整理監控類ConfigRefresh
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

建立其他專案

可以把其他微服務的組態檔都放到Apollo上

生產環境部署

當一個專案要上線部署到生產環境時,專案的設定比如數據庫連線、RocketMQ地址等都會發生變化,這時候就需要通過Apollo爲生產環境新增自己的設定。

企業部署方案

在企業中常用的部署方案爲:Apollo-adminservice和Apollo-configservice兩個服務分別在線上環境(pro),模擬環境(uat)和開發環境(dev)各部署一套,Apollo-portal做爲管理端只部署一套,統一管理上述三套環境。
在这里插入图片描述

建立數據庫

建立生產環境的ApolloConfigDB:每新增一套環境就需要部署一套ApolloConfgService和ApolloAdminService

source apollo/ApolloConfigDB_PRO__initialization.sql

設定啓動參數

  1. 設定生產環境數據庫連線
  2. 設定ApolloConfigService埠爲:8081,ApolloAdminService埠爲8091
echo

set url="localhost:3306"
set username="root"
set password="mysqlpwd"

start "configService-PRO" java -Dserver.port=8081 -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDBPRO?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-configservice.log -jar .\apollo-configservice-1.3.0.jar
start "adminService-PRO" java -Dserver.port=8091 -Xms256m -Xmx256m -Dapollo_profile=github -Dspring.datasource.url=jdbc:mysql://%url%/ApolloConfigDBPRO?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-adminservice.log -jar .\apollo-adminservice-1.3.0.jar
  1. 執行runApollo-PRO.bat

修改Eureka地址

更新生產環境Apollo的Eureka地址:

USE ApolloConfigDBPRO;

UPDATE ServerConfig SET `Value` = "http://localhost:8081/eureka/" WHERE `key` = "eureka.service.url";

調整ApolloPortal服務設定

服務設定項統一儲存在ApolloPortalDB.ServerConfig表中,可以通過管理員工具 - 系統參數頁面進行設定:apollo.portal.envs - 可支援的環境列表
在这里插入图片描述
預設值是dev,如果portal需要管理多個環境的話,以逗號分隔即可(大小寫不敏感),如:
dev,pro

啓動ApolloPortal

Apollo Portal需要在不同的環境存取不同的meta service(apollo-configservice)地址,所以我們需要在設定中提供這些資訊。
-Ddev_meta=http://localhost:8080/ -Dpro_meta=http://localhost:8081/

  1. 關閉之前啓動的ApolloPortal服務,使用runApolloPortal.bat啓動多環境設定
 echo

   set url="localhost:3306"
   set username="root"
   set password="123"

   start "ApolloPortal" java -Xms256m -Xmx256m -Dapollo_profile=github,auth -Ddev_meta=http://localhost:8080/ -Dpro_meta=http://localhost:8081/ -Dserver.port=8070 -Dspring.datasource.url=jdbc:mysql://%url%/ApolloPortalDB?characterEncoding=utf8 -Dspring.datasource.username=%username% -Dspring.datasource.password=%password% -Dlogging.file=.\logs\apollo-portal.log -jar .\apollo-portal-1.3.0.jar
  1. 啓動之後,點選account-service服務設定後會提示環境缺失,此時需要補全上邊新增生產環境的設定
    在这里插入图片描述
  2. 點選補缺環境
    在这里插入图片描述
  3. 補缺過生產環境後,切換到PRO環境後會提示有Namespace缺失,點選補缺
    在这里插入图片描述
    在这里插入图片描述
  4. 從dev環境同步設定到pro
    在这里插入图片描述
    在这里插入图片描述

驗證設定

同步完成後,切換到pro環境,修改生產環境rocketmq地址後發佈設定
在这里插入图片描述

  1. 設定專案使用pro環境,測試設定是否生效
    • 在apollo-env.properties中增加pro.meta=http://localhost:8081
    • 修改account-service啓動參數爲:-Denv=pro
      -Denv=pro -Dapollo.cacheDir=/opt/data/apollo-config -Dapollo.cluster=DEFAULT
    • 存取http://127.0.0.1:63000/account-service/mq 驗證RocketMQ地址是否爲上邊設定的PRO環境的值

灰度發佈

定義

灰度發佈是指在黑與白之間,能夠平滑過渡的一種發佈方式。在其上可以進行A/B testing,即讓一部分使用者繼續用產品特性A,一部分使用者開始用產品特性B,如果使用者對B沒有什麼反對意見,那麼逐步擴大範圍,把所有使用者都遷移到B上面來。

Apollo實現的功能

  1. 對於一些對程式有比較大影響的設定,可以先在一個或者多個範例生效,觀察一段時間沒問題後再全量發佈設定。
  2. 對於一些需要調優的設定參數,可以通過灰度發佈功能來實現A/B測試。可以在不同的機器上應用不同的設定,不斷調整、測評一段時間後找出較優的設定再全量發佈設定。

場景介紹

apollo-quickstart專案有兩個用戶端:

  1. 172.16.0.160
  2. 172.16.0.170
    在这里插入图片描述
    灰度目標
    當前有一個設定timeout=2000,我們希望對172.16.0.160灰度發佈timeout=3000,對172.16.0.170仍然是timeout=2000。
    在这里插入图片描述

建立灰度

  1. 點選application namespace右上角的建立灰度按鈕
    在这里插入图片描述
  2. 點選確定後,灰度版本就建立成功了,頁面會自動切換到灰度版本Tab
    在这里插入图片描述

灰度設定

  1. 點選主版本的設定中,timeout設定最右側的對此設定灰度按鈕
    在这里插入图片描述
  2. 在彈出框中填入要灰度的值:3000,點選提交
    在这里插入图片描述
    在这里插入图片描述

設定灰度規則

  1. 切換到灰度規則Tab,點選新增規則按鈕
    在这里插入图片描述
  2. 在彈出框中灰度的IP下拉框會預設展示當前使用設定的機器列表,選擇我們要灰度的IP,點選完成
    在这里插入图片描述
    在这里插入图片描述
    如果下拉框中沒找到需要的IP,說明機器還沒從Apollo取過設定,可以點選手動輸入IP來輸入,輸入完後點擊新增按鈕
    在这里插入图片描述

灰度發佈

啓動apollo-quickstart專案的GrayTest類輸出timeout的值

public class GrayTest {

   	// VM options:
   	// -Dapp.id=apollo-quickstart -Denv=DEV -Ddev_meta=http://localhost:8080
   	public static void main(String[] args) throws InterruptedException {
   		Config config = ConfigService.getAppConfig();
   		String someKey = "timeout";

   		while (true) {
   			String value = config.getProperty(someKey, null);
   			System.out.printf("now: %s, timeout: %s%n", LocalDateTime.now().toString(), value);
   			Thread.sleep(3000L);
   		}
   	}
   }

在这里插入图片描述

  1. 切換到設定Tab,再次檢查灰度的設定部分,如果沒有問題,點選灰度發佈
    在这里插入图片描述
  2. 在彈出框中可以看到主版本的值是2000,灰度版本即將發佈的值是3000。填入其它資訊後,點擊發布
    在这里插入图片描述
  3. 發佈後,切換到灰度範例列表Tab,就能看到172.16.0.160已經使用了灰度發佈的值
    在这里插入图片描述
    在这里插入图片描述

全量發佈

如果灰度的設定測試下來比較理想,符合預期,那麼就可以操作全量發佈。
全量發佈的效果是:

  1. 灰度版本的設定會合並回主版本,在這個例子中,就是主版本的timeout會被更新成3000
  2. 主版本的設定會自動進行一次發佈
  3. 在全量發佈頁面,可以選擇是否保留當前灰度版本,預設爲不保留。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

放棄灰度

如果灰度版本不理想或者不需要了,可以點選放棄灰度
在这里插入图片描述

發佈歷史

點選主版本的發佈歷史按鈕,可以看到當前namespace的主版本以及灰度版本的發佈歷史

在这里插入图片描述

搭建Apollo高可用叢集

在这里插入图片描述
現在在203節點上通過修改組態檔的方式, 改成DEV環境下的叢集, 202和203應該共用一套mysql的ConfigServiceDB

  1. 修改203節點的configservice和adminservice的mysql設定 改成和202節點一樣
  2. 因爲此時沒有PRO環境了, 所以在Portal的mysql數據庫的ServerConfig表中改回dev
    在这里插入图片描述
  3. 修改202節點連線的數據庫的ApolloConfigDB中的ServerConfig表, 修改eureka的地址,加上203節點的eureka地址
    在这里插入图片描述