天天看點

Spring Boot 內建 Apollo 配置中心,真香、真強大!

由于 Apollo 概念比較多,剛開始使用比較複雜,最好先過一遍概念再動手實踐嘗試使用。

1、背景

随着程式功能的日益複雜,程式的配置日益增多,各種功能的開關、參數的配置、伺服器的位址……對程式配置的期望值也越來越高,配置修改後實時生效,灰階釋出,分環境、分叢集管理配置,完善的權限、稽核機制……

在這樣的大環境下,傳統的通過配置檔案、資料庫等方式已經越來越無法滿足開發人員對配置管理的需求。是以 Apollo 配置中心應運而生!

2、簡介

Apollo(阿波羅)是攜程架構部門研發的開源配置管理中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,并且具備規範的權限、流程治理等特性。

3、特點

部署簡單

灰階釋出

版本釋出管理

提供開放平台API

用戶端配置資訊監控

提供Java和.Net原生用戶端

配置修改實時生效(熱釋出)

權限管理、釋出稽核、操作審計

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

4、基礎模型

如下即是 Apollo 的基礎模型:

(1)、使用者在配置中心對配置進行修改并釋出

(2)、配置中心通知Apollo用戶端有配置更新

(3)、Apollo用戶端從配置中心拉取最新的配置、更新本地配置并通知到應用

Spring Boot 內建 Apollo 配置中心,真香、真強大!

5、Apollo 的四個次元

Apollo支援4個次元管理Key-Value格式的配置:

application (應用)

environment (環境)

cluster (叢集)

namespace (命名空間)

(1)、application

Apollo 用戶端在運作時需要知道目前應用是誰,進而可以根據不同的應用來擷取對應應用的配置。

每個應用都需要有唯一的身份辨別,可以在代碼中配置 app.id 參數來辨別目前應用,Apollo 會根據此指來辨識目前應用。

(2)、environment

在實際開發中,我們的應用經常要部署在不同的環境中,一般情況下分為開發、測試、生産等等不同環境,不同環境中的配置也是不同的,在 Apollo 中預設提供了四種環境:

FAT(Feature Acceptance Test):功能測試環境

UAT(User Acceptance Test):內建測試環境

DEV(Develop):開發環境

PRO(Produce):生産環境

在程式中如果想指定使用哪個環境,可以配置變量 env 的值為對應環境名稱即可。

(3)、cluster

一個應用下不同執行個體的分組,比如典型的可以按照資料中心分,把上海機房的應用執行個體分為一個叢集,把北京機房的應用執行個體分為另一個叢集。

對不同的叢集,同一個配置可以有不一樣的值,比如說上面所指的兩個北京、上海兩個機房設定兩個叢集,兩個叢集中都有 mysql 配置參數,其中參數中配置的位址是不一樣的。

(4)、namespace

一個應用中不同配置的分組,可以簡單地把 namespace 類比為不同的配置檔案,不同類型的配置存放在不同的檔案中,如資料庫配置檔案,RPC 配置檔案,應用自身的配置檔案等。

熟悉 SpringBoot 的都知道,SpringBoot 項目都有一個預設配置檔案 application.yml,如果還想用多個配置,可以建立多個配置檔案來存放不同的配置資訊,通過指定 spring.profiles.active 參數指定應用不同的配置檔案。這裡的 namespace 概念與其類似,将不同的配置放到不同的配置 namespace 中。

Namespace 分為兩種權限,分别為:

public(公共的): public權限的 Namespace,能被任何應用擷取。

private(私有的): 隻能被所屬的應用擷取到。一個應用嘗試擷取其它應用 private 的 Namespace,Apollo 會報 “404” 異常。

Namespace 分為三種類型,分别為:

私有類型: 私有類型的 Namespace 具有 private 權限。例如 application Namespace 為私有類型。

公共類型: 公共類型的 Namespace 具有 public 權限。公共類型的N amespace 相當于遊離于應用之外的配置,且通過 Namespace 的名稱去辨別公共 Namespace,是以公共的 Namespace 的名稱必須全局唯一。

