移動(dòng)配置信息從應(yīng)用部署包到一個(gè)集中位置。這個(gè)模式可以提供機(jī)會,以便管理和配置數(shù)據(jù)的控制,以及用于跨應(yīng)用程序和應(yīng)用程序?qū)嵗蚕淼呐渲脭?shù)據(jù)。
大多數(shù)應(yīng)用程序運(yùn)行時(shí)環(huán)境包括位于應(yīng)用程序文件夾內(nèi)的在部署應(yīng)用程序文件保持配置信息。在某些情況下也能夠編輯這些文件來改變該應(yīng)用程序的行為,它已經(jīng)被部署之后。然而,在許多情況下,改變配置所需要的應(yīng)用程序被重新部署,從而導(dǎo)致不可接受的停機(jī)時(shí)間和額外的管理開銷。
本地配置文件還配置限制為單個(gè)應(yīng)用程序,而在某些情況下將是有用的,以在多個(gè)應(yīng)用程序之間共享的配置設(shè)置。例子包括數(shù)據(jù)庫連接字符串,UI 主題的信息,或隊(duì)列和存儲所使用的一組相關(guān)的應(yīng)用程序的URL。
變更管理跨應(yīng)用程序的多個(gè)運(yùn)行實(shí)例的本地配置,尤其是在云托管的情況,也可能是具有挑戰(zhàn)性的。它可能會導(dǎo)致使用不同的配置設(shè)置的實(shí)例,而更新正被部署。
另外,更新應(yīng)用程序和組件可能需要更改的配置方案。許多配置系統(tǒng)不支持不同版本的配置信息。
存儲在外部存儲器中的配置信息,并提供可用于快速和有效地讀取和更新的配置設(shè)置的接口。外部存儲的類型取決于應(yīng)用程序的主機(jī)和運(yùn)行時(shí)環(huán)境。在一個(gè)云托管的情況下它是一個(gè)典型的基于云的存儲服務(wù),但可能是一個(gè)托管數(shù)據(jù)庫或其他系統(tǒng)。
選擇用于配置信息的備份存儲應(yīng)通過適當(dāng)?shù)慕涌?,它提供了一個(gè)可控制的方式,使回用保持一致和易于使用的訪問被朝向。理想情況下,它應(yīng)該公開在鍵入正確,結(jié)構(gòu)化的格式的信息。的實(shí)施也可能需要對用戶進(jìn)行授權(quán)“,以保護(hù)結(jié)構(gòu)的數(shù)據(jù)訪問,并且具有足夠的靈活性,以允許要被存儲的多個(gè)版本的配置(例如,開發(fā),分段,或生產(chǎn),并且每一個(gè)的多個(gè)發(fā)行版本)。
注意: 許多內(nèi)置的系統(tǒng)配置中讀取數(shù)據(jù)時(shí),應(yīng)用程序啟動(dòng)和高速緩存內(nèi)存中的數(shù)據(jù)提供快速訪問,并盡量減少對應(yīng)用程序性能的影響。根據(jù)所使用的后備存儲器的類型,以及該商店的等待時(shí)間,這可能是有利的,以實(shí)現(xiàn)外部配置存儲器內(nèi)的高速緩存機(jī)制。有關(guān)實(shí)現(xiàn)緩存的詳細(xì)信息,請參閱緩存指導(dǎo)。
圖1示出了本模式的概述。
圖1 - 外部配置存儲模式可選本地緩存概述
在決定如何實(shí)現(xiàn)這個(gè)模式時(shí),請考慮以下幾點(diǎn):
這種模式非常適合于:
在微軟 Azure 托管應(yīng)用,用于從外部存儲配置信息的典型的選擇是使用 Azure 存儲。這是有彈性的,提供高性能,并重復(fù) 3 次自動(dòng)故障切換提供高可用性。 Azure 的表格提供了一個(gè)鍵/值存儲與使用一個(gè)靈活的架構(gòu)的價(jià)值的能力。 Azure 的 Blob 存儲提供了一個(gè)分層的基于容器的存儲,可以保存任何類型的單獨(dú)命名的 blob 數(shù)據(jù)。
下面的示例顯示了如何配置存儲可以通過 Azure 的 Blob 存儲來實(shí)現(xiàn)存儲和揭露的配置信息。該BlobSettingsStore 類文摘 Blob 存儲用于保存配置信息,并實(shí)現(xiàn)在下面的代碼所示 ISettingsStore 接口。
注意: 此代碼在 ExternalConfigurationStore 解決方案 ExternalConfigurationStore.Cloud 項(xiàng)目提供。該解決方案可用于下載本指導(dǎo)意見。
public interface IsettingsStore
{
string Version { get; }
?
Dictionary<string, string> FindAll();
?
void Update(string key, string value);
}
該接口定義的方法,用于檢索和更新在配置存儲中保持的配置設(shè)置,并且包括可用于檢測是否有任何配置設(shè)置最近已修改的版本號。何時(shí)配置設(shè)置被更新時(shí),版本號的變化。該 BlobSettingsStore 類使用 BLOB 的 ETag 的屬性來實(shí)現(xiàn)的版本。一個(gè) blob 的 ETag 的屬性將 BLOB 寫入每一次自動(dòng)更新。
注意
需要注意的是,按照設(shè)計(jì),這個(gè)簡單的解決方案,展現(xiàn)了所有的配置設(shè)置為字符串值,而不是類型的值。 該 ExternalConfigurationManager 類提供了圍繞 BlobSettingsStore 物體的包裝。應(yīng)用程序可以使用這個(gè)類來存儲和檢索配置信息。這個(gè)類使用 Microsoft 無擴(kuò)展庫來揭露過的 IObservable 接口的實(shí)現(xiàn)做出任何配置更改。如果設(shè)置是通過調(diào)用SetAppSetting法修改,更改的事件引發(fā),所有訂閱者此事件將被通報(bào)。 請注意,所有的設(shè)置也緩存到 ExternalConfigurationManager 類快速訪問內(nèi)部 Dictionary 對象。該 SetAppSetting 方法更新該高速緩存中,并且該應(yīng)用程序可以使用以檢索配置設(shè)置的 GetSetting 方法從高速緩存中讀取數(shù)據(jù)(如果未在該高速緩存中找到該設(shè)置,它從 BlobSettingsStore 對象檢索代替)。
所述的 getSettings 方法調(diào)用 CheckForConfigurationChanges 的方法來檢測在 Blob 存儲的配置信息是否通過檢查版本號,并將它與所述 ExternalConfigurationManager 對象保持當(dāng)前的版本號進(jìn)行比較已經(jīng)改變。如果一個(gè)或多個(gè)已經(jīng)發(fā)生了變化,改變的事件引發(fā),并緩存在 Dictionary 對象的配置設(shè)置被刷新。這是緩存除了圖案的應(yīng)用。
下面的代碼示例演示如何更改的情況下,SetAppSettings 方法,該方法的 getSettings 和 CheckForConfigurationChanges 方法實(shí)現(xiàn)
public class ExternalConfigurationManager : IDisposable
{
// An abstraction of the configuration store.
private readonly ISettingsStore settings;
private readonly ISubject<KeyValuePair<string, string>> changed;
...
private Dictionary<string, string> settingsCache;
private string currentVersion;
...
public ExternalConfigurationManager(ISettingsStore settings, ...)
{
this.settings = settings;
...
}
...
public IObservable<KeyValuePair<string, string>> Changed
{
get { return this.changed.AsObservable(); }
}
...
public void SetAppSetting(string key, string value)
{
...
// Update the setting in the store.
this.settings.Update(key, value);
?
// Publish the event.
this.Changed.OnNext(
new KeyValuePair<string, string>(key, value));
?
// Refresh the settings cache.
this.CheckForConfigurationChanges();
}
?
public string GetAppSetting(string key)
{
...
// Try to get the value from the settings cache.
// If there is a miss, get the setting from the settings store.
string value;
if (this.settingsCache.TryGetValue(key, out value))
{
return value;
}
?
// Check for changes and refresh the cache.
this.CheckForConfigurationChanges();
?
return this.settingsCache[key];
}
...
private void CheckForConfigurationChanges()
{
try
{
?
// Assume that updates are infrequent. Lock to avoid
// race conditions when refreshing the cache.
lock (this.settingsSyncObject)
{ {
var latestVersion = this.settings.Version;
?
// If the versions differ, the configuration has changed.
if (this.currentVersion != latestVersion)
{
// Get the latest settings from the settings store and publish the changes.
var latestSettings = this.settings.FindAll();
latestSettings.Except(this.settingsCache).ToList().ForEach(
kv => this.changed.OnNext(kv));
?
// Update the current version.
this.currentVersion = latestVersion;
?
// Refresh settings cache.
this.settingsCache = latestSettings;
}
}
}
catch (Exception ex)
{
this.changed.OnError(ex);
}
}
}
注意
該 ExternalConfigurationManager 類還提供了一個(gè)名為 Environment 屬性。此屬性的目的是為了支持不同的配置為在不同的環(huán)境中,如臨時(shí)和生產(chǎn)運(yùn)行的應(yīng)用程序。
一個(gè) ExternalConfigurationManager 對象也可以定期查詢 BlobSettingsStore 對象的任何變化(通過使用定時(shí)器)。該 StartMonitor 和 StopMonitor 方法如下圖所示的啟動(dòng)代碼示例和停止計(jì)時(shí)器。該 OnTimerElapsed 方法當(dāng)定時(shí)器到期時(shí),并調(diào)用 CheckForConfigurationChanges 方法來檢測的任何變化,并提高了變更的情況下,如前面所描述運(yùn)行。
public class ExternalConfigurationManager : IDisposable
{
...
private readonly ISubject<KeyValuePair<string, string>> changed;
private readonly Timer timer;
private ISettingsStore settings;
...
public ExternalConfigurationManager(ISettingsStore settings,
TimeSpan interval, ...)
{
...
?
// Set up the timer.
this.timer = new Timer(interval.TotalMilliseconds)
{
AutoReset = false;
};
this.timer.Elapsed += this.OnTimerElapsed;
?
this.changed = new Subject<KeyValuePair<string, string>>();
...
}
?
...
?
public void StartMonitor()
{
if (this.timer.Enabled)
{
return;
}
?
lock (this.timerSyncObject)
{
if (this.timer.Enabled)
{
return;
}
this.keepMonitoring = true;
?
// Load the local settings cache.
this.CheckForConfigurationChanges();
?
this.timer.Start();
}
}
?
public void StopMonitor()
{
lock (this.timerSyncObject)
{
this.keepMonitoring = false;
this.timer.Stop();
}
}
?
private void OnTimerElapsed(object sender, EventArgs e)
{
Trace.TraceInformation(
"Configuration Manager: checking for configuration changes.");
?
try
{
this.CheckForConfigurationChanges();
}
finally
{
...
// Restart the timer after each interval.
this.timer.Start();
...
}
}
...
}
該 ExternalConfigurationManager 類被實(shí)例化作為由 ExternalConfiguration 類如下所示的單一實(shí)例。
public static class ExternalConfiguration
{
private static readonly Lazy<ExternalConfigurationManager> configuredInstance
= new Lazy<ExternalConfigurationManager>(
() =>
{
var environment = CloudConfigurationManager.GetSetting("environment");
return new ExternalConfigurationManager(environment);
});
?
public static ExternalConfigurationManager Instance
{
get { return configuredInstance.Value; }
}
}
下面的代碼取自 WorkerRole 類中 ExternalConfigurationStore.Cloud 項(xiàng)目。它顯示了如何在應(yīng)用程序使用 ExternalConfiguration 類讀取和更新設(shè)置。
public override void Run()
{
// Start monitoring for configuration changes.
ExternalConfiguration.Instance.StartMonitor();
?
// Get a setting.
var setting = ExternalConfiguration.Instance.GetAppSetting("setting1");
Trace.TraceInformation("Worker Role: Get setting1, value: " + setting);
?
Thread.Sleep(TimeSpan.FromSeconds(10));
?
// Update a setting.
Trace.TraceInformation("Worker Role: Updating configuration");
ExternalConfiguration.Instance.SetAppSetting("setting1", "new value");
?
this.completeEvent.WaitOne();
}
下面的代碼,也是從 WorkerRole 類,展示了如何應(yīng)用訂閱配置事件。
public override bool OnStart()
{
...
// Subscribe to the event.
ExternalConfiguration.Instance.Changed.Subscribe(
m => Trace.TraceInformation("Configuration has changed. Key:{0} Value:{1}",
m.Key, m.Value),
ex => Trace.TraceError("Error detected: " + ex.Message));
...
}
更多建議: