微軟Azure配置中心 App Configuration (三):配置的動態更新

語言: CN / TW / HK

寫在前面

我在前文:

《微軟Azure配置中心 App Configuration (一):輕鬆整合到Asp.Net Core》已經介紹了Asp.net Core怎麼輕易的接入azure 配置中心App Configuration(下稱azure 配置中心);

《微軟Azure配置中心 App Configuration (二):Feature Flag 功能開關特性》 講Asp.net Core功能開關的兩種方式的簡單使用;

本文重點來講講Azure配置中心是怎麼配置的動態更新的。

概念定義

一般對配置中心來說都有動態更新的概念,我這裡給個定義:

配置中心的動態更新是指,當用戶在配置中心管理後臺更新配置後,整合的客戶端能以某種形式到配置的更新;

一般有兩種模式

  • 1、客戶端輪詢;
  • 2、服務端主動推送更新;包括但不限於Grpc(Nacos),Websocket等方式;

客戶端輪詢模式

本文在前文基礎上開始的,有些略過的地方請看前文;

1、修改整合方式

var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("AppConfig");

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    //配置不同功能
    config.AddAzureAppConfiguration(options =>
    {
        ////啟用Label(多環境)支援
        //options.Connect(connectionString)
        //    .Select(KeyFilter.Any, LabelFilter.Null)//配置過濾器,讀取空Lable的配置
        //    .Select(KeyFilter.Any, hostingContext.HostingEnvironment.EnvironmentName); //配置過濾器,只讀取某個環境的配置

        //啟用Poll模式的主動更新
        options.Connect(connectionString)
            .Select(KeyFilter.Any, LabelFilter.Null)//配置過濾器,讀取空Lable的配置
            .Select(KeyFilter.Any, hostingContext.HostingEnvironment.EnvironmentName) //配置過濾器,只讀取某個環境的配置
            .ConfigureRefresh(refresh =>
            {
                refresh.Register("TestApp:Settings:Sentinel", refreshAll: true).SetCacheExpiration(new TimeSpan(0, 0, 30));
            });
    });
});

這裡方法 ConfigureRefresh 引數:

TestApp:Settings:Sentinel:這就是程式輪詢的配置Key;

refreshAll=true:表示當輪詢的配置Key更新時,更新所有配置;

SetCacheExpiration:設定多久時間輪詢一次,這裡設定了30秒,這也是預設值;

2、注入服務

builder.Services.AddAzureAppConfiguration();

3、驗證

我們現在Azure配置管理後臺設定好key: TestApp:Settings:Sentinel

可以看到初始值==1;

我們新增一個測試的TestKey4==TestKey4-azure

啟動程式後,我們無論怎麼修改配置後臺,都不會程式拿到值始終:TestKey4 ==TestKey4-azure

我們把監控Key:TestApp:Settings:Sentinel設定為2

再次獲取可以看到,TestKey4 的值更新了:

OK,輪詢模式就是這麼樸實無華,卻又足以滿足大部分需求;

服務端主動推送更新

流程簡介

除了輪詢的方式動態更新配置外,Azure配置中心也提供了push的方式主動推送配置更新到客戶端,不過它的實現流程不太像我們經常遇到的Grpc或者Websocket等那樣直連的方式,而是藉助Azure訊息佇列Service Bus實現的。

大體流程:

  1. 先建立一個Service Bus的Topic訂閱(類似於RabbitMQ的Topic);

  2. Azure配置中心註冊一個事件訂閱到Service Bus的Topic訂閱,當配置修改時觸發事件傳送一個配置更新訊息到Service Bus;

  3. 客戶端程式訂閱了Service Bus的Topic,實時接收配置更新訊息並更新本地程式的IConfiguration;

下面我們看看流程怎麼實現;

1、先建立Service Bus的Topic訂閱

建立Service Bus名稱空間

資訊自己填

建立Topic

我建立的topic名:config-topic

建立topic訂閱

訂閱名:config-s1

ok,到這步位置,Service Bus這邊基本配置完;

2、建立配置中心的事件訂閱到topic訂閱

App Configuration下建立事件訂閱

填寫Topic訂閱資訊

注意右側紅框,要選擇你上步建立的Service Bus 的Topic訂閱 config-topic

建立成功

3、Asp.Net Core中整合

安裝包

install-package Microsoft.Azure.ServiceBus

新增配置資訊

