開發與維運

Spring Cloud Alibaba 分佈式配置

1. 簡介

Nacos 提供用於存儲配置和其他元數據的 key/value 存儲,為分佈式系統中的外部化配置提供服務器端和客戶端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 應用的外部屬性配置。

Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客戶端和服務器上的概念與 Spring Environment 和 PropertySource 有著一致的抽象,在特殊的 bootstrap 階段,配置被加載到 Spring 環境中。當應用程序通過部署管道從開發到測試再到生產時,您可以管理這些環境之間的配置,並確保應用程序具有遷移時需要運行的所有內容。Nacos 的獲取和啟動方式可以參考 Nacos 官網

如果讀者是阿里雲商業化組件 ANS 或 ACM 用戶,請使用 Nacos Config 代替對應的組件。

2. 學習目標

  • 使用 Nacos Config 作為 Spring Cloud 分佈式配置
  • 使用 Nacos Config 實現 Bean 動態刷新
  • 瞭解 Nacos Config 高級配置

3. 詳細內容

  • 快速上手:使用 Nacos Config 作為外部化配置源
  • 多文件擴展名支持:以 YAML 文件擴展名為例,討論 Nacos Config 多文件擴展名支持
  • 動態配置更新:演示 @RefreshScope 特性,實現 Bean 動態刷新
  • 自定義擴展:自定義 namespace、Group 以及 Data Id 的配置擴展
  • 運維特性:演示 Nacos Config 高級外部化配置以及 Endpoint 內部細節

4. 快速上手

4.1 如何引入 Nacos Config 支持分佈式配置

Nacos Config 引入的方式同樣也有兩種,即 Aliyun Java Initializr 引入和 Maven pom.xml 依賴。官方推薦使用 Aliyun Java Initializr 方式引入 Nacos Discovery,以便簡化組件之間的依賴關係。

4.1.1 [簡單] 通過 Aliyun Java Initializr 創建工程並引入 Nacos Config(推薦)

由於 Spring Cloud 組件的版本和依賴較為複雜,推薦讀者使用 Aliyun Java Initializr 構建應用工程。
讀者選擇偏好的 Web 瀏覽器訪問 Aliyun Java Initializr,其資源網址為:https://start.aliyun.com/bootstrap.html

下文以 Google Chrome 瀏覽器為例,當網頁加載後,首先,在 "項目基本信息" 部分輸入 Group :“com.alibaba.cloud” 以及 Artifact:“nacos-config-sample”。然後,“組件依賴” 輸入框搜索:“Nacos Config”,選擇 "Nacos Configuration",如下所示:

1.png

同上組件操作,增加 “Spring Web” 和 “Spring Boot Actuator” 組件:

2.png

組件選擇後,點擊 “生成” 高亮按鈕。隨後,平臺將生成一個名為 “nacos-config-sample.zip” 的壓縮文件,將其保存到本地目錄,並解壓該文件,工程目錄將隨之生成。打開目錄下的 pom.xml 文件,不難發現 Nacos starter 聲明其中(以下 XML 內容均來自於項目根路徑中的 pom.xml 文件):

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

不過該 starter 並未指定版本,具體的版本聲明在 com.alibaba.cloud:spring-cloud-alibaba-dependencies 部分:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其中,${spring-cloud-alibaba.version} 和 ${spring-boot.version} 分別為 Spring Cloud Alibaba 和 Spring Boot 組件依賴的版本,它們的版本定義在 <properties>元素中,即 2.2.1.RELEASE2.3.0.RELEASE

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
    </properties>

如果讀者非常熟悉 Maven 依賴管理的配置方式,可以考慮 Maven pom.xml 依賴 Nacos Config。

4.1.2 [高級] 通過 Maven pom.xml 依賴 Nacos Config

如果要在您的項目中使用 Nacos 來實現服務註冊/發現,使用 group ID 為 com.alibaba.cloud和 artifact ID 為 spring-cloud-starter-alibaba-nacos-config的 starter。

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

該聲明方式同樣需要聲明 com.alibaba.cloud:spring-cloud-alibaba-dependencies,內容與上小節相同,在此不再贅述。下一節將討論如何使用 Nacos Config 支持分佈式配置。

4.1.3 [偷懶] 直接在沙箱裡查看應用代碼

