天天看點

微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

目錄

  • 前言
  • 1. Nacos 配置中心基礎知識
    • 1.1 Nacos 在配置中心中的功能
    • 1.2 Nacos 配置管理 Data ID 的構成
    • 1.3 Nacos 配置的復原機制
    • 1.4 Nacos 配置的圖形化管理界面
    • 1.5 Namespace、Group、Data ID 三者的關系
    • 1.6 Nacos 對配置的 CRUD
    • 1.7 Nacos 動态監聽的長輪詢機制
    • 1.8 Nacos 配置中心的源碼分析
  • 2. Nacos 基礎配置
    • 2.1 下載下傳 Nacos 伺服器
    • 2.2 引入 pom.xml 依賴檔案
    • 2.3 修改 yml 配置檔案
    • 2.4 在主程式類上添加注解
    • 2.5 編寫業務類
    • 2.6 在 Nacos 伺服器中添加配置資訊
    • 2.7 報錯無法裝配 bean
  • 3. Nacos 加載配置的三種方案
    • 3.1 Data ID 方案
    • 3.2 Group 方案
    • 3.3 Namespace 方案
  • 最後

參考資料:

《Spring Microservices in Action》

《Spring Cloud Alibaba 微服務原理與實戰》

《B站 尚矽谷 SpringCloud 架構開發教程 周陽》

《Nacos 官網》

Nacos 緻力于解決微服務中的統一配置、服務注冊與發現等問題。它提供了一組簡單易用的特性集,幫助開發者快速實作動态服務發現、服務配置、服務中繼資料及流量管理;

  • CRUD、版本管理、灰階管理、監聽管理、推送軌迹、聚合資料等功能;

  • ${prefix}-${spring.profiles.active}.${file-extension}

  • 預設為:

    ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

    • prefix:預設為

      spring.application.name

      的值,也可以通過配置項

      spring.cloud.nacos.config.prefix

      來配置;
    • spring.profiles.active:即為目前環境對應的

      profile

      。當 spring.profiles.active 為空時,對應的連接配接符 - 也将不存在,dataId 的拼接格式變成

      ${prefix}.${file-extension}

    • file-exetension:為配置内容的資料格式,可以通過配置項

      spring.cloud.nacos.config.file-extension

      來配置。目前隻支援 properties 和 yaml 類型;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

  • Nacos 會記錄配置檔案的曆史版本預設保留 30 天,此外還有一鍵復原功能,復原操作将會觸發配置更新;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

  • 配置管理;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 命名空間;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 類似 Java 裡面的 package 名和類名,最外層的 Namespace 是可以用于區分部署環境的,Group 和 Data ID 邏輯上區分兩個目标對象;
  • 預設情況下,

    Namespace=public

    Group=DEFAULT_GROUP

    Cluster=DEFAULT

    • Namespace 命名空間:主要用來實作隔離,用于解決多環境及多租戶資料的隔離問題。比如有三個環境:開發、測試、生産環境,可以建立三個Namespace,不同的Namespace 之間是隔離的;
    • Group 分組:可以把不同的微服務劃分到同一個分組裡面去,用來實作 Data ID 分組管理的機制;
    • Data ID:通常用于組織劃分系統的配置集;
    • Cluster 簇:對指定微服務的一個虛拟劃分;
    • Instance 執行個體:微服務的執行個體;
  • 官方的建議是,通過 Namespace 來區分不同的環境,而 Group 可以專注在業務層面的資料分組;

  • 主要通過提供 Open API 接口或 SDK 實作;
  • 用戶端通過 Open API 或調用 SDK 接口發送請求給伺服器,伺服器解析請求,并做相應的處理;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
對配置的操作 SDK Open API 說明
釋出配置 public boolean publishConfig(String dataId, String group, String content) throws NacosException POST: /nacos/v1/cs/configs 将配置儲存到 Nacos Config Server 中
删除配置 public boolean removeConfig(String dataId, String group)throws NacosException DELETE: /nacos/v1/cs/configs 删除配置中心的指定配置
擷取配置 public string getConfig(String dataId, String group, long timeoutMs) throws NacosException GET: /nacos/v1/cs/configs 從 Nacos Config Server 中讀取配置
監聽配置 public void addListener(String dataId, String group, Listener listener) POST: /nacos/v1/cs/configs/listener 訂閱感興趣的配置,當配置發生變化時可以收到一個事件

  • 一般來說,動态監聽有兩種機制:
