移动配置信息从应用部署包到一个集中位置。这个模式可以提供机会,以便管理和配置数据的控制,以及用于跨应用程序和应用程序实例共享的配置数据。
大多数应用程序运行时环境包括位于应用程序文件夹内的在部署应用程序文件保持配置信息。在某些情况下也能够编辑这些文件来改变该应用程序的行为,它已经被部署之后。然而,在许多情况下,改变配置所需要的应用程序被重新部署,从而导致不可接受的停机时间和额外的管理开销。
本地配置文件还配置限制为单个应用程序,而在某些情况下将是有用的,以在多个应用程序之间共享的配置设置。例子包括数据库连接字符串,UI 主题的信息,或队列和存储所使用的一组相关的应用程序的URL。
变更管理跨应用程序的多个运行实例的本地配置,尤其是在云托管的情况,也可能是具有挑战性的。它可能会导致使用不同的配置设置的实例,而更新正被部署。
另外,更新应用程序和组件可能需要更改的配置方案。许多配置系统不支持不同版本的配置信息。
存储在外部存储器中的配置信息,并提供可用于快速和有效地读取和更新的配置设置的接口。外部存储的类型取决于应用程序的主机和运行时环境。在一个云托管的情况下它是一个典型的基于云的存储服务,但可能是一个托管数据库或其他系统。
选择用于配置信息的备份存储应通过适当的接口,它提供了一个可控制的方式,使回用保持一致和易于使用的访问被朝向。理想情况下,它应该公开在键入正确,结构化的格式的信息。的实施也可能需要对用户进行授权“,以保护结构的数据访问,并且具有足够的灵活性,以允许要被存储的多个版本的配置(例如,开发,分段,或生产,并且每一个的多个发行版本)。
注意: 许多内置的系统配置中读取数据时,应用程序启动和高速缓存内存中的数据提供快速访问,并尽量减少对应用程序性能的影响。根据所使用的后备存储器的类型,以及该商店的等待时间,这可能是有利的,以实现外部配置存储器内的高速缓存机制。有关实现缓存的详细信息,请参阅缓存指导。
在决定如何实现这个模式时,请考虑以下几点:
这种模式非常适合于:
在微软 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)); ... }