點擊 鏈接,直接訪問沙箱環境,這裡會有為你準備好的案例代碼^_^。

4.2 使用 Nacos Config 實現分佈式配置

使用 Nacos Config 實現分佈式配置與 Spring Cloud Consul 和 Spring Cloud Zookeeper 的方式非常類似,僅需添加相關外部化配置即可工作。換言之,Nacos Config 同樣不會侵入應用代碼,方便應用整合和遷移,如果讀者熟悉 Spring Cloud Consul 或 Spring Cloud Zookeeper 使用方式 的話,通常需要將 Consul 或 Zookeeper 服務進程預先部署,Nacos Config 也如此。

4.2.1 啟動 Nacos 服務器

我們為開發者提供了一套免費的 Nacos Server :進入 http://139.196.203.133:8848/nacos/ 查看控制檯(賬號名/密碼為 nacos-configuration/nacos-configuration),選擇 "配置管理/配置列表":

3.png

由於服務是公共免費的,為了做好隔離,所以分佈式配置的功能,請選擇在 sandbox-configuration 的命名空間下操作。

具體啟動方式參考 Nacos 官網
關於更多的 Nacos Server 版本,可以從 release 頁面 下載最新的版本。

4.2.2 添加 Nacos 配置

點擊“配置列表”頁面右側的 "+" 號(紅色箭頭所指):

4.png

瀏覽器跳轉新頁面,並填充內容如下:

5.png

其中,Data ID 由應用名(nacos-config-sample)+ 文件後綴名(.properties) 組成,點擊“發佈”按鈕(紅色箭頭所指),配置內容為:

user.name=nacos-config-sample
user.age=90

發佈成功後,控制檯會出現提示彈出框。

特別提醒

圖片中的 Data ID 中的應用名,使用的是 nacos-config-sample 。如果使用我們提供的 Nacos Server ,由於多個用戶同時使用,可能存在應用名衝突的問題。建議修改為跟別人都不重複的應用名:
spring.application.name=xxxxxx
替換上面的xxxxx部分

4.2.3 配置應用 Nacos Config Server 地址

回到應用 nacos-config-sample 工程,在 resources 目錄下新建名為 “application.properties" 文件,並配置以下內容:

spring.cloud.nacos.config.server-addr=139.196.203.133:8848
spring.cloud.nacos.config.username=nacos-configuration
spring.cloud.nacos.config.password=nacos-configuration
spring.cloud.nacos.config.namespace=sandbox-configuration

注意,Nacos Server 地址必須配置在 application.properties 文件。

注意當你使用域名的方式來訪問 Nacos 時, spring.cloud.nacos.config.server-addr配置的方式為域名:port。 例如 Nacos 的域名為abc.com.nacos,監聽的端口為 80,則 spring.cloud.nacos.config.server-addr=abc.com.nacos:80。 注意 80 端口不能省略。

4.2.4 添加讀取 Nacos Config 實現

修改 nacos-config-sample 引導類,如下所示:

@SpringBootApplication
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

4.2.5 啟動 Nacos Config 應用

運行 nacos-config-sample 引導類 NacosConfigSampleApplication,觀察控制檯結果(截取關鍵日誌信息):

[init] user name : nacos-config-sample , age : 90

如果在沙箱內啟動應用,由於暫時還無法看到運行時的日誌,可以通過WEB 控制器的方式查看參數,具體操作見 5.1 節。

5. 使用 Nacos Config 實現 Bean 動態刷新

Nacos Confg 支持標準 Spring Cloud @RefreshScope特性,即應用訂閱某個 Nacos 配置後,當配置內容變化時,Refresh Scope Beans 中的綁定配置的屬性將有條件的更新。所謂的條件是指 Bean 必須:

  • 必須條件:Bean 的聲明類必須標註 @RefreshScope
  • 二選一條件:

    • 屬性(非 static 字段)標註 @Value
    • @ConfigurationPropertiesBean

除此之外,Nacos Confg 也引入了 Nacos Client 底層數據變化監聽接口,即 com.alibaba.nacos.api.config.listener.Listener。下面的內容將分別討論這三種不同的使用場景。

Nacos Client:Nacos 客戶端 API,也是 Nacos Config 底層依賴

