(八)——外部配置存儲模式

2018-02-24 15:44 更新

云計算設(shè)計模式(八)——外部配置存儲模式

移動配置信息從應(yīng)用部署包到一個集中位置。這個模式可以提供機會,以便管理和配置數(shù)據(jù)的控制,以及用于跨應(yīng)用程序和應(yīng)用程序?qū)嵗蚕淼呐渲脭?shù)據(jù)。

背景和問題

大多數(shù)應(yīng)用程序運行時環(huán)境包括位于應(yīng)用程序文件夾內(nèi)的在部署應(yīng)用程序文件保持配置信息。在某些情況下也能夠編輯這些文件來改變該應(yīng)用程序的行為,它已經(jīng)被部署之后。然而,在許多情況下,改變配置所需要的應(yīng)用程序被重新部署,從而導(dǎo)致不可接受的停機時間和額外的管理開銷。

本地配置文件還配置限制為單個應(yīng)用程序,而在某些情況下將是有用的,以在多個應(yīng)用程序之間共享的配置設(shè)置。例子包括數(shù)據(jù)庫連接字符串,UI 主題的信息,或隊列和存儲所使用的一組相關(guān)的應(yīng)用程序的URL。

變更管理跨應(yīng)用程序的多個運行實例的本地配置,尤其是在云托管的情況,也可能是具有挑戰(zhàn)性的。它可能會導(dǎo)致使用不同的配置設(shè)置的實例,而更新正被部署。

另外,更新應(yīng)用程序和組件可能需要更改的配置方案。許多配置系統(tǒng)不支持不同版本的配置信息。

解決方案

存儲在外部存儲器中的配置信息,并提供可用于快速和有效地讀取和更新的配置設(shè)置的接口。外部存儲的類型取決于應(yīng)用程序的主機和運行時環(huán)境。在一個云托管的情況下它是一個典型的基于云的存儲服務(wù),但可能是一個托管數(shù)據(jù)庫或其他系統(tǒng)。

選擇用于配置信息的備份存儲應(yīng)通過適當(dāng)?shù)慕涌?,它提供了一個可控制的方式,使回用保持一致和易于使用的訪問被朝向。理想情況下,它應(yīng)該公開在鍵入正確,結(jié)構(gòu)化的格式的信息。的實施也可能需要對用戶進(jìn)行授權(quán)“,以保護(hù)結(jié)構(gòu)的數(shù)據(jù)訪問,并且具有足夠的靈活性,以允許要被存儲的多個版本的配置(例如,開發(fā),分段,或生產(chǎn),并且每一個的多個發(fā)行版本)。

注意: 許多內(nèi)置的系統(tǒng)配置中讀取數(shù)據(jù)時,應(yīng)用程序啟動和高速緩存內(nèi)存中的數(shù)據(jù)提供快速訪問,并盡量減少對應(yīng)用程序性能的影響。根據(jù)所使用的后備存儲器的類型,以及該商店的等待時間,這可能是有利的,以實現(xiàn)外部配置存儲器內(nèi)的高速緩存機制。有關(guān)實現(xiàn)緩存的詳細(xì)信息,請參閱緩存指導(dǎo)。

圖1示出了本模式的概述。

圖1 - 外部配置存儲模式可選本地緩存概述

問題和注意事項

在決定如何實現(xiàn)這個模式時,請考慮以下幾點:

  • 選擇一個后備存儲,提供可接受的性能,高可用性,健壯性和可備份作為應(yīng)用程序的維護(hù)和管理過程的一部分。在一個云托管的應(yīng)用程序,使用云存儲的機制通常是一個不錯的選擇,以滿足這些要求。
  • 設(shè)計的后備存儲的架構(gòu)允許在信息能夠保存類型的靈活性。確保它提供了一種使用它可以要求該申請的所有配置的要求,例如輸入數(shù)據(jù)中,設(shè)置的集合,多個版本的設(shè)置,以及任何其他功能。該模式應(yīng)該是易于擴展的需求,以支持更多的設(shè)置更改。
  • 考慮后備存儲的物理性能,它與配置信息的存儲方式,以及對性能的影響。例如,存儲一個包含XML文件的配置信息將要求使用配置界面或應(yīng)用程序解析該文件以讀取各個設(shè)置,將使得更新的設(shè)置更加復(fù)雜,盡管高速緩存中的設(shè)置可有助于抵消較慢的讀取性能。
  • 考慮如何配置界面將允許配置設(shè)置的范圍和繼承的控制權(quán)。例如,它可能是一個要求的范圍的配置設(shè)置在組織,應(yīng)用程序和設(shè)備的水平;支持在訪問不同范圍的控制下放;并且,以防止或允許單獨的應(yīng)用程序,以覆蓋設(shè)置。
  • 確保配置界面可以在需要的格式的配置數(shù)據(jù)暴露,如輸入值的集合,鍵/值對,或財產(chǎn)包。然而,考慮能力和API的復(fù)雜性之間的平衡,以使其有用的,但盡可能地易于使用。
  • 考慮配置存儲界面將如何表現(xiàn)時,設(shè)定有誤,或沒有在內(nèi)部存儲存在。它可能是適當(dāng)?shù)?,返回默認(rèn)設(shè)置和記錄錯誤。也可以考慮,如配置設(shè)置按鍵或者名稱,二進(jìn)制數(shù)據(jù)的存儲和處理,以及null或空值處理方式的情況下,靈敏度方面。
  • 考慮如何將保護(hù)配置數(shù)據(jù)僅允許訪問相應(yīng)的用戶和應(yīng)用程序。這很可能是在配置存儲器接口的一個特征,但它也是必要的,以確保在后備存儲器中的數(shù)據(jù)不能被直接訪問,而不適當(dāng)?shù)臋?quán)限。確保讀取和寫入配置數(shù)據(jù)所需的權(quán)限之間的嚴(yán)格分離。也可以考慮是否需要加密部分的配置設(shè)置或全部,以及如何將配置存儲接口中實現(xiàn)。
  • 請記住,集中存儲配置,這在運行時改變應(yīng)用程序的行為,是非常重要的,并應(yīng)部署,更新,并使用相同的機制,部署應(yīng)用程序代碼進(jìn)行管理。例如,可能會影響多個應(yīng)用程序的更改,必須進(jìn)行使用一個完整的測試和分階段部署的方式,以確保變化是適用于所有使用該配置的應(yīng)用程序。如果管理員簡單地進(jìn)行編輯的設(shè)置來更新一個應(yīng)用程序,它可以產(chǎn)生不利使用相同設(shè)置的其他應(yīng)用程序的影響。
  • 如果應(yīng)用程序的高速緩存的配置信息,應(yīng)用程序可能需要的,如果配置更改被提醒。有可能實現(xiàn)的過期策略在緩存中的配置數(shù)據(jù),使這些信息被自動定期刷新任何更改拿起(和付諸行動)。本指南中其他地方所描述的運行模式重構(gòu)可能有關(guān)您的方案。 何時使用這個模式