關聯類型(繼承類型): 關聯類型又可稱為繼承類型,關聯類型具有 private 權限。關聯類型的 Namespace 繼承于公共類型的 Namespace,将裡面的配置全部繼承,并且可以用于覆寫公共 Namespace 的某些配置。

6、本地緩存

Apollo用戶端會把從服務端擷取到的配置在本地檔案系統緩存一份,用于在遇到服務不可用,或網絡不通的時候,依然能從本地恢複配置,不影響應用正常運作。

本地緩存路徑預設位于以下路徑,是以請確定/opt/data或C:\opt\data\目錄存在,且應用有讀寫權限。

Mac/Linux: /opt/data/{appId}/config-cache

Windows: C:\opt\data{appId}\config-cache

本地配置檔案會以下面的檔案名格式放置于本地緩存路徑下:

Spring Boot 內建 Apollo 配置中心,真香、真強大!

上圖簡要描述了Apollo用戶端的實作原理

用戶端和服務端保持了一個長連接配接,進而能第一時間獲得配置更新的推送。

用戶端還會定時從 Apollo 配置中心服務端拉取應用的最新配置。

這是一個 fallback 機制,為了防止推送機制失效導緻配置不更新

用戶端定時拉取會上報本地版本,是以一般情況下,對于定時拉取的操作,服務端都會傳回 304 - Not Modified

定時頻率預設為每 5 分鐘拉取一次,用戶端也可以通過在運作時指定 apollo.refreshInterval 來覆寫,機關為分鐘。

用戶端從 Apollo 配置中心服務端擷取到應用的最新配置後,會儲存在記憶體中。

用戶端會把從服務端擷取到的配置在本地檔案系統緩存一份 在遇到服務不可用,或網絡不通的時候,依然能從本地恢複配置。

應用程式從 Apollo 用戶端擷取最新的配置、訂閱配置更新通知。

配置更新推送實作

前面提到了 Apollo 用戶端和服務端保持了一個長連接配接,進而能第一時間獲得配置更新的推送。長連接配接實際上我們是通過 Http Long Polling 實作的,具體而言:

用戶端發起一個 Http 請求到服務端

服務端會保持住這個連接配接 60 秒

如果在 60 秒内有用戶端關心的配置變化,被保持住的用戶端請求會立即傳回,并告知用戶端有配置變化的 namespace 資訊,用戶端會據此拉取對應 namespace 的最新配置

如果在 60 秒内沒有用戶端關心的配置變化,那麼會傳回 Http 狀态碼 304 給用戶端

用戶端在收到服務端請求後會立即重新發起連接配接,回到第一步

考慮到會有數萬用戶端向服務端發起長連,在服務端我們使用了 async servlet(Spring DeferredResult) 來服務 Http Long Polling 請求。

8、總體設計

Spring Boot 內建 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 程序中

9、可用性考慮

配置中心作為基礎服務,可用性要求非常高,下面的表格描述了不同場景下Apollo的可用性:

Spring Boot 內建 Apollo 配置中心,真香、真強大!

Apollo 配置中心建立項目與配置

接下來我們将建立一個 Apollo 的用戶端項目,引用 Apollo 來實作配置動态更新,不過在此之前我們需要提前進入 Apollo Portal 界面,在裡面提前建立一個項目并在其配置一個參數,友善後續用戶端引入該配置參數,測試是否能動态變化。

1、登入 Apollo

我這裡是部署到 Kubernetes 中,通過 NodePort 方式暴露出一個端口,打開這個位址登入 Apollo:

使用者名:apollo

密 碼:admin

Spring Boot 內建 Apollo 配置中心,真香、真強大!

2、修改與增加部門資料

在登入後建立項目時,選擇部門預設隻能選擇 Apollo 自帶的 測試部門1與測試部門2兩個選項。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

開始這真讓人迷糊,原來 Apoloo 沒有修改或新增部門資訊的管理節目,隻能通過修改資料庫,來新增或者修改資料,這裡打開 Portal 對月的資料庫中的表 ApolloPortalDB 修改 key 為 organizations 的 value 的 json 資料,改成自己對于的部門資訊。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

3、建立一個項目

