ASP.NET Core的配置(5):配置的同步[設計篇]

来源:http://www.cnblogs.com/liuhui1/archive/2017/04/28/6782156.html
-Advertisement-
Play Games

本節所謂的“配置同步”主要體現在兩個方面:其一,如何監控配置源併在其變化的時候自動載入其數據,其目的是讓應用中通過Configuration對象承載的配置與配置源的數據同步;其二、當Configuration對象承載的配置放生變換的時候如何嚮應用程式發送通知,最終讓應用程式使用最新的配置。 一、配置 ...


本節所謂的“配置同步”主要體現在兩個方面:其一,如何監控配置源併在其變化的時候自動載入其數據,其目的是讓應用中通過Configuration對象承載的配置與配置源的數據同步;其二、當Configuration對象承載的配置放生變換的時候如何嚮應用程式發送通知,最終讓應用程式使用最新的配置。

一、配置與配置源的同步

配置模型提供了三個原生ConfigurationProvider(JsonConfigrationProvider、XmlConfigurationProvider和IniConfigurationProvider)使我們可以將三種格式(JSON、XML和INI)的文件作為配置原始數據的來源,所以針對物理文件的配置同步是配置同步機制的一個主要的應用領域。在上面演示的實例中,基於物理文件的同步是通過調用ConfigurationRoot的擴展方法ReloadOnChanged來實現的。

這個擴展方法定義在NuGet包“Microsoft.Extensions.Configuration.FileProviderExtensions”之中,除了在我們演示的實例中使用的那個方法之外,這個ReloadOnChanged方法還具有如下兩個額外的重載。對於這三個ReloadOnChanged方法重載來說,最終的實現均落在第三個重載上。至於最本質的物理文件監控的功能則由一個名為FileProvider的對象負責。

   1: public static class FileProviderExtensions
   2: {
   3:     public static IConfigurationRoot ReloadOnChanged(
   4:         this IConfigurationRoot config, string filename);
   5:  
   6:     public static IConfigurationRoot ReloadOnChanged(
   7:         this IConfigurationRoot config, string basePath, string filename);
   8:  
   9:     public static IConfigurationRoot ReloadOnChanged(this IConfigurationRoot config, 
  10:         IFileProvider fileProvider, string filename);
  11: }

這裡所謂的FileProvider是對所有實現了IFileProvider介面的類型及其對象的統稱。IFileProvier介面定義在命名空間“Microsoft.AspNet.FileProviders”下,它通過定義其中的方法提供抽象化的目錄與文件信息,針對文件監控相關的方法也定義在這個介面下。如下麵的代碼片段所示,IFileProvier具有三個方法,其中GetDirectoryContents和GetFileInfo用於提供目錄和文件的相關信息,我們只需要關註旨在監控文件變化的Watch方法。

   1: public interface IFileProvider
   2: {
   3:     IDirectoryContents GetDirectoryContents(string subpath);
   4:     IFileInfo GetFileInfo(string subpath);
   5:     IChangeToken Watch(string filter);
   6: }

一個FileProvider總是針對一個具體的目錄,Watch方法的參數filter旨在幫助篩選出需要監控的文件。這個參數是一個可以攜帶通配符(“*”)的字元串,比如 “ *.*”則表示所有文件,而“ *.json”則表示所有擴展名為“ .json”的文件。如果我們需要監控當前目錄下某個確定的文件,直接將文件名作為參數即可。Watch方法的返回類型為具有如下定義的IChangeToken介面,我們可以將它理解為一個用於傳遞數據變換通知的令牌。

   1: public interface IChangeToken
   2: {
   3:     bool HasChanged { get; }
   4:     bool ActiveChangeCallbacks { get; }
   5:     
   6:     IDisposable RegisterChangeCallback(Action<object> callback, object state);    
   7: }

IChangeToken的只讀屬性HasChanged表示目標數據是否發生改變。我們可以通過調用它的RegisterChangeCallback方法註冊一個在數據發生變化時需要執行的回調操作。該方法返回的對象對應的類型必須實現IDisposable介面,回調註冊的接觸可以通過Dispose方法來完成。至於IChangeToken介面的另個只讀屬性ActiveChangeCallbacks表示當數據發生變化時是否需要主動執行註冊的回調操作。實際上IConfiguration的GetReloadToke方法的返回類型就是這麼一個介面,至於該方法具體返回一個怎樣的對象,我們會在下一節予以介紹。

當我們指定一個具體的FileProvider對象調用ConfigurationRoot的擴展方法ReloadOnChanged時,後者會調用這個FileProvider的RegisterChangeCallback方法以註冊一個在指定文件發生變化時的回調。至於這個註冊的回調,它會調用ConfigurationRoot的Reload方法實現對配置數據的重新載入。由於註冊了這樣一個回調,該方法只需要調用FileProvider的Watch方法監控指定文件的變化即可,如下所示的代碼片段基本上體現了ReloadOnChanged方法的邏輯。

   1: public static IConfigurationRoot ReloadOnChanged(
   2:     this IConfigurationRoot config, IFileProvider fileProvider, string filename)
   3: {
   4:     Action<object> callback = null;
   5:     callback = _ =>
   6:     {
   7:         config.Reload();
   8:         fileProvider.Watch(filename).RegisterChangeCallback(callback, null);
   9:     };
  10:     fileProvider.Watch(filename).RegisterChangeCallback(callback, null);
  11:     return config;
  12: }