5.1 使用 Nacos Config 實現 Bean @Value屬性動態刷新

基於應用 nacos-config-sample 修改,將引導類 NacosConfigSampleApplication標註@RefreshScope@RestController,使得該類變為 Spring MVC REST 控制器,同時具備動態刷新能力,具體代碼如下:

@SpringBootApplication
@RestController
@RefreshScope
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return String.format("[HTTP] user name : %s , age : %d", userName, userAge);
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

重啟引導類 NacosConfigSampleApplication,控制檯輸出如故:

[init] user name : nacos-config-sample , age : 90

再通過命令行訪問 REST 資源 /user

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 90

如果使用沙箱環境,請直接點擊應用列表訪問按鈕,並在打開的瀏覽器窗口的地址欄中追加/user,如下圖:

本文中,其他的基於 http 訪問的步驟類似,後面不在贅述。

本次請求結果中的 user name 和 age 數據與應用啟動時的一致,因為此時 Nacos Server 中的配置數據沒變化。

隨後,通過 Nacos 控制檯調整 nacos-config-sample.properties 配置,將 user.age 從 90 變更為 99:

6.png

點擊“發佈”按鈕,觀察應用日誌變化(部分內容被省略):

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=4a8cb29154adb9a0e897e071e1ec8d3c, content=user.name=nacos-config-sample
user.age=99, type=properties
o.s.boot.SpringApplication               : Started application in 0.208 seconds (JVM running for 290.765)
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]
  • 第 1 和 2 行代碼是由 Nacos Client 輸出,通知開發者具體的內容變化,不難發現,這裡沒有輸出完整的配置內容,僅為變更部分,即配置 user.age。
  • 第 3 行日誌似乎讓 SpringApplication 重啟了,不過消耗時間較短,這裡暫不解釋,後文將會具體討論,只要知道這與 Bootstrap 應用上下文相關即可。
  • 最後一行日誌是由 Spring Cloud 框架輸出,提示開發人員具體變更的 Spring 配置 Property,可能會有多個,不過本例僅修改一處,所以顯示單個。

接下來,重新訪問 REST 資源 /user

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 99

終端日誌顯示了這次配置變更同步到了 @Value("${user.age}") 屬性 userAge 的內容。除此之外,應用控制檯也輸出了以下內容:

[init] user name : nacos-config-sample , age : 99

而該日誌是由 init()方法輸出,那麼是否說明該方法被框架調用了呢?答案是肯定的。既然 @PostConstruct方法執行了,那麼 @PreDestroy方法會不會被調用呢?不妨增加 Spring Bean 銷燬回調方法:

@SpringBootApplication
@RestController
@RefreshScope
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }
    
    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    ...
}

再次重啟引導類 NacosConfigSampleApplication,初始化日誌仍舊輸出:

[init] user name : nacos-config-sample , age : 99

將配置 user.age 內容從 99 調整為 18,觀察控制檯日誌變化:

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=e25e486af432c403a16d5fc8a5aa4ab2, content=user.name=nacos-config-sample
user.age=18, type=properties
o.s.boot.SpringApplication               : Started application in 0.208 seconds (JVM running for 144.467)
[destroy] user name : nacos-config-sample , age : 99
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

相較於前一個版本,日誌插入了 destroy()方法輸出內容,並且Bean 屬性 userAge 仍舊是變更前的數據 99。隨後,再次訪問 REST 資源 /user,其中終端日誌:

% curl http://127.0.0.1:8080/user
[HTTP] user name : nacos-config-sample , age : 18

應用控制檯日誌:

[init] user name : nacos-config-sample , age : 18

兩者與前一版本並無差異,不過新版本給出了一個現象,即當 Nacos Config 接收到服務端配置變更時,對應的 @RefreshScopeBean 生命週期回調方法會被調用,並且是先銷燬,然後由重新初始化。本例如此設計,無非想提醒讀者,要意識到 Nacos Config 配置變更對 @RefreshScopeBean 生命週期回調方法的影響,避免出現重複初始化等操作。

注: Nacos Config 配置變更調用了 Spring Cloud API ContextRefresher,該 API 會執行以上行為。同理,執行 Spring Cloud Acutator Endpoint refresh也會使用 ContextRefresher