//ServiceBus 的配置
 "AzureServiceBusConfig": {
    "ConnectionString": "< ConnectionString >",
    "TopicName": "< Your TopicName >", //我的是config-topic
    "SubscriptionName": "< Your SubscriptionName >" // 我的是 config-s1
  }

修改ConfigureService做整合

var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("AppConfig");
IConfigurationRefresher _refresher = null;

builder.Host.ConfigureAppConfiguration((hostingContext, config) =>
{
    ////簡單使用只配置connection string
    //config.AddAzureAppConfiguration(connectionString);

    //配置不同功能
    config.AddAzureAppConfiguration(options =>
    {
        //啟用Push模式的主動推送更新配置
        options.Connect(connectionString)
            .Select(KeyFilter.Any, LabelFilter.Null)//配置過濾器,讀取空Lable的配置
            .Select(KeyFilter.Any, hostingContext.HostingEnvironment.EnvironmentName) //配置過濾器,只讀取某個環境的配置
            .ConfigureRefresh(refresh =>
            {
                refresh.Register("TestApp:Settings:Sentinel", refreshAll: true)
                       .SetCacheExpiration(TimeSpan.FromDays(10)); //這個重新整理頻率要設定特別低了
            });
        _refresher = options.GetRefresher();

    });
});

TestApp:Settings:Sentinel:只訂閱這個key的重新整理事件;

SetCacheExpiration:這裡的重新整理頻率設定很低就行;

修改IApplicationBuilder整合

先寫個拓展方法

/// <summary>
/// 啟用一個Service bus事件處理程式在配置更新時重新整理 IConfiguration
/// </summary>
/// <param name="app">The application.</param>
/// <param name="refresher">The refresher.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">serviceBusConfig</exception>
public static IApplicationBuilder UseAzureConfigChangeEventHandler(this IApplicationBuilder app, IConfigurationRefresher refresher)
{
    var serviceBusConfig = PassportConfig.Get<AzureServiceBusConfig>(nameof(AzureServiceBusConfig));
    if (serviceBusConfig == null)
    {
        throw new ArgumentNullException(nameof(serviceBusConfig));
    }

    SubscriptionClient serviceBusClient = new SubscriptionClient(serviceBusConfig.ConnectionString, serviceBusConfig.TopicName, serviceBusConfig.SubscriptionName);

    serviceBusClient.RegisterMessageHandler(handler: (message, cancellationToken) =>
        {
            // 構建一個 EventGridEvent
            EventGridEvent eventGridEvent = EventGridEvent.Parse(BinaryData.FromBytes(message.Body));

            // 建立PushNotification
            eventGridEvent.TryCreatePushNotification(out PushNotification pushNotification);

            // 重新整理IConfiguration
            refresher.ProcessPushNotification(pushNotification);
            refresher.TryRefreshAsync();

            return Task.CompletedTask;
        },
        exceptionReceivedHandler: (exceptionargs) =>
        {
            Console.WriteLine($"{exceptionargs.Exception}");
            return Task.CompletedTask;
        }
        );

    return app;
}

然後直接在管道中啟用

app.UseAzureConfigChangeEventHandler(_refresher);

這個函式的功能是,訂閱Service Bus的Topic ,當服務端配置修改時,接收配置更新資訊,重新整理本地配置;

驗證

還是用TestKey4來測試,先執行程式,

本來:TestKey4TestKey4-azure,我們改成:TestKey4TestKey4-azure 2022年8月6日

但怎麼重新整理程式獲取的值都不會更新。

我們打個斷點到函式: UseAzureConfigChangeEventHandler() ,再更新Key TestApp:Settings:Sentinel

看到,收到配置更新訊息命中斷點了:

同時,通過Service Bus的後臺,我們也已看到配置更新事件訊息正確傳送:

我們再次獲取配置看到確已更新:

OK,服務端基於訂閱訊息佇列獲取配置的主動更新方式驗證成功;

總結

1、我覺得動態更新配置用主動輪詢的方式基本能滿足大部分需求(但是每次輪詢消耗次數,請設定好輪詢間隔時間),基於訊息佇列的主動推送方式稍微有點麻煩,看需求選用;

2、當配置中心的Key和本地配置檔案的Key衝突時,以配置中心為準;

3、總體來說Azure配置中心還是挺香的,除了貴(畢竟Azure 土豪雲), 價格

Azure配置中心的基本學習到這裡告一段落,後面挖掘到更實用的功能/技巧將再次水文補充;

原始碼

https://github.com/gebiWangshushu/Hei.Azure.Test

[參考]

https://docs.microsoft.com/en-us/azure/azure-app-configuration/overview