修改完資料庫部門資訊後,重新登入 Apollo Portal,然後建立項目,這時候選擇部門可以看到已經變成我們自己修改後的部門資訊了,選擇我們自定義部門,然後設定應用 ID 為

apollo-test

,應用名為

apollo-demo

Spring Boot 內建 Apollo 配置中心,真香、真強大!

建立完成後進入配置管理界面:

Spring Boot 內建 Apollo 配置中心,真香、真強大!

4、建立一個配置參數

建立一個配置參數,友善後續 Apollo 用戶端項目引入該參數,進行動态配置測試。

Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
1
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4    <modelVersion>4.0.0modelVersion>
 5
 6    <parent>
 7        <groupId>org.springframework.bootgroupId>
 8        <artifactId>spring-boot-starter-parentartifactId>
 9        <version>2.1.8.RELEASEversion>
10    parent>
11
12    <groupId>club.mydlqgroupId>
13    <artifactId>apollo-demoartifactId>
14    <version>0.0.1version>
15    <name>apollo-demoname>
16    <description>Apollo Demodescription>
17
18    <properties>
19        <java.version>1.8java.version>
20    properties>
21
22    <dependencies>
23        <dependency>
24            <groupId>org.springframework.bootgroupId>
25            <artifactId>spring-boot-starter-webartifactId>
26        dependency>
27        <dependency>
28            <groupId>com.ctrip.framework.apollogroupId>
29            <artifactId>apollo-clientartifactId>
30            <version>1.4.0version>
31        dependency>
32    dependencies>
33
34    <build>
35        <plugins>
36            <plugin>
37                <groupId>org.springframework.bootgroupId>
38                <artifactId>spring-boot-maven-pluginartifactId>
39            plugin>
40        plugins>
41    build>
42
43project>      

2、配置檔案添加參數

在 application.yml 配置檔案中添加下面參數,這裡簡單介紹下 Apollo 參數作用:

apollo.meta: Apollo 配置中心位址。

apollo.cluster: 指定使用某個叢集下的配置。

apollo.bootstrap.enabled: 是否開啟 Apollo。

apollo.bootstrap.namespaces : 指定使用哪個 Namespace 的配置,預設 application。

apollo.cacheDir=/opt/data/some-cache-dir: 為了防止配置中心無法連接配接等問題,Apollo 會自動将配置本地緩存一份。

apollo.autoUpdateInjectedSpringProperties: Spring應用通常會使用 Placeholder 來注入配置,如${someKey:someDefaultValue},冒号前面的是 key,冒号後面的是預設值。如果想關閉 placeholder 在運作時自動更新功能,可以設定為 false。

apollo.bootstrap.eagerLoad.enabled : 将 Apollo 加載提到初始化日志系統之前,如果設定為 false,那麼将列印出 Apollo 的日志資訊,但是由于列印 Apollo 日志資訊需要日志先啟動,啟動後無法對日志配置進行修改,是以 Apollo 不能管理應用的日志配置,如果設定為 true,那麼 Apollo 可以管理日志的配置,但是不能列印出 Apollo 的日志資訊。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

3、建立測試 Controller 類

寫一個 Controller 類來輸出 test 變量的值,使用了 Spring 的 @Value 注解,用于讀取配置檔案中的變量的值,這裡來測試該值,項目啟動後讀取到的變量的值是設定在 application 配置檔案中的預設值,還是遠端 Apollo 中的值,如果是 Apollo 中配置的值,那麼再測試在 Apollo 配置中心中改變該變量的值後,這裡是否會産生變化。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

5、JVM 啟動參數添加啟動參數

由于本人的 Apollo 是部署在 Kubernetes 環境中的,JVM 參數中必須添加兩個變量:

env: 應用使用 Apollo 哪個環境,例如設定為 DEV 就是指定使用開發環境,如果設定為 PRO 就是制定使用生産環境。

apollo.configService: 指定配置中心的位址,跳過 meta 的配置,在測試時指定 meta 位址無效果。如果 Apollo 是部署在 Kubernetes 中,則必須設定該參數為配置中心位址,如果 Apollo 不是在 Kubernetes 環境中,可以不設定此參數,隻設定 meta 參數即可。一般情況下,configService 和 meta 值一緻。