這種模式非常適合于:

  • 被多個應(yīng)用程序和應(yīng)用程序?qū)嵗蛟跇?biāo)準(zhǔn)配置中,必須跨多個應(yīng)用程序和應(yīng)用程序?qū)嵗龍?zhí)行之間共享配置設(shè)置。
  • 在標(biāo)準(zhǔn)配置的系統(tǒng)不支持所有所需的配置設(shè)置,如存儲圖像或復(fù)雜的數(shù)據(jù)類型。
  • 作為補充商店的一些應(yīng)用程序的設(shè)置,或許允許應(yīng)用程序重寫一些集中存儲或所有設(shè)置。
  • 作為一種機制,通過記錄的部分或全部類型的訪問來配置存儲監(jiān)控使用的配置設(shè)置簡化了多個應(yīng)用程序管理,以及可選。

例子

在微軟 Azure 托管應(yīng)用,用于從外部存儲配置信息的典型的選擇是使用 Azure 存儲。這是有彈性的,提供高性能,并重復(fù) 3 次自動故障切換提供高可用性。 Azure 的表格提供了一個鍵/值存儲與使用一個靈活的架構(gòu)的價值的能力。 Azure 的 Blob 存儲提供了一個分層的基于容器的存儲,可以保存任何類型的單獨命名的 blob 數(shù)據(jù)。

下面的示例顯示了如何配置存儲可以通過 Azure 的 Blob 存儲來實現(xiàn)存儲和揭露的配置信息。該BlobSettingsStore 類文摘 Blob 存儲用于保存配置信息,并實現(xiàn)在下面的代碼所示 ISettingsStore 接口。

注意: 此代碼在 ExternalConfigurationStore 解決方案 ExternalConfigurationStore.Cloud 項目提供。該解決方案可用于下載本指導(dǎo)意見。

public interface IsettingsStore  
{  
  string Version { get; }  
?
  Dictionary<string, string> FindAll();  
?
  void Update(string key, string value);  
}

該接口定義的方法,用于檢索和更新在配置存儲中保持的配置設(shè)置,并且包括可用于檢測是否有任何配置設(shè)置最近已修改的版本號。何時配置設(shè)置被更新時,版本號的變化。該 BlobSettingsStore 類使用 BLOB 的 ETag 的屬性來實現(xiàn)的版本。一個 blob 的 ETag 的屬性將 BLOB 寫入每一次自動更新。

注意

需要注意的是,按照設(shè)計,這個簡單的解決方案,展現(xiàn)了所有的配置設(shè)置為字符串值,而不是類型的值。 該 ExternalConfigurationManager 類提供了圍繞 BlobSettingsStore 物體的包裝。應(yīng)用程序可以使用這個類來存儲和檢索配置信息。這個類使用 Microsoft 無擴展庫來揭露過的 IObservable 接口的實現(xiàn)做出任何配置更改。如果設(shè)置是通過調(diào)用SetAppSetting法修改,更改的事件引發(fā),所有訂閱者此事件將被通報。 請注意,所有的設(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)改變。如果一個或多個已經(jīng)發(fā)生了變化,改變的事件引發(fā),并緩存在 Dictionary 對象的配置設(shè)置被刷新。這是緩存除了圖案的應(yīng)用。

下面的代碼示例演示如何更改的情況下,SetAppSettings 方法,該方法的 getSettings 和 CheckForConfigurationChanges 方法實現(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 類還提供了一個名為 Environment 屬性。此屬性的目的是為了支持不同的配置為在不同的環(huán)境中,如臨時和生產(chǎn)運行的應(yīng)用程序。

一個 ExternalConfigurationManager 對象也可以定期查詢 BlobSettingsStore 對象的任何變化(通過使用定時器)。該 StartMonitor 和 StopMonitor 方法如下圖所示的啟動代碼示例和停止計時器。該 OnTimerElapsed 方法當(dāng)定時器到期時,并調(diào)用 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 項目。它顯示了如何在應(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));  
  ...  
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號