比較項 Pull 機制 Push 機制
用戶端從服務端主動拉取資料 服務端主動把資料推送到用戶端
缺點 不能保證資料實時性;在服務端配置長時間不更新的情況下,用戶端的定時任務會做一些無效的 Pull 如果用戶端的數量比較多,服務端需要耗費大量的記憶體資源來儲存每個連接配接;需要心跳機制來維持每個連接配接狀态
  • Nacos 的解決方案:長輪詢機制:
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 如果用戶端發起 Pull 請求,服務端收到請求之後,先檢查配置是否發生了變更:
    • 變更:傳回變更配置;
    • 無變更:設定一個定時任務,延期 29.5s 執行,把目前的用戶端長輪詢連接配接加入 allSubs 隊列;
  • 在這 29.5s 内的配置變化:
    • 配置無變化:等待 29.5s 後觸發自動檢查機制,傳回配置;
    • 配置變化:在 29.5s 内任意一個時刻配置變化,會觸發一個事件機制,監聽到該事件的任務會周遊 allSubs 隊列,找到發生變更的配置項對應的 ClientLongPolling 任務,将變更的資料通過該任務中的連接配接進行傳回。相當于完成了一次 PUSH 操作;
  • 長輪詢機制結合了 Pull 機制和 Push 機制的優點;
  • 源碼分析詳情請見:微服務架構 | *2.5 Nacos 長輪詢定時機制的源碼分析;

  • 由于篇幅有限,該内容放在以下兩篇文章:
    • 點選跳轉:微服務架構 | *2.3 Spring Cloud 啟動及加載配置檔案源碼分析(以 Nacos 為例)
    • 點選跳轉:微服務架構 | *2.4 Nacos 擷取配置與事件訂閱機制的源碼分析
    • 點選跳轉:微服務架構 | *2.5 Nacos 長輪詢定時機制的源碼分析

以 Data ID 方案為例,更多方案詳情請見本篇第三點《3. Nacos 加載配置的三種方案》;

  • 詳情請見:《微服務架構 | 3.2 Alibaba Nacos 注冊中心》的 2. 安裝并運作 Nacos 伺服器;

<!--nacos-config-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
           

  • bootstrap.yml:
# nacos配置
server:
  port: 18082

spring:
  application:
    name: nacos-config-client     #必須,構成 Nacos 配置管理 Data ID 字段的一部分
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848     #Nacos 服務注冊中心位址
      config:
        server-addr: localhost:8848     #Nacos 作為配置中心位址
        file-extension: yaml     #指定 yaml 格式的配置
        
        # prefix: hhh     #Data ID 的字首,如果不指定,就是 nacos-config-client-dev.yaml。指定後,是 hhh-dev.yaml
        # refresh: true      #是否動态重新整理
        
        # group: DEFAULT_GROUP     #指定組
        # namespace: PUBLIC     #指定命名空間 ID
           
  • application.yml:
spring:
  profiles:
    active: dev # 表示開發環境
           

  • @EnableDiscoveryClient:使用其他元件(Nacos、zookeeper、Consul)作為注冊中心;

  • 這裡僅編寫一個 controller 作為示例:
@RestController
@RefreshScope //使目前類下的配置支援 Nacos 的動态重新整理功能
public class ConfigClientController{
    @Value("${config.info}")
    private String configInfo;

    @GetMapping("/config/info")
    public String getConfigInfo() {
        return configInfo;
    }
}
           

  • 在 Nacos 伺服器裡建立并添加配置:
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 啟動服務,調用接口:

    http://localhost:18082/config/info

    擷取配置資訊;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

  • 注意:如果

    .yml

    和 Nacos 伺服器配置的

    Data ID

    比對不上,将導緻 ConfigClientController 類裡的

    ${config.info}

    找不到,最終報 ConfigClientController 無法裝配的錯誤,如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.configClientController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'config.info' in value "${config.info}"
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:380) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$1(AbstractBeanFactory.java:356) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.cloud.context.scope.GenericScope$BeanLifecycleWrapper.getBean(GenericScope.java:389) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.cloud.context.scope.GenericScope.get(GenericScope.java:186) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:353) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	at org.springframework.cloud.context.scope.refresh.RefreshScope.eagerlyInitialize(RefreshScope.java:134) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.cloud.context.scope.refresh.RefreshScope.start(RefreshScope.java:125) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.cloud.context.scope.refresh.RefreshScope.onApplicationEvent(RefreshScope.java:119) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.cloud.context.scope.refresh.RefreshScope.onApplicationEvent(RefreshScope.java:73) ~[spring-cloud-context-2.1.2.RELEASE.jar:2.1.2.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]
	......
           

  • 以下對 bootstrap.yml 的修改都可以在啟動時配置 JVM 環境替代,使用如下指令:
  • -Dspring.profiles.active=${profile}

  • 建立三個 Data ID 的環境:
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 指定

    spring.profile.active

    中的配置,重新開機服務,可以看到配置已經切換成其他環境了:
spring:
  profiles:
    active: prod
           
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

  • 建立三個 Group 的環境:
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • spring.profile.active

    spring.cloud.nacos.config.group

spring:
  profiles:
    active: info #修改
           
server:
  port: 18082
spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 
      config:
        server-addr: localhost:8848
        file-extension: yaml
        group: PROD_GROUP #新增
           
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

  • 建立兩個 Namespace 的環境:
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • 克隆 / 建立幾份配置檔案到新的命名空間,然後編輯修改;
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理
  • spring.profile.active

    spring.cloud.nacos.config.group

spring:
  profiles:
    active: dev #修改
           
server:
  port: 18082
spring:
  application:
    name: nacos-config-client
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 
      config:
        server-addr: localhost:8848
        file-extension: yaml
        group: DEFAULT_GROUP #新增
        namespace: 48b2da7d-0b26-4c15-907b-9a379db8f7de #新增,命名空間的 ID,在建立命名空間時會給出
           
微服務架構 | 2.2 Alibaba Nacos 的統一配置管理

新人制作,如有錯誤,歡迎指出,感激不盡!

歡迎關注公衆号,會分享一些更日常的東西!

如需轉載,請标注出處!

微服務架構 | 2.2 Alibaba Nacos 的統一配置管理