# 云计算设计模式(十九)——运行重构模式
设计应用程序,使得它可以在不需要重新部署或者重新启动应用程序重新配置。这有助于保持可用性并减少停机时间。
## 背景和问题
一个主要目的为重要的应用,如商业和企业网站是尽量减少停机时间以及由此引发的中断给客户和用户。但是,有时有必要重新配置应用程序改变特定行为或设置,而在部署和使用。因此,它是用于该应用程序被设计成这样一种方式,以允许在运行时要应用这些配置的变化,并为应用程序,以检测所述变化并且尽快地应用它们的部件的优点。
该种要应用可能被调整记录,以协助与应用程序调试问题,交换使用不同数据存储的连接字符串,或者打开或关闭特定的部分或应用程序的功能的粒度配置变化的例子。
## 解决方案
为实施这一模式的解决方案依赖于应用程序托管环境中可用的功能。典型地,应用程序代码将响应于由检测到的变化对应用程序配置时,主机基础设施提出的一个或多个事件。这通常是上载新的配置文件,或响应于改变通过管理门户的配置或者通过访问的API的结果。
码处理的配置变化事件可以检查变化,并将其应用到该应用程序的组件。有必要对这些部件进行检测和反应的变化,因此它们的值通常会被公开为可写的属性或方法,在事件处理程序的代码可以设置为新值,或执行。从这一点来说,该部件应使用新的值,以便在需要改变应用程序的行为发生。
如果这是不可能的部件,以应用更改在运行时,这将是必要的,重新启动该应用程序,从而当应用程序启动时再次这些更改应用。在一些托管环境中它可能会检测到这些类型的变化,并指出对环境的应用程序必须重新启动。在其他情况下,可能有必要执行该分析的设置更改,并强制必要的应用程序重新启动时的代码。
图1示出了本模式的概述。
![](https://box.kancloud.cn/2015-09-21_55ffa43132706.png)
图1 - 此模式的基本概述
大多数环境中暴露响应配置更改引发的事件。在那些不这样做,定期检查更改配置并应用这些变化将是必要的轮询机制。它也可能有必要重新启动应用程序,如果变化不能在运行时被应用。例如,有可能以比较在预设的时间间隔一个配置文件的日期和时间,并运行代码以应用更改时的较新版本中找到。另一种方法是,其中包含一个控制中的应用程序的管理用户界面,或使一个安全端点可以从应用程序外部进行访问,其执行读取,并应用更新的配置的代码。
或者,该应用程序可以反应以在环境中的一些其他变化。例如,发生于特定的运行时错误可能会改变日志配置自动收集更多的信息,或者代码可以使用当前日期读取和应用主题,反映了季节或特殊事件。
## 问题和注意事项
在决定如何实现这个模式时,请考虑以下几点:
- 配置设置必须存储在部署的应用程序之外,使得它们可以在不需要整个包被重新部署更新。典型的设置被存储在配置文件中,或者在外部存储库中,如一个数据库或网络存储。访问运行时配置机制,应严格控制,以及使用时的严格审核。
- 如果托管的基础设施不会自动检测配置更改的事件,揭露这些事件对应用程序代码,您必须实现一种替代机制来检测和应用更改。这可以是通过轮询机制,或者通过暴露交互式控制或端点发起更新过程。
- 如果您需要实现一个轮询机制,考虑如何经常检查更新的配置应该发生。长轮询间隔将意味着变化可能不被应用了一段时间。短的间隔可能会产生不利影响,通过吸收现有的计算和I / O资源的操作。
- 如果是应用程序的多个实例,附加的考虑因素,这取决于如何变化进行检测。如果改变是通过由宿主基础结构引发的事件自动检测到,这些变化可能不被同时应用的所有实例进行检测。这意味着,某些情况下,将要使用的原始配置为一个周期,而有些则使用新的设置。如果该更新是通过轮询机制检测到,这必须以保持一致性通信改变到所有实例。
- 一些配置的变化可能要求应用程序重新启动,甚至要求托管服务器重新启动。您必须确定这些类型的配置设置和执行的每一个相应的操作。例如,要求应用程序重新启动的变化可能会自动执行此操作,或者它可能是管理员负责发起重新启动在适当的时间时,应用过大的负荷和应用程序可以处理的其他实例下是不的负载。
- 更新并确认他们是成功的,而更新的应用程序实例正在执行正确,将更新应用到所有实例之前的分阶段部署计划。由此,能够防止应发生错误的应用程序的总的中断。凡更新需要重新启动或应用程序的重新启动,特别是在应用程序有一个显著启动或热身的时候,用一个分阶段部署的方式,以防止多个实例脱机在同一时间。
- 考虑如何将回滚造成的问题配置更改,或导致申请失败。例如,它应该能够滚动的等待轮询间隔,以检测所述变化背部的变化立即代替。
- 考虑如何配置设置的位置可能会影响应用程序的性能。例如,你应该处理将发生,如果您使用外部存储不可用的错误,当应用程序启动时,或配置更改将被应用,比如用一个默认的配置或通过本地缓存的设置在服务器和重用这些值而重试访问远程数据存储。
- 高速缓存可以帮助减少延迟,如果一个组件需要多次访问配置设置。然而,当配置改变时,应用程序代码将需要无效缓存设置,该组件必须使用更新后的设置。
## 何时使用这个模式
这种模式非常适合于:
- 应用程序,而您必须避免一切不必要的停机时间,同时仍然能够将更改应用到应用程序配置。
- 环境,揭露事件自动提出的主要配置更改时。通常,这是当检测到一个新的配置文件,或者更改了现有的配置文件。
- 应用的地方,往往配置更改和变化可以应用于组件,而不要求应用程序重新启动,或无需托管服务器必须重新启动。
这种模式可能不是合适的,如果运行时组件的设计使得它们只能在初始化时被配置,并更新这些部件的努力不能相比,重新启动应用程序和持久的一个短的停机时间是合理的。
## 例子
微软 Azure 云服务的角色发现和揭露被提了两个事件,当主机环境检测变化的 ServiceConfiguration.cscfg 文件:
- RoleEnvironment.Changing。引发此事件被检测到的结构变化后,但在此之前它被施加到该应用程序。你可以处理查询的变化,并取消运行时重新配置的活动。如果取消了变化,网页或辅助角色将自动以使新配置被应用程序使用的重新启动。
- RoleEnvironment.Changed。引发此事件后,应用程序的配置得到了应用。可以处理该事件来查询所应用的改变。
当取消在 RoleEnvironment.Changing 事件改变要表示到 Azure,一个新的设置不能被应用于该应用程序正在运行时,并且它必须以使用新的值被重新启动。有效地,你会取消更改只有在您的应用程序或组件无法反应在运行时改变,需要重新启动才能使用新的值。
> 注意: 欲了解更多信息,请参阅 RoleEnvironment.Changing 事件并使用 RoleEnvironment.Changing 事件 MSDN 上。
处理 RoleEnvironment.Changing 和 RoleEnvironment.Changed 事件,你通常会添加一个自定义处理该事件。例如,从你可以下载本手册的例子运行时重新配置的解决方案的 Global.asax.cs 类下面的代码显示了如何添加一个名为 RoleEnvironment_Changed 到事件处理链中的自定义函数。这是从实施例的的 Global.asax.cs 文件。
> 注意: 这种模式的例子是,在 RuntimeReconfiguration 解决方案的 RuntimeReconfiguration.Web 项目。
~~~
protected void Application_Start(object sender, EventArgs e)
{
ConfigureFromSetting(CustomSettingName);
RoleEnvironment.Changed += this.RoleEnvironment_Changed;
}
~~~
在 Web 或工作的角色,你可以在处理 RoleEnvironment.Changing 事件的作用的 OnStart 事件处理程序中使用类似的代码。这是从实施例的 WebRole.cs 文件。
~~~
public override bool OnStart()
{
// Add the trace listener. The web role process is not configured by web.config.
Trace.Listeners.Add(new DiagnosticMonitorTraceListener());
RoleEnvironment.Changing += this.RoleEnvironment_Changing;
return base.OnStart();
}
~~~
要注意的是,在网页的角色的情况下,所述的 OnStart 事件处理程序中从 Web 应用程序本身的单独进程中运行。这就是为什么你通常会处理在 Global.asax 文件中 RoleEnvironment.Changed 事件处理程序,让您可以更新您的 Web 应用程序的运行时配置,而 RoleEnvironment.Changing 事件中的角色本身。在辅助角色的情况下,您可以订阅双方 RoleEnvironment.Changing和RoleEnvironment.Changed 的 OnStart 事件处理程序中的事件。
> 注意: 可以在服务配置文件中存储自定义的配置设置,在自定义配置文件,在数据库中,如在虚拟机中的 Azure SQ L数据库或 SQL Server,或者在天青 blob 和表存储。您需要创建一个可以访问自定义配置设置和应用程序内设置组件的属性,这些适用于应用程序通常代码。
例如,下面的自定义函数读取设置,其名称作为参数传递,从 Azure 的服务配置文件中的值,然后将它应用到一个名为 SomeRuntimeComponent 运行时组件的当前实例。这是从实施例的的 Global.asax.cs 文件
~~~
private static void ConfigureFromSetting(string settingName)
{
var value = RoleEnvironment.GetConfigurationSettingValue(settingName);
SomeRuntimeComponent.Instance.CurrentValue = value;
}
~~~
**注意**
一些配置设置,如那些用于 Windows 标识框架,不能存储在 Azure 服务配置文件中,并且必须在 App.config 或 Web.config 文件。
在Azure中,一些配置的变化检测,并自动应用。这包括在 Diagnostics.wadcfg 文件寡妇天青诊断系统,它指定的信息类型来收集和如何保持日志文件的结构。因此,它仅需要编写处理添加到服务配置文件的自定义设置的代码。你的代码应该:
- 从更新的配置应用自定义设置您的应用程序在运行时的相应组件,使他们的行为体现了新的配置。
- 取消改变,以指示到 Azure 新的值不能在运行时应用,该应用程序必须按顺序重新开始对要应用的变化。
例如,从你可以下载本手册的例子运行时重新配置的解决方案 WebRole.cs 类下面的代码显示了如何使用 RoleEnvironment.Changing 事件取消所有设置的更新,除了可应用于那些在运行时,不需要重新启动。此示例允许在运行时应用无需重新启动应用程序(使用此设置将能够读取新的值,并相应地在运行时改变其行为的组成部分)更改为“CustomSetting”的设置。任何其他更改的配置将自动使网页或工作的角色重新启动。
~~~
private void RoleEnvironment_Changing(object sender,
RoleEnvironmentChangingEventArgs e)
{
var changedSettings = e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Select(c => c.ConfigurationSettingName).ToList();
Trace.TraceInformation("Changing notification. Settings being changed: "
+ string.Join(", ", changedSettings));
if (changedSettings
.Any(settingName => !string.Equals(settingName, CustomSettingName,
StringComparison.Ordinal)))
{
Trace.TraceInformation("Cancelling dynamic configuration change (restarting).");
// Setting this to true will restart the role gracefully. If Cancel is not
// set to true, and the change is not handled by the application, the
// application will not use the new value until it is restarted (either
// manually or for some other reason).
e.Cancel = true;
}
Else
{
Trace.TraceInformation("Handling configuration change without restarting. ");
}
}
~~~
注意:这种方法证明了好的做法,因为它确保了更改应用程序代码不知道任何设置(因此不能确保它可以在运行时应用)将导致重新启动。如果更改任何一个被取消,该角色将被重新启动。
然后可以检测到并应用于应用程序的组件的新的配置后已被接受由 Azure 的框架更新未在 RoleEnvironment.Changing 事件处理程序取消。例如,在该示例解决方案的 Global.asax 文件以下代码处理 RoleEnvironment.Changed 事件。它检查每个配置设置,并且当它找到名为“CustomSetting”的设置,调用一个函数(前面所示),该应用新的设置,以在应用程序中的适当组件。
~~~
private void RoleEnvironment_Changed(object sender,
RoleEnvironmentChangedEventArgs e)
{
Trace.TraceInformation("Updating instance with new configuration settings.");
foreach (var settingChange in
e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>())
{
if (string.Equals(settingChange.ConfigurationSettingName,
CustomSettingName,
StringComparison.Ordinal))
{
// Execute a function to update the configuration of the component.
ConfigureFromSetting(CustomSettingName );
}
}
}
~~~
需要注意的是,如果你不取消配置的变化,但不将新值应用到您的应用程序组件,那么更改将不会生效的下一次重新启动应用程序之前。这可能会导致不可预测的行为,尤其是当所述宿主角色实例由 Azure 的自动重启在其日常维护操作,在该点的新的设定值将被应用的一部分。
- 前言
- (一)—— 缓存预留模式
- (二)—— 断路器模式
- (三)—— 补偿交易模式
- (四)——消费者的竞争模式
- (五)——计算资源整合模式
- (六)——命令和查询职责分离(CQRS)模式
- (七)——事件获取模式
- (八)——外部配置存储模式
- (九)—— 联合身份模式
- (十)——守门员模式
- (十一)—— 健康端点监控模式
- (十二)—— 索引表模式
- (十三)——领导人选举模式
- (十四)——实体化视图模式
- (十五)—— 管道和过滤器模式
- (十六)——优先级队列模式
- (十七)—— 基于队列的负载均衡模式
- (十八)—— 重试模式
- (十九)——运行重构模式
- (二十)—— 调度程序代理管理者模式
- (二十一)——Sharding 分片模式
- (二十二)——静态内容托管模式
- (二十三)——Throttling 节流模式
- (二十四)—— 仆人键模式