Spring Cloud 系列之 Apollo 配置中心(一)


背景

  

  

  隨着程序功能的日益復雜,程序的配置日益增多:各種功能的開關、參數的配置、服務器的地址等等。

  對程序配置的期望值也越來越高:配置修改后實時生效,灰度發布,分環境、分集群管理配置,完善的權限、審核機制等等。

  在這樣的大環境下,傳統的通過配置文件、數據庫等方式已經越來越無法滿足開發人員對配置管理的需求。

  Apollo 配置中心應運而生!Apollo - 一個可靠的配置管理系統。

  

Apollo 介紹

  

  Apollo(阿波羅)是攜程框架部門研發的分布式配置中心,能夠集中化管理應用不同環境、不同集群的配置,配置修改后能夠實時推送到應用端,並且具備規范的權限、流程治理等特性,適用於微服務配置管理場景。服務端基於 Spring Boot 和 Spring Cloud 開發,打包后可以直接運行,不需要額外安裝 Tomcat 等應用容器。

  Apollo 支持 4 個維度管理 Key-Value 格式的配置:

  1. application (應用)
  2. environment (環境)
  3. cluster (集群)
  4. namespace (命名空間 Namespace 是配置項的集合,類似於一個配置文件的概念)

  同時,Apollo 基於開源模式開發,開源地址:https://github.com/ctripcorp/apollo

  官網文檔:https://github.com/ctripcorp/apollo/wiki/Quick-Start

  演示環境(Demo):

上圖是Apollo配置中心中一個項目的配置首頁

  • 在頁面左上方的環境列表模塊展示了所有的環境和集群,用戶可以隨時切換。
  • 頁面中央展示了兩個namespace(application和FX.apollo)的配置信息,默認按照表格模式展示、編輯。用戶也可以切換到文本模式,以文件形式查看、編輯。
  • 頁面上可以方便地進行發布、回滾、灰度、授權、查看更改歷史和發布歷史等操作。

  

Apollo 核心概念

  

  1. application (應用)
    • 這個很好理解,就是實際使用配置的應用,Apollo客戶端在運行時需要知道當前應用是誰,從而可以去獲取對應的配置
    • 每個應用都需要有唯一的身份標識 -- appId,我們認為應用身份是跟着代碼走的,所以需要在代碼中配置,具體信息請參見Java客戶端使用指南
  2. environment (環境)
    • 配置對應的環境,Apollo客戶端在運行時需要知道當前應用處於哪個環境,從而可以去獲取應用的配置
    • 我們認為環境和代碼無關,同一份代碼部署在不同的環境就應該能夠獲取到不同環境的配置
    • 所以環境默認是通過讀取機器上的配置(server.properties中的env屬性)指定的,不過為了開發方便,我們也支持運行時通過System Property等指定,具體信息請參見Java客戶端使用指南
  3. cluster (集群)
    • 一個應用下不同實例的分組,比如典型的可以按照數據中心分,把上海機房的應用實例分為一個集群,把北京機房的應用實例分為另一個集群。
    • 對不同的cluster,同一個配置可以有不一樣的值,如zookeeper地址。
    • 集群默認是通過讀取機器上的配置(server.properties中的idc屬性)指定的,不過也支持運行時通過System Property指定,具體信息請參見Java客戶端使用指南
  4. 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

  

  • 基於EurekaSpring 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、錯誤重試

  

服務端

  

上圖簡要描述了配置發布的大致過程:

  1. 用戶在Portal操作配置發布
  2. Portal調用Admin Service的接口操作發布
  3. Admin Service發布配置后,發送ReleaseMessage給各個Config Service
  4. Config Service收到ReleaseMessage后,通知對應的客戶端

  

客戶端

  

上圖簡要描述了Apollo客戶端的實現原理:

  1. 客戶端和服務端保持了一個長連接,從而能第一時間獲得配置更新的推送。(通過Http Long Polling實現)
  2. 客戶端還會定時從Apollo配置中心服務端拉取應用的最新配置。
    • 這是一個fallback機制,為了防止推送機制失效導致配置不更新
    • 客戶端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,服務端都會返回304 - Not Modified
    • 定時頻率默認為每5分鍾拉取一次,客戶端也可以通過在運行時指定System Property: apollo.refreshInterval來覆蓋,單位為分鍾。
  3. 客戶端從Apollo配置中心服務端獲取到應用的最新配置后,會保存在內存中
  4. 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份
    • 在遇到服務不可用,或網絡不通的時候,依然能從本地恢復配置
  5. 應用程序可以從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網速不給力的話,可以從百度網盤下載。

  1. 從Github下載
  2. 從百度網盤下載
  3. 為啥安裝包要58M這么大?
    • 因為這是一個可以自啟動的jar包,里面包含了所有依賴jar包以及一個內置的tomcat容器

  

安裝 Apollo

  

創建數據庫

  

  Apollo 服務端共需要兩個數據庫:ApolloPortalDBApolloConfigDB,我們把數據庫、表的創建和樣例數據都分別准備了 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-serviceorder-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-serviceorder-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-serviceorder-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-serviceorder-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/namehttp://localhost:9091/name 結果如下:

  

  訪問:http://localhost:9090/mysqlhttp://localhost:9091/mysql 結果如下:

  

新增配置信息

  

  進入項目后點擊右上角的 新增配置

  

  添加配置項 namemysql.usernamemysql.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/namehttp://localhost:9091/name 結果如下:

  

  訪問:http://localhost:9090/mysqlhttp://localhost:9091/mysql 結果如下:

以上只是 Apollo 的入門教程,后面我們會學習 Apollo 的更多高級玩法,比如多環境部署,高可用環境搭建等等。

下一篇我們講解 Apollo 部門管理、用戶管理、配置管理、集群管理,記得關注噢~

本文采用 知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議

大家可以通過 分類 查看更多關於 Spring Cloud 的文章。

  

🤗 您的點贊轉發是對我最大的支持。

📢 掃碼關注 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕松噢 ~


免責聲明!

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



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