通過上述討論,相信讀者已對 Nacos 配置變更操作相當的熟悉,後文將不再贅述相關配置。接下來繼續討論 @ConfigurationPropertiesBean 的場景。

5.2 使用 Nacos Config 實現

@ConfigurationPropertiesBean 屬性動態刷新
在應用 nacos-config-sample 新增 User類,並標註 @RefreshScope@ConfigurationProperties,代碼如下:

@RefreshScope
@ConfigurationProperties(prefix = "user")
public class User {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

根據 @ConfigurationProperties的定義, User類的屬性綁定到了配置屬性前綴 user。下一步,調整引導類,代碼如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @Autowired
    private User user;

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return "[HTTP] " + user;
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

較前一個版本 NacosConfigSampleApplication實現,主要改動點:

  • 激活 @ConfigurationPropertiesBean @EnableConfigurationProperties(User.class)
  • 通過 @Autowired依賴注入 UserBean
  • 使用 user Bean( toString() 方法替換 user()中的實現

下一步,重啟應用後,再將 user.age 配置從 18 調整為 99,控制檯日誌輸出符合期望:

[init] user name : nacos-config-sample , age : 18
......
[fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=b0f42fac52934faf69757c2b6770d39c, content=user.name=nacos-config-sample
user.age=90, type=properties
......
[destroy] user name : nacos-config-sample , age : 18
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

接下來,訪問 REST 資源 /user,觀察終端日誌輸出:

% curl http://127.0.0.1:8080/user
[HTTP] User{name='nacos-config-sample', age=90}

User Bean 屬性成功地變更為 90,達到實戰效果。上小節提到 Nacos Config 配置變更會影響 @RefreshScopeBean 的生命週期方法回調。同理,如果為 User增加初始化和銷燬方法的話,也會出現行文,不過本次將 User實現 Spring 標準的生命週期接口 InitializingBeanDisposableBean

@RefreshScope
@ConfigurationProperties(prefix = "user")
public class User implements InitializingBean, DisposableBean {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("[afterPropertiesSet()] " + toString());
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("[destroy()] " + toString());
    }
}

代碼調整後,重啟應用,並修改配置(90 -> 19),觀察控制檯日誌輸出:

[init] user name : nacos-config-sample , age : 90
......
c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=30d26411b8c1ffc1d16b3f9186db498a, content=user.name=nacos-config-sample
user.age=19, type=properties
......
[destroy()] User{name='nacos-config-sample', age=90}
[afterPropertiesSet()] User{name='nacos-config-sample', age=19}
[destroy] user name : nacos-config-sample , age : 90
......
o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [user.age]

不難發現, UserBean 的生命週期方法不僅被調用,並且仍舊是先銷燬,再初始化。那麼,這個現象和之前看到的 SpringApplication重啟是否有關係呢?答案也是肯定的,不過還是後文再討論。

下一小節將繼續討論怎麼利用底層 Nacos 配置監聽實現 Bean 屬性動態刷新

5.3 使用 Nacos Config 監聽實現 Bean 屬性動態刷新

前文曾提及 com.alibaba.nacos.api.config.listener.Listener是 Nacos Client API 標準的配置監聽器接口,由於僅監聽配置內容,並不能直接與 Spring 體系打通,因此,需要藉助於 Spring Cloud Alibaba Nacos Config API NacosConfigManager(感謝小夥伴 liaochuntao 和 zkzlx 的代碼貢獻),代碼調整如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {

    @Value("${user.name}")
    private String userName;

    @Value("${user.age}")
    private int userAge;

    @Autowired
    private User user;

    @Autowired
    private NacosConfigManager nacosConfigManager;

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            String dataId = "nacos-config-sample.properties";
            String group = "DEFAULT_GROUP";
            nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    System.out.println("[Listener] " + configInfo);
                }
            });
        };
    }

    @PostConstruct
    public void init() {
        System.out.printf("[init] user name : %s , age : %d%n", userName, userAge);
    }

    @PreDestroy
    public void destroy() {
        System.out.printf("[destroy] user name : %s , age : %d%n", userName, userAge);
    }

    @RequestMapping("/user")
    public String user() {
        return "[HTTP] " + user;
    }

    public static void main(String[] args) {
        SpringApplication.run(NacosConfigSampleApplication.class, args);
    }
}