如果是在 Idea 中啟動,可以配置啟動參數,加上:

1-Dapollo.configService=http://192.168.2.11:30002 -Denv=DEV

Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!

(2)、兩個叢集都配置同樣的參數不同的值

在兩個叢集

beijing

shanghai

中,都統一配置參數

test

,并且設定不同的值。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

(3)、示例項目 application.yml 修改叢集配置參數,并啟動項目觀察結果

指定叢集為 beijing:

Spring Boot 內建 Apollo 配置中心,真香、真強大!

可以看到用的是 shanghai 叢集的配置

3、不同命名空間下的配置

(1)、建立兩個命名空間

命名空間有兩種,一種是 public(公開),一種是 private 私有,公開命名空間所有項目都能讀取配置資訊,而私有的隻能 app.id 值屬于該應用的才能讀取配置。

這裡建立 dev-1 與 dev-2 兩個私有的命名空間,用于測試。

Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!

在兩個命名空間中,都統一配置參數

test

,并且設定不同的值,設定完後釋出。

Spring Boot 內建 Apollo 配置中心,真香、真強大!

(3)、示例項目 application.yml 修改命名空間配置參數,并啟動項目觀察結果

指定命名空間為 dev-1:

1......
1......
2
3apollo:
4  bootstrap:
5    namespaces: dev-1                   #設定 dev-1 命名空間
6
7......
      
Spring Boot 內建 Apollo 配置中心,真香、真強大!
Spring Boot 內建 Apollo 配置中心,真香、真強大!

springboot-apollo.yaml

1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: springboot-apollo
 5spec:
 6  type: NodePort
 7  ports:
 8    - name: server
 9      nodePort: 31080
10      port: 8080
11      targetPort: 8080
12    - name: management
13      nodePort: 31081
14      port: 8081
15      targetPort: 8081
16  selector:
17    app: springboot-apollo
18---
19apiVersion: apps/v1
20kind: Deployment
21metadata:
22  name: springboot-apollo
23  labels:
24    app: springboot-apollo
25spec:
26  replicas: 1
27  selector:
28    matchLabels:
29      app: springboot-apollo
30  template:
31    metadata:
32      name: springboot-apollo
33      labels:
34        app: springboot-apollo
35    spec:
36      restartPolicy: Always
37      containers:
38        - name: springboot-apollo
39          image: mydlqclub/springboot-apollo:0.0.1
40          imagePullPolicy: Always
41          ports:
42            - containerPort: 8080
43              name: server
44          env:
45            - name: JAVA_OPTS
46              value: "-Denv=DEV"
47              ##注意修改此處的 mydlqcloud 為你自己的 Namespace 名稱
48            - name: APP_OPTS
49              value: "
50                     --app.id=apollo-demo
51                     --apollo.bootstrap.enabled=true
52                     --apollo.bootstrap.eagerLoad.enabled=false
53                     --apollo.cacheDir=/opt/data/
54                     --apollo.cluster=default
55                     --apollo.bootstrap.namespaces=application
56                     --apollo.autoUpdateInjectedSpringProperties=true
57                     --apollo.meta=http://service-apollo-config-server-dev.mydlqcloud:8080    
58                     "
59          resources:
60            limits:
61              memory: 1000Mi
62              cpu: 1000m
63            requests:
64              memory: 500Mi
65              cpu: 500m      

(2)、部署 SpringBoot 應用到 Kubernetes

-n:建立應用到指定的 Namespace 中。

1$ kubectl apply -f springboot-apollo.yaml -n mydlqcloud

3、測試部署的應用接口

上面的應用配置了 NodePort 端口,可以通過此端口通路 Kubernetes 叢集内的應用接口,本人 Kubernetes 叢集位址為 192.168.2.11 且 NodePort 端口為 31081,是以浏覽器通路位址

http://192.168.2.11:31081/test

來測試接口,顯示:

1test的值為:123456

可以看到能通過 Apollo 擷取參數值,此文章到此結束。