如果我們通過指定目錄和文件名調用另一個ReloadOnChanged方法重載,後者會根據指定的目錄創建一個PhysicalFileProvider對象並作為參數調用上面這個重載。顧名思義,PhysicalFileProvider是一個針對具體物理文件的FileProvider,它實際上是藉助一個FileSystemWatcher對象來監控指定的文件。這個ReloadOnChanged方法的實現邏輯體現在如下所示的代碼片段中。當我們僅僅指定監控文件名調用第一個ReloadOnChanged方法重載時,該方法會將當前應用所在的目錄作為參數調用上面一個重載。

   1: public static class FileProviderExtensions
   2: {
   3:    public static IConfigurationRoot ReloadOnChanged(
   4:        this IConfigurationRoot config, string basePath, string filename) 
   5:        => config.ReloadOnChanged(new PhysicalFileProvider(basePath), filename);
   6:     //其他成員
   7: }


二、應用重新載入的配置

ConfigurationRoot通過擴展方法ReloadOnChanged方法與一個具體的物理文件綁定在一起,針對該文件的任何修改操作都會促使Reload方法的調用,進而保證自身承載的數據總是與配置源保持同步。現在我們來討論配置同步的另一個話題,即如何在不重啟應用程式的情況下使用新的配置。要瞭解這個問題的解決方案,我們得先來聊聊定義在IConfiguration介面中這個一直刻意迴避的方法GetReloadToken。

   1: public interface IConfiguration
   2: {
   3:     //其他成員
   4:     IChangeToken GetReloadToken();
   5: }

如上面的代碼片段所示,這個GetReloadToken方法的返回類型為上面討論過的IChangeToken介面,我們說可以將後者視為一個傳遞數據變化信息的令牌。對於一個Configuration對象來說,它所謂的數據變換體現作為配置根節點的ConfigurationRoot對象的重新載入,所以這個方法返回的ChangeToken對象體現了最近一次載入引起的配置變化。

   1: public class ConfigurationReloadToken : IChangeToken
   2: {
   3:     public void OnReload();
   4:     public IDisposable RegisterChangeCallback(Action<object> callback, 
   5:         object state);
   6:   
   7:     public bool ActiveChangeCallbacks { get; }
   8:     public bool HasChanged { get; }
   9: }

對於實現了IConfiguration介面的兩個預設類型(ConfigurationRoot和ConfigurationSection)來說,它們的GetReloadToken方法返回的是一個ConfigurationReloadToken對象。如上面的代碼片段所示,除了實現定義在IConfiguration介面中的所有成員之外,ConfigurationReloadToken還具有另一個名為OnReload的方法。當配置數據發生變化,也就是調用通過ConfigurationRoot的Reload方法重新載入配置的時候,這個方法會被調用用以發送“配置已經發生變化”的信號。

實現在ConfigurationReloadToken之中用於傳遞配置變化的邏輯其實很簡單,具體的邏輯是藉助於一個CancellationTokenSource對象來完成。如果讀者朋友們瞭解針對Task的非同步編程,相信對這個類型不會感到陌生。總的來說,我們可以利用CancellationTokenSource向某個非同步執行的Task發送“取消任務”的信號。

   1: public class ConfigurationReloadToken : IChangeToken
   2: {
   3:     private CancellationTokenSource tokenSource = new CancellationTokenSource();
   4:  
   5:     public void OnReload() => tokenSource.Cancel();
   6:     public IDisposable RegisterChangeCallback(Action<object> callback, object state) 
   7:         => tokenSource.Token.Register(callback, state);
   8:  
   9:     public bool ActiveChangeCallbacks { get; } = true;
  10:     public bool HasChanged
  11:     {
  12:         get { return tokenSource.IsCancellationRequested; }
  13:     }
  14: }

如上面的代碼片段所示,ConfigurationReloadToken本質上就是一個CancellationTokenSource對象的封裝。當OnReload方法被調用的時候,它直接調用CancellationTokenSource的Cancel方法發送取消任務的請求,而HasChanged屬性則通過CancellationTokenSource的IsCancellationRequested屬性通過判斷任務取消請求是否發出來判斷配置數據是否發生變化。通過RegisterChangeCallback註冊的回調最終註冊到由CancellationTokenSource創建的CancellationToken對象上,所以一旦OnReload方法被調用,註冊的回調會自動執行。ConfigurationReloadToken的ActiveChangeCallbacks屬性總是返回True。