代碼主要變化:

  • @Autowired依賴注入 NacosConfigManager
  • 新增 runner()方法,通過 NacosConfigManagerBean 獲取 ConfigService,並增加了 AbstractListener( Listener抽象類)實現,監聽 dataId = "nacos-config-sample.properties" 和 group = "DEFAULT_GROUP" 的配置內容

重啟應用,並將配置 user.age 從 19 調整到 90,觀察日誌變化:

c.a.n.client.config.impl.ClientWorker    : [fixed-127.0.0.1_8848] [data-received] dataId=nacos-config-sample.properties, group=DEFAULT_GROUP, tenant=null, md5=b0f42fac52934faf69757c2b6770d39c, content=user.name=nacos-config-sample
user.age=90, type=properties
[Listener] user.name=nacos-config-sample
user.age=90
......

在第 1 行日誌下方,新增了監聽實現代碼的輸出內容,不過這段內容是完整的配置,而非變化的內容。讀者請務必注意其中的差異。下一步要解決的是將配置映射到 Bean 屬性,此處給出一個簡單的解決方案,實現步驟有兩個:

  • 將 String 內容轉化為 Properties 對象
  • 將 Properties 屬性值設置到對應的 Bean 屬性

代碼調整如下:

@SpringBootApplication
@RestController
@RefreshScope
@EnableConfigurationProperties(User.class)
public class NacosConfigSampleApplication {
    
    ......

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            String dataId = "nacos-config-sample.properties";
            String group = "DEFAULT_GROUP";
            nacosConfigManager.getConfigService().addListener(dataId, group, new AbstractListener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    System.out.println("[Listener] " + configInfo);
                    System.out.println("[Before User] " + user);

                    Properties properties = new Properties();
                    try {
                        properties.load(new StringReader(configInfo));
                        String name = properties.getProperty("user.name");
                        int age = Integer.valueOf(properties.getProperty("user.age"));
                        user.setName(name);
                        user.setAge(age);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println("[After User] " + user);
                }
            });
        };
    }

    ......
        
}

重啟應用,並將配置 user.age 從 90 調整到 19,觀察日誌變化:

[Listener] user.name=nacos-config-sample
user.age= 19
[Before User] User{name='nacos-config-sample', age=90}
[After User] User{name='nacos-config-sample', age=19}

上述三個例子均圍繞著 Nacos Config 實現 Bean 屬性動態更新,不過它們是 Spring Cloud 使用場景。如果讀者的應用僅使用 Spring 或者 Spring Boot,可以考慮 Nacos Spring 工程, Github 地址:https://github.com/nacos-group/nacos-spring-project,其中 @NacosValue支持屬性粒度的更新。

6. Nacos Config 高級配置

6.1 支持自定義 namespace 的配置

首先看一下 Nacos 的 Namespace 的概念, Nacos 概念
用於進行租戶粒度的配置隔離。不同的命名空間下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用場景之一是不同環境的配置的區分隔離,例如開發測試環境和生產環境的資源(如配置、服務)隔離等。

在沒有明確指定 ${spring.cloud.nacos.config.namespace}配置的情況下, 默認使用的是 Nacos 上 Public 這個namespae。如果需要使用自定義的命名空間,可以通過以下配置來實現:

spring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7

注:該配置必須放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace的值是 namespace 對應的 id,id 值可以在 Nacos 的控制檯獲取。並且在添加配置時注意不要選擇其他的 namespae,否則將會導致讀取不到正確的配置

6.2 支持自定義 Group 的配置

在沒有明確指定 ${spring.cloud.nacos.config.group}配置的情況下, 默認使用的是 DEFAULT_GROUP 。如果需要自定義自己的 Group,可以通過以下配置來實現:

spring.cloud.nacos.config.group=DEVELOP_GROUP

注:該配置必須放在 bootstrap.properties 文件中。並且在添加配置時 Group 的值一定要和 spring.cloud.nacos.config.group的配置值一致。

6.3 支持自定義擴展的 Data Id 配置

Spring Cloud Alibaba Nacos Config 從 0.2.1 版本後,可支持自定義 Data Id 的配置。關於這部分詳細的設計可參考 這裡。 一個完整的配置案例如下所示:

