Asp.net core中實現自動更新的Option

    Asp.net core可以監視json、xml等配置文件的變化, 自動刷新內存中的配置內容, 但如果想每隔1秒從zookeeper、consul獲取最新的配置信息, 需要自己實現.

   閱讀了 Asp.net core Document的Custom configuration provider, 得知只需要實現自己的IConfigurationSource和對應ConfigurationProvider即可

   在這個示例中, 我建立了一個簡單的option, 只包含一個不斷變化的計數器變量.

public class RefreshableOptions
{
    public int IncreasementCount { get; set; }
}

    實現IConfigurationSource和對應ConfigurationProvider, 內部有一個timer模擬從外部獲取了最新的數據, 這裏爲簡單起見, 採用硬編碼的方式指定了option的路徑

public class AutoRefreshConfigurationSource : IConfigurationSource
{
    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new AutoRefreshConfigurationProvider();
    }
}

public class AutoRefreshConfigurationProvider : ConfigurationProvider
{
    private int count = 0;
    private bool isChanged;

    public AutoRefreshConfigurationProvider() : base()
    {
        Timer timer = new Timer(TimerCallback);
        timer.Change(1000, 3000);
    }

    public override void Load()
    {
        var beforeData = Data;
        // 這裏採用硬編碼指定option的路徑
        Data = new Dictionary<string, string>() { { "AutoRefreshOptions:IncreasementCount", count.ToString() } };
        isChanged = IsDictionaryChanged(beforeData, Data);
    }

    private void TimerCallback(object state)
    {
        count++;
        this.Load();
        if (isChanged)
        {
            base.OnReload();//通知IConfiguration實例, 有參數發生了改變
            isChanged = false;
        }
    }
    //判斷兩個Idictionary是否有不同的幫助方法
    private static bool IsDictionaryChanged(IDictionary<string, string> before, IDictionary<string, string> after)
    {
        if (before == null && after == null)
        {
            return false;
        }
        if ((before == null) != (after == null))
        {
            return true;
        }
        if (before.Count != after.Count)
        {
            return true;
        }
        var ignoreCaseBefore = new Dictionary<string, string>(before, StringComparer.OrdinalIgnoreCase);
        foreach (var afterItemKey in after.Keys)
        {
            if (!ignoreCaseBefore.TryGetValue(afterItemKey, out var beforeItemValue))
            {
                return true;
            }
            if (beforeItemValue != after[afterItemKey])
            {
                return true;
            }
            ignoreCaseBefore.Remove(afterItemKey);
        }
        if (ignoreCaseBefore.Count > 0)
        {
            return true;
        }
        return false;
    }
}

實現擴展方法

public static class AutoRereshConfigurationExtensions
{
    public static IConfigurationBuilder AddAutoRereshConfiguration(this IConfigurationBuilder builder)
    {
        return builder.Add(new AutoRefreshConfigurationSource());
    }
}

 

 

使用方法

新建一個WebApi項目, 在Program.CreateWebHostBuilder中增加黃色部分

WebHost.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration(config =>
    {
        config.AddAutoRereshConfiguration();
    })
    .UseStartup<Startup>();

在Startup. ConfigureServices中配置

services.Configure<RefreshableOptions>(Configuration.GetSection("AutoRefreshOptions"));

修改ValuesController

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    private RefreshableOptions refreshableOptions;
    public ValuesController(IOptionsSnapshot<RefreshableOptions> refreshableOptions)
    {
        this.refreshableOptions = refreshableOptions.Value;
    }

    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "value1", "value2", refreshableOptions.IncreasementCount.ToString() };
    }
}

 啓動後不停的刷新http://localhost:5000/api/values可以看到返回內容的變化

 

本文代碼

 

後記: 這個功能寫得比較早, 後來在nuget上發現很多consul configure的擴展, 這段代碼就全當練習吧.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章