ConfigurationRoot和ConfigurationSection這兩個類型分別採用如下的形式實現了GetReloadToken方法。我們從給出的代碼片段不難看出所有的ConfigurationSection對象和作為它們根的ConfigurationRoot對象來說,它們的GetReloadToken方法在同一時刻返回的是同一個ConfigurationReloadToken對象。當ConfigurationRoot的Reload方法被調用的時候,當前ConfigurationReloadToken對象的OnReload方法會被調用,在此之後一個新的ConfigurationReloadToken對象會被創建出來並代替原來的對象。

   1: public class ConfigurationRoot : IConfigurationRoot
   2: {
   3:     private ConfigurationReloadToken reloadToken = new ConfigurationReloadToken();
   4:  
   5:     public IChangeToken GetReloadToken()
   6:     {
   7:         return reloadToken;
   8:     }
   9:  
  10:     public void Reload()
  11:     {
  12:         //省略重新載入配置代碼
  13:         Interlocked.Exchange<ConfigurationReloadToken>(ref this._reloadToken, 
  14:             new ConfigurationReloadToken()).OnReload();
  15:     }
  16:     //其他成員
  17: }
  18:  
  19: public class ConfigurationSection : IConfigurationSection, IConfiguration
  20: {
  21:     private readonly ConfigurationRoot root;
  22:     public IChangeToken GetReloadToken()
  23:     {
  24:         return root.GetReloadToken();
  25:     }
  26:     //其他成員
  27: }

正是因為GetReloadToken方法並不能保證每次返回的都是同一個ConfigurationReloadToken對象,所以當我們註冊配置載入回調時,需要在回調中完成針對新的ConfigurationReloadToken對象的回調註冊,實際上我們上面演示的實例就是這麼做的。除此之外,調用RegisterChangeCallback方法會返回一個類型實現了IDisposable 介面的對象,不要忘記調用它的Dispose方法以免產生一些記憶體泄漏的問題。

   1: public class Program
   2: {
   3:     private static IDisposable callbackRegistration;
   4:     private static void OnSettingChanged(object state)
   5:     {
   6:         callbackRegistration?.Dispose();
   7:         IConfiguration configuration = (IConfiguration)state;
   8:         Console.WriteLine(configuration.Get<ThreadPoolSettings>());
   9:         callbackRegistration = configuration.GetReloadToken()
  10:             .RegisterChangeCallback(OnSettingChanged, state);
  11:     }
  12: }

 

ASP.NET Core的配置(1):讀取配置信息
ASP.NET Core的配置(2):配置模型詳解
ASP.NET Core的配置(3): 將配置綁定為對象[上篇]
ASP.NET Core的配置(3): 將配置綁定為對象[下篇]
ASP.NET Core的配置(4):多樣性的配置源[上篇]
ASP.NET Core的配置(4):多樣性的配置源[中篇]
ASP.NET Core的配置(4):多樣性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]

參考頁面:http://qingqingquege.cnblogs.com/p/5933752.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近做一個指紋採集和比對的功能,因為公司整個項目是WEB類型的,所以指紋採集的模塊要嵌套在網頁中,那隻有用ActiveX了,以下是一些操作及效果,做個筆記! 新建用戶控制項,編寫CS代碼,如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...
  • Visual Studio 我們在Windows平臺上開發應用程式使用的工具主要是Visual Studio.這個集成開發環境已經演化了很多年,從一個簡單的C++編輯器和編譯器到一個高度集成、支持軟體開發整個生命周期的多語言環境。 Visual Studio以及它發佈的工具和服務提供了:設計、開發、 ...
  • 在開發Windows服務程式時,我們一般需要添加安裝程式,即:serviceInstaller,裡面有幾個關於名稱屬性,你都搞明白了嗎? 1.Description:表示服務說明(描述服務是乾什麼的); 2.DisplayName:表示友好名稱,可以理解為服務名的別名; 3.ServiceName: ...
  • 從編程的角度來講,ASP.NET Web API針對CORS的實現僅僅涉及到HttpConfiguration的擴展方法EnableCors和EnableCorsAttribute特性。但是整個CORS體系不限於此,在它們背後隱藏著一系列的類型,我們將會利用本章餘下的內容對此作全面講述,今天我們就來 ...
  • 為什麼要使用StringBuilder 為什麼要使用StringBuilder 為什麼要使用StringBuilder 為什麼使用StringBuilder要從string對象的特性說起。 為什麼使用StringBuilder要從string對象的特性說起。 string對象在進行字元串拼接時,因為 ...
  • 簡介 簡介 對於.net來說,用web api來構建服務是一個不錯的選擇,都是http請求,調用簡單,但是如果真的要在程式中調用,則還有些工作要做,比如我們需要手寫httpClient調用,並映射Model, 如果服務少還可以,多了就繁瑣了。 Swagger Swagger 關於Swagger的信息 ...
  • 我們在上面對ASP.NET Core預設提供的具有跨平臺能力的KestrelServer進行了詳細介紹(《聊聊ASP.NET Core預設提供的這個跨平臺的伺服器——KestrelServer》),為了讓讀者朋友們對管道中的Server具有更加深刻的認識,接下來我們採用實例演示的形式創建一個自定義的 ...
  • 概述: ASP.NET Web API 的好用使用過的都知道,沒有複雜的配置文件,一個簡單的ApiController加上需要的Action就能工作。但是在使用API的時候總會遇到跨域請求的問題, 特別各種APP萬花齊放的今天,對API使用者身份角色驗證是不能避免的(完全開發的API不需要對使用者身 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...