spring.application.name=opensource-service-provider
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# config external configuration
# 1、Data Id 在默認的組 DEFAULT_GROUP,不支持配置的動態刷新
spring.cloud.nacos.config.extension-configs[0].data-id=ext-config-common01.properties
# 2、Data Id 不在默認的組,不支持動態刷新
spring.cloud.nacos.config.extension-configs[1].data-id=ext-config-common02.properties
spring.cloud.nacos.config.extension-configs[1].group=GLOBALE_GROUP
# 3、Data Id 既不在默認的組,也支持動態刷新
spring.cloud.nacos.config.extension-configs[2].data-id=ext-config-common03.properties
spring.cloud.nacos.config.extension-configs[2].group=REFRESH_GROUP
spring.cloud.nacos.config.extension-configs[2].refresh=true

可以看到:

  • 通過 spring.cloud.nacos.config.extension-configs[n].data-id的配置方式來支持多個 Data Id 的配置。
  • 通過 spring.cloud.nacos.config.extension-configs[n].group的配置方式自定義 Data Id 所在的組,不明確配置的話,默認是 DEFAULT_GROUP。
  • 通過 spring.cloud.nacos.config.extension-configs[n].refresh的配置方式來控制該 Data Id 在配置變更時,是否支持應用中可動態刷新, 感知到最新的配置值。默認是不支持的。

注:多個 Data Id 同時配置時,他的優先級關係是 spring.cloud.nacos.config.extension-configs[n].data-id其中 n 的值越大,優先級越高。
spring.cloud.nacos.config.extension-configs[n].data-id的值必須帶文件擴展名,文件擴展名既可支持 properties,又可以支持 yaml/yml。 此時 spring.cloud.nacos.config.file-extension的配置對自定義擴展配置的 Data Id 文件擴展名沒有影響。

通過自定義擴展的 Data Id 配置,既可以解決多個應用間配置共享的問題,又可以支持一個應用有多個配置文件。

為了更加清晰的在多個應用間配置共享的 Data Id ,你可以通過以下的方式來配置:

# 配置支持共享的 Data Id
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml
# 配置 Data Id 所在分組,缺省默認 DEFAULT_GROUP
spring.cloud.nacos.config.shared-configs[0].group=GROUP_APP1
# 配置Data Id 在配置變更時,是否動態刷新,缺省默認 false
spring.cloud.nacos.config.shared-configs[0].refresh=true

可以看到:

  • 通過 spring.cloud.nacos.config.shared-configs[n].data-id來支持多個共享 Data Id 的配置。
  • 通過 spring.cloud.nacos.config.shared-configs[n].group來配置自定義 Data Id 所在的組,不明確配置的話,默認是 DEFAULT_GROUP。
  • 通過 spring.cloud.nacos.config.shared-configs[n].refresh來控制該Data Id在配置變更時,是否支持應用中動態刷新,默認false。

6.4 配置的優先級

Spring Cloud Alibaba Nacos Config 目前提供了三種配置能力從 Nacos 拉取相關的配置。

  • A: 通過 spring.cloud.nacos.config.shared-configs[n].data-id支持多個共享 Data Id 的配置
  • B: 通過 spring.cloud.nacos.config.extension-configs[n].data-id的方式支持多個擴展 Data Id 的配置
  • C: 通過內部相關規則(應用名、應用名+ Profile )自動生成相關的 Data Id 配置

當三種方式共同使用時,他們的一個優先級關係是:A < B < C

6.5 完全關閉配置

通過設置 spring.cloud.nacos.config.enabled = false 來完全關閉 Spring Cloud Nacos Config

6.6 更多高級配置

更多關於 Nacos Config Starter 的配置項如下所示:

