雲端計算設計模式(八)——外部配置儲存模式
雲端計算設計模式(八)——外部配置儲存模式
移動配置資訊從應用部署包到一個集中位置。這個模式可以提供機會,以便管理和配置資料的控制,以及用於跨應用程式和應用程式例項共享的配置資料。
背景和問題
大多數應用程式執行時環境包括位於應用程式資料夾內的在部署應用程式檔案保持配置資訊。在某些情況下也能夠編輯這些檔案來改變該應用程式的行為,它已經被部署之後。然而,在許多情況下,改變配置所需要的應用程式被重新部署,從而導致不可接受的停機時間和額外的管理開銷。
本地配置檔案還配置限制為單個應用程式,而在某些情況下將是有用的,以在多個應用程式之間共享的配置設定。例子包括資料庫連線字串,UI主題的資訊,或佇列
變更管理跨應用程式的多個執行例項的本地配置,尤其是在雲託管的情況,也可能是具有挑戰性的。它可能會導致使用不同的配置設定的例項,而更新正被部署。
另外,更新應用程式和元件可能需要更改的配置方案。許多配置系統不支援不同版本的配置資訊。
解決方案
儲存在外部儲存器中的配置資訊,並提供可用於快速和有效地讀取和更新的配置設定的介面。外部儲存的型別取決於應用程式的主機和執行時環境。在一個雲託管的情況下它是一個典型的基於雲的儲存服務,但可能是一個託管資料庫或其他系統。
選擇用於配置資訊的備份儲存應通過適當的介面,它提供了一個可控制的方式,使回用保持一致和易於使用的
注意:
許多內建的系統配置中讀取資料時,應用程式啟動和快取記憶體記憶體中的資料提供快速訪問,並儘量減少對應用程式效能的影響。根據所使用的後備儲存器的型別,以及該商店的等待時間,這可能是有利的,以實現外部配置儲存器內的快取記憶體機制。有關實現快取的詳細資訊,請參閱快取指導。
圖1示出了本模式的概述。
圖1 - 外部配置儲存模式可選本地快取概述
問題和注意事項
在決定如何實現這個模式時,請考慮以下幾點:
•選擇一個後備儲存,提供可接受的效能,高可用性,健壯性和可備份作為應用程式的維護和管理過程的一部分。在一個雲託管的應用程式,使用雲端儲存的機制通常是一個不錯的選擇,以滿足這些要求。
•設計的後備儲存的架構允許在資訊能夠儲存型別的靈活性。確保它提供了一種使用它可以要求該申請的所有配置的要求,例如輸入資料中,設定的集合,多個版本的設定,以及任何其他功能。該模式應該是易於擴充套件的需求,以支援更多的設定更改。
•考慮後備儲存的物理效能,它與配置資訊的儲存方式,以及對效能的影響。例如,儲存一個包含XML檔案的配置資訊將要求使用配置介面或應用程式解析該檔案以讀取各個設定,將使得更新的設定更加複雜,儘管快取記憶體中的設定可有助於抵消較慢的讀取效能。
•考慮如何配置介面將允許配置設定的範圍和繼承的控制權。例如,它可能是一個要求的範圍的配置設定在組織,應用程式和裝置的水平;支援在訪問不同範圍的控制下放;並且,以防止或允許單獨的應用程式,以覆蓋設定。
•確保配置介面可以在需要的格式的配置資料暴露,如輸入值的集合,鍵/值對,或財產包。然而,考慮能力和API的複雜性之間的平衡,以使其有用的,但儘可能地易於使用。
•考慮配置儲存介面將如何表現時,設定有誤,或沒有在內部儲存存在。它可能是適當的,返回預設設定和記錄錯誤。也可以考慮,如配置設定按鍵或者名稱,二進位制資料的儲存和處理,以及null或空值處理方式的情況下,靈敏度方面。
•考慮如何將保護配置資料僅允許訪問相應的使用者和應用程式。這很可能是在配置儲存器介面的一個特徵,但它也是必要的,以確保在後備儲存器中的資料不能被直接訪問,而不適當的許可權。確保讀取和寫入配置資料所需的許可權之間的嚴格分離。也可以考慮是否需要加密部分的配置設定或全部,以及如何將配置儲存介面中實現。
•請記住,集中儲存配置,這在執行時改變應用程式的行為,是非常重要的,並應部署,更新,並使用相同的機制,部署應用程式程式碼進行管理。例如,可能會影響多個應用程式的更改,必須進行使用一個完整的測試和分階段部署的方式,以確保變化是適用於所有使用該配置的應用程式。如果管理員簡單地進行編輯的設定來更新一個應用程式,它可以產生不利使用相同設定的其他應用程式的影響。
•如果應用程式的快取記憶體的配置資訊,應用程式可能需要的,如果配置更改被提醒。有可能實現的過期策略在快取中的配置資料,使這些資訊被自動定期重新整理任何更改拿起(和付諸行動)。本指南中其他地方所描述的執行模式重構可能有關您的方案。
何時使用這個模式
這種模式非常適合於:
•被多個應用程式和應用程式例項,或在標準配置中,必須跨多個應用程式和應用程式例項執行之間共享配置設定。
•在標準配置的系統不支援所有所需的配置設定,如儲存影象或複雜的資料型別。
•作為補充商店的一些應用程式的設定,或許允許應用程式重寫一些集中儲存或所有設定。
•作為一種機制,通過記錄的部分或全部型別的訪問來配置儲存監控使用的配置設定簡化了多個應用程式管理,以及可選。
例子
在微軟Azure託管應用,用於從外部儲存配置資訊的典型的選擇是使用Azure儲存。這是有彈性的,提供高效能,並重複3次自動故障切換提供高可用性。
Azure的表格提供了一個鍵/值儲存與使用一個靈活的架構的價值的能力。
Azure的Blob儲存提供了一個分層的基於容器的儲存,可以儲存任何型別的單獨命名的blob資料。
下面的示例顯示瞭如何配置儲存可以通過Azure的Blob儲存來實現儲存和揭露的配置資訊。該BlobSettingsStore類文摘Blob儲存用於儲存配置資訊,並實現在下面的程式碼所示ISettingsStore介面。
注意:
此程式碼在ExternalConfigurationStore解決方案ExternalConfigurationStore.Cloud專案提供。該解決方案可用於下載本指導意見。
public interface IsettingsStore
{
string Version { get; }
Dictionary<string, string> FindAll();
void Update(string key, string value);
}
該介面定義的方法,用於檢索和更新在配置儲存中保持的配置設定,並且包括可用於檢測是否有任何配置設定最近已修改的版本號。何時配置設定被更新時,版本號的變化。該BlobSettingsStore類使用BLOB的ETag的屬性來實現的版本。一個blob的ETag的屬性將BLOB寫入每一次自動更新。
注意:
需要注意的是,按照設計,這個簡單的解決方案,展現了所有的配置設定為字串值,而不是型別的值。
該ExternalConfigurationManager類提供了圍繞BlobSettingsStore物體的包裝。應用程式可以使用這個類來儲存和檢索配置資訊。這個類使用Microsoft無擴充套件庫來揭露過的IObservable介面的實現做出任何配置更改。如果設定是通過呼叫SetAppSetting法修改,更改的事件引發,所有訂閱者此事件將被通報。
請注意,所有的設定也快取到ExternalConfigurationManager類快速訪問內部Dictionary物件。該SetAppSetting方法更新該快取記憶體中,並且該應用程式可以使用以檢索配置設定的GetSetting方法從快取記憶體中讀取資料(如果未在該快取記憶體中找到該設定,它從BlobSettingsStore物件檢索代替)。
所述的getSettings方法呼叫CheckForConfigurationChanges的方法來檢測在Blob儲存的配置資訊是否通過檢查版本號,並將它與所述ExternalConfigurationManager物件保持當前的版本號進行比較已經改變。如果一個或多個已經發生了變化,改變的事件引發,並快取在Dictionary物件的配置設定被重新整理。這是快取除了圖案的應用。
下面的程式碼示例演示如何更改的情況下,SetAppSettings方法,該方法的getSettings和CheckForConfigurationChanges方法實現
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類還提供了一個名為Environment屬性。此屬性的目的是為了支援不同的配置為在不同的環境中,如臨時和生產執行的應用程式。
一個ExternalConfigurationManager物件也可以定期查詢BlobSettingsStore物件的任何變化(通過使用定時器)。該StartMonitor和StopMonitor方法如下圖所示的啟動程式碼示例和停止計時器。該OnTimerElapsed方法當定時器到期時,並呼叫CheckForConfigurationChanges方法來檢測的任何變化,並提高了變更的情況下,如前面所描述執行。
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類被例項化作為由ExternalConfiguration類如下所示的單一例項。
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專案。它顯示瞭如何在應用程式使用ExternalConfiguration類讀取和更新設定。
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類,展示瞭如何應用訂閱配置事件。
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));
...
}