天天看點

淺析 .Net Core中Json配置的自動更新

很早在看 Jesse 的Asp.net Core快速入門的課程的時候就了解到了在Asp .net core中,如果添加的Json配置被更改了,是支援自動重載配置的,作為一名有着嚴重"造輪子"情節的程式員,最近在折騰一個部落格系統,也想造出一個這樣能自動更新以Mysql為資料源的ConfigureSource,于是點開了AddJsonFile這個拓展函數的源碼,發現别有洞天,蠻有意思,本篇文章就簡單地聊一聊Json config的ReloadOnChange是如何實作的,在學習ReloadOnChange的過程中,我們會把Configuration也順帶撩一把😁,希望對小夥伴們有所幫助.

在Asp .net core中如果配置了json資料源,把reloadOnChange屬性設定為true即可實作當檔案變更時自動更新配置,這篇部落格我們首先從它的源碼簡單看一下,看完你可能還是會有點懵的,别慌,我會對這些代碼進行精簡,做個簡單的小例子,希望能對你有所幫助.

首先,我們當然是從這個我們耳熟能詳的擴充函數開始,它經曆的演變過程如下.

傳遞一個null的FileProvider給另外一個重載Addjson函數.

敲黑闆,Null的FileProvider很重要,後面要考😄.

把傳入的參數演變成一個Action委托給<code>JsonConfigurationSource</code>的屬性指派.

最終調用的builder.add(action)方法.

在Add方法裡,建立了一個Source執行個體,也就是JsonConfigurationSource執行個體,然後把這個執行個體傳為剛剛的委托,這樣一來,我們在最外面傳入的<code>"appsettings.json",optional:true,reloadOnChange:true</code>參數就作用到這個示例上了.

最終,這個執行個體添加到builder中.那麼builder又是什麼?它能幹什麼?

前面提及的builder預設情況下是<code>ConfigurationBuilder</code>,我對它的進行了簡化,關鍵代碼如下.

可以看到,這個builder中有個集合類型的Sources,這個Sources可以儲存任何實作了<code>IConfigurationSource</code>的Source,前面聊到的<code>JsonConfigurationSource</code>就是實作了這個接口,常用的還有<code>MemoryConfigurationSource</code>,<code>XmlConfigureSource</code>,<code>CommandLineConfigurationSource</code>等.

另外,它有一個很重要的build方法,這個build方法在<code>WebHostBuilder</code>方法執行<code>build</code>的時候也被調用,不要問我<code>WebHostBuilder.builder</code>方法什麼執行的😂.

在ConfigureBuilder的方法裡面就調用了每個Source的Builder方法,我們剛剛傳入的是一個<code>JsonConfigurationSource</code>,是以我們有必要看看JsonSource的builder做了什麼.

這裡是不是被這些builder繞哭了? 别慌,下一篇文章中我會講解如何自定義一個ConfigureSoure,會把Congigure系列類UML類圖整理一下,應該會清晰很多.

這就是<code>JsonConfigurationSource</code>的所有代碼,未精簡,它隻實作了一個Build方法,在Build内,EnsureDefaults被調用,可别小看它,之前那個空的FileProvider在這裡被指派了.

可以看到這個FileProvider預設情況下就是<code>PhysicalFileProvider</code>,為什麼對這個<code>FileProvider</code>如此寵幸讓我花如此大的伏筆要強調它呢?往下看.

在JsonConfigurationSource的build方法内,傳回的是一個JsonConfigurationProvider執行個體,是以直覺告訴我,在它的構造函數内必有存在某種問題或陰謀😕.

看不出什麼的代碼,事出反常必有妖~~

看看base的構造函數.

真是個天才,問題就在這個構造函數裡,它構造函數調用了一個<code>ChangeToken.OnChange</code>方法,這是實作ReloadOnChange的關鍵,如果你點到這裡還沒有關掉,恭喜,好戲開始了.

Talk is cheap. Show me the code (屁話少說,放<code>碼</code>過來).

OnChange方法裡,先不管什麼func,action,就看看這兩個參數的名稱,producer,consumer,生産者,消費者,不知道看到這個關鍵詞想到的是什麼,反正我想到的是國小時學習食物鍊時的🐍與🐀.

那麼我們來看看這裡的🐍是什麼,🐀又是什麼,還得回到<code>FileConfigurationProvider</code>的構造函數.

可以看到生産者🐀是:

消費者🐍是:

我們想一下,一旦有一條🐀跑出來,就立馬被🐍吃了,

那我們這裡也一樣,一旦有FileProvider.Watch傳回了什麼東西,就會發生Load()事件來重新加載資料.

🐍與🐀好了解,可是代碼就沒那麼好了解了,我們通過<code>OnChange</code>的第一個參數<code>Func&lt;IChangeToken&gt; changeTokenProducer</code>方法知道,這裡的🐀,其實是<code>IChangeToken</code>.

IChangeToken的重點在于裡面有個RegisterChangeCallback方法,🐍吃🐀的這件事,就發生在這回調方法裡面.

我們來做個🐍吃🐀的實驗.

這是運作結果

淺析 .Net Core中Json配置的自動更新

可以看到,一旦在監聽的目錄下建立檔案,立即觸發了執行回調函數,但是如果我們繼續手動地更改(複制)監聽目錄中的檔案,回調函數就不再執行了.

這是因為changeToken監聽到檔案變更并觸發回調函數後,這個changeToken的使命也就完成了,要想保持一直監聽,那麼我們就在在回調函數中重新擷取token,并給新的token的回調函數注冊通用的事件,這樣就能保持一直監聽下去了.

這也就是ChangeToken.Onchange所作的事情,我們看一下源碼.

簡單來說,就是給token注冊了一個<code>OnChangeTokenFired</code>的回調函數,仔細看看<code>OnChangeTokenFired</code>裡做了什麼,總體來說三步.

擷取一個新的token.

調用消費者進行消費.

給新擷取的token再次注冊一個<code>OnChangeTokenFired</code>的回調函數.

如此周而複始~~

既然知道了OnChange的工作方式,那麼我們把實驗1的代碼修改一下.

執行效果看一下

淺析 .Net Core中Json配置的自動更新

可以看到,隻要被監控的目錄發生了檔案變化,不管是建立檔案,還是修改了檔案内的内容,都會觸發回調函數,其實JsonConfig中,這個回調函數就是Load(),它負責重新加載資料,可也就是為什麼Asp .net core中如果把ReloadOnchang設定為true後,Json的配置一旦更新,配置就會自動重載.

那麼,為什麼檔案一旦變化,就會觸發ChangeToken的回調函數呢? 其實<code>PhysicalFileProvider</code>中調用了<code>PhysicalFilesWatcher</code>對檔案系統進行監視,觀察PhysicalFilesWatcher的構造函數,可以看到<code>PhysicalFilesWatcher</code>需要傳入<code>FileSystemWatcher</code>,<code>FileSystemWatcher</code>是<code>system.io</code>下的底層IO類,在構造函數中給這個Watcher的Created,Changed,Renamed,Deleted注冊EventHandler事件,最終,在這些EventHandler中會調用ChangToken的回調函數,是以檔案系統一旦發生變更就會觸發回調函數.

蔣金楠老師有一篇優秀的文章介紹<code>FileProvider</code>,有興趣的可以看一下

https://www.cnblogs.com/artech/p/net-core-file-provider-02.html.

如果你和我一樣,對源碼感興趣,可以從官方的<code>aspnet/Extensions</code>中下載下傳源碼研究:https://github.com/aspnet/Extensions

在下一篇文章中,我會講解如何自定義一個以Mysql為資料源的ConfigureSoure,并實作自動更新功能,同時還會整理Configure相關類的UML類圖,有興趣的可以關注我以便第一時間收到下篇文章.

本文章涉及的代碼位址:https://github.com/liuzhenyulive/MiniConfiguration

繼續閱讀