配置項 Key 默認值 說明
服務端地址 spring.cloud.nacos.config.server-addr Nacos Server 啟動監聽的ip地址和端口
配置對應的 DataId spring.cloud.nacos.config.name 先取 prefix,再取 name,最後取 spring.application.name
配置對應的 DataId spring.cloud.nacos.config.prefix 先取 prefix,再取 name,最後取 spring.application.name
配置內容編碼 spring.cloud.nacos.config.encode 讀取的配置內容對應的編碼
GROUP spring.cloud.nacos.config.group DEFAULT_GROUP 配置對應的組
文件擴展名 spring.cloud.nacos.config.fileExtension properties 配置項對應的文件擴展名,目前支持 properties 和 yaml(yml)
獲取配置超時時間 spring.cloud.nacos.config.timeout 3000 客戶端獲取配置的超時時間(毫秒)
接入點 spring.cloud.nacos.config.endpoint 地域的某個服務的入口域名,通過此域名可以動態地拿到服務端地址
命名空間 spring.cloud.nacos.config.namespace 常用場景之一是不同環境的配置的區分隔離,例如開發測試環境和生產環境的資源(如配置、服務)隔離等
AccessKey spring.cloud.nacos.config.accessKey 當要上阿里雲時,阿里雲上面的一個雲賬號名
SecretKey spring.cloud.nacos.config.secretKey 當要上阿里雲時,阿里雲上面的一個雲賬號密碼
Nacos Server 對應的 context path spring.cloud.nacos.config.contextPath Nacos Server 對外暴露的 context path
集群 spring.cloud.nacos.config.clusterName 配置成Nacos集群名稱
共享配置 spring.cloud.nacos.config.sharedDataids 共享配置的 DataId, "," 分割
共享配置動態刷新 spring.cloud.nacos.config.refreshableDataids 共享配置中需要動態刷新的 DataId, "," 分割
自定義 Data Id 配置 spring.cloud.nacos.config.extConfig 屬性是個集合,內部由 ConfigPOJO 組成。Config有 3 個屬性,分別是 dataId, group以及 refresh

7. Nacos Config Actuator Endpoint

Nacos Config 內部提供了一個 Endpoint, 對應的 Endpoint ID 為 nacos-config,其 Actuator Web Endpoint URI 為 /actuator/nacos-config

注:使用 Nacos Config Spring Cloud 1.x 版本的話,其 URI 地址則為 /nacos-config

其中,Endpoint 暴露的 json 中包含了三種屬性:

  • NacosConfigProperties: 當前應用 Nacos 的基礎配置信息
  • RefreshHistory: 配置刷新的歷史記錄
  • Sources: 當前應用配置的數據信息

由於 Aliyun Java Initializr 所生成的應用工程默認激活 Spring Boot Actuator Endpoints(JMX 和 Web),具體配置存放在 application.properties文件中,同時,Actuator Web 端口設置為 8081,內容如下:

management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# Actuator Web 訪問端口
management.server.port=8081

因此,應用 nacos-config-sample 無需調整,直接訪問:http://127.0.0.1:8081/actuator/nacos-config,服務響應的內容如下:

{
  "NacosConfigProperties": {
    "serverAddr": "127.0.0.1:8848",
    "username": "",
    "password": "",
    "encode": null,
    "group": "DEFAULT_GROUP",
    "prefix": null,
    "fileExtension": "properties",
    "timeout": 3000,
    "maxRetry": null,
    "configLongPollTimeout": null,
    "configRetryTime": null,
    "enableRemoteSyncConfig": false,
    "endpoint": null,
    "namespace": null,
    "accessKey": null,
    "secretKey": null,
    "contextPath": null,
    "clusterName": null,
    "name": null,
    "sharedConfigs": null,
    "extensionConfigs": null,
    "refreshEnabled": true,
    "sharedDataids": null,
    "refreshableDataids": null,
    "extConfig": null,
    "configServiceProperties": {
      "secretKey": "",
      "namespace": "",
      "username": "",
      "enableRemoteSyncConfig": "false",
      "configLongPollTimeout": "",
      "configRetryTime": "",
      "encode": "",
      "serverAddr": "127.0.0.1:8848",
      "maxRetry": "",
      "clusterName": "",
      "password": "",
      "accessKey": "",
      "endpoint": ""
    }
  },
  "RefreshHistory": [
    
  ],
  "Sources": [
    {
      "lastSynced": "2020-09-14 11:11:37",
      "dataId": "nacos-config-sample.properties"
    },
    {
      "lastSynced": "2020-09-14 11:11:37",
      "dataId": "nacos-config-sample"
    }
  ]
}

對文檔有任何問題,請在評論區留言!

Leave a Reply

Your email address will not be published. Required fields are marked *