软件体系架构阅读笔记(十一)

架构设计则为满足架构需求的质量属性寻找适当的战术。对如何实现特定的质量属性感兴趣。质量需求指定了软件的响应,以实现业务目标。我们感兴趣的是设计使用设计模式、架构模式或架构策略创建设计的“战术”。

是什么使一个设计具有了可移植性,一个设计具有了高性能,而另一个设计具备了可集成性?实现这些质量属性依赖于基本的设计策略。我们将对这些称之为“战术”的设计决策进行分析。战术就是影响质量属性响应控制的设计决策。战术集合称为“架构策略”。架构模式以某种方式将战术打包在一起。

系统设计是由决策集合组成。对设计师来说,每个战术都是一个设计选择。例如,其中一个战术引入了冗余,以提高系统的可用性。这是提高可用性的一个选择但是不是唯一选择。

我们将每个系统质量属性的战术组织为层次形式,但是每个层次只是为了说明一些战术,而且任何战术列表都肯定是不完成的。

1. 可用性战术

恢复和修复是可用性的重要方面,为了阻止错误发展成故障,至少能够把错误限制在一定的范围内,从而使修复成为可能。维持可用性的所有方法包括某种类型的冗余,用来检测故障的某种类型的健康监视,以及当检测到故障时某种类型的恢复。有些情况下,监视或恢复是自动进行的,有时需要手动。

我们事项考虑错误检测,然后分析错误恢复,最后讨论错误预防。

1> 错误检测

用于识别错误的3个战术是命令/响应、心跳和异常

⑴命令/响应。一个组件发出一个命令,并希望在预定义的时间内收到一个来自审查组件的响应。可以把该战术用在共同负责某项任务的一组组件内。客户机也可以使用这种战术,以确保服务器对象和到服务器的通信路径在期望的性能边界内操作。可以用一种层级形式组织“命令/响应”错误探测器,其中最底层的探测器对与其共享一个处理器的软件进程发出命令,较高层的错误探测器对较低层的探测器发出命令。与所有进程发出命令的远程错误探测器相比,这种战术所使用的通信带宽更少。

⑵心跳。一个组件定期发出一个心跳消息,另一个组件接收听该信息。如果心跳失败,则假定最初的组件失败,并通知错误纠正组件。心跳还可以传递数据。例如,自动柜员机定期向服务器发送一次交易日志。该消息不仅起到心跳的作用,而且传送了要处理的数据。

⑶异常。识别错误的一个方法就是遇到了异常。

命令/响应和心跳战术在不同的进程中操作,异常战术在一个进程中操作。异常处理程序通常将错误在语义上转换为可以被处理的形式。

2> 错误恢复

错误恢复由准备恢复和修复系统两部分组成。

⑴表决。运行在冗余处理器上的每个进程都具有相同的输入,它们计算发送给表决者的一个简单的输出值。如果表决者检测到单处理器的异常行为,那么就中止这一行为。表决算法可以是“多数规则”或“首选组件“或其他算法。该方法用于纠正算法的错误操作或者处理器的故障,通常用在控制系统。每个冗余组件的软件可以由不同的小组开发,并且在不同平台上执行。稍微好一点情况是在不同平台上开发一个软件组件,但是这样的开发和维护费用非常昂贵。

⑵主动冗余(热重启)。所有的冗余组件都以并行的方式对事件做出响应。因此他们都处在相同的状态。仅使用一个组件的响应,丢弃其他组件的响应。错误发生时,使用该战术的系统停机时间通常是几毫秒,因为备份是最新的,所以恢复所需要的时间就是切换时间。

⑶被动冗余(暖重启/双冗余/三冗余)

一个组件(主要的)对事件做出响应,并通知其他组件(备用的)必须进行状态更新。当错误发生时,在继续提供服务前,系统必须首先确保备用状态是最新的。该方法也用在控制系统中,通常情况是在输入信息通过通信通道或传感器到来时,如果出现故障必须从主组件切换到备用组件时使用。

⑷备件

备用件是计算平台配置用于更换各种不同的故障组件。出现故障时,必须将其重新启动为适当的软件配置,并对其状态进行初始化。定期设置持久设备的系统状态的检查点,并记录持久设备的所有状态变化能够使备件设置为适当的状态。这通常用作备用客户机工作站,出现故障时,用户可以离开。该战术的停机时间通常是几分钟。

⑸Shadow操作。以前出现故障的组件可以在短时间内以“shadow模式”运行,以确保在恢复该组件前,模仿工作组件行为。

⑹状态再同步。主动和被动冗余战术要求恢复的组件在重新提供服务前更新其状态。更新的方法取决于可以承受的停机时间、更新的规模以及更新所要求的消息的数量。

⑺检查点/回滚。检查点就是记录所创建的一致状态,或者是定期进行,或者是对具体事件做出响应。有时系统会以一种不同寻常的方式出现故障,可检测到其状态不一致。在这种情况下,应该使用上一个一致状态检查点和拍了快照后所发生的事务日志来恢复系统。

3> 错误预防

⑴从服务中删除。该战术从操作中删除了系统的一个组件,以执行某些活动来防止预期发生的故障。一个示例就是重新启动组件,以防止内存泄露导致故障的发生。如果从服务中删除是自动的,则可以设计架构策略来支持它。如果是人工进行的,则必须对系统进行设计以对其提供支持。

⑵事务。事务就是绑定几个有序的步骤,以能够立刻撤销整个绑定。如果进程中的一个步骤失败的话,可以使用事务来防止任何数据受到影响,还可以使用事务来防止访问相同数据的几个同时线程之间发生冲突。
⑶进程监视器。一旦检测到进程中存在着错误,监视进程就可以删除非执行进行,并为该进程创建一个新的实例,就像在备件战术中一样,初始化为某个适当的状态。

总结了上面讨论的战术。

2. 可修改性战术

可修改战术的目标是控制实现、测试和部署变更的时间和成本。把可修改性战术根据其目标进行分组。一组可修改性战术目标是减少由某个变更直接影响的数量。这组称为“局部化修改”。另一组可修改战术的目标是限制对局部化的模块的修改。这组称为“防止连锁反应”。两组之间的差别是有直接受变更影响的模块(那些调整其责任来完成变更的模块)间接受变更影响的模块(那些责任保持不变,但必须改变其实现来适应直接受影响的模块)。第三组战术的目标是控制部署时间和成本。我们把这组战术叫做“延迟绑定时间”。

1> 局部化修改。

目标是在设计期间为模块分配责任,以把预期的变更限制在一定范围内。其战术有:维持语义的一致性、预期期望的变更、泛化该模块、限制可能的选择。

⑴维持语义的一致性。语义的一致性是在模块中责任之间的关系。目标是确保所有这些责任都能够协同工作,不需要过多地依赖其他模块。该目标是通过选择具有语义一致性的责任来实现的。耦合和内聚指标是度量语义一致性的尝试,但它们遗漏了变更的上下文。相反根据一组预期的变更来度量语义一致性。其中一个子战术就是“抽象通用服务”。通过专门的模块提供通用服务通常被视为支持重用。但是抽象通用服务也支持可修改性。如果已经抽象出了通用服务,那么对这些通用服务的修改只需要进行一次,而不需要在使用这些服务的每个模块中都进行修改。此外,对使用这些服务的模块的修改不会影响到其他用户。不仅支持局部化修改,而且还能够防止连锁反应。抽象通用服务的示例就是应用框架的使用和其他中间件软件的使用。

⑵预期期望的变更。考虑所预想的变更的集合提供了一个评估特定的责任分配的方法。基本的问题是“对于每次变更,所建议的分解是否限定了为完成变更所需要修改的模块的集合?”一个相关的问题是“根本不同的变更会影响相同模块吗?”这与语义一致性有什么不同呢?根据语义一致性分配责任,假定期望的变更在语义上是一致的。预测期望变更的战术不关心模块责任的一致性,它所关心的是使变更的影响最小。在实际中很难单独使用该战术,因为不可能预期所有变更。基于此原因,我们通常结合语义一致性来使用该战术。

⑶泛化该模块。使一个模块更通用能够使它根据输入计算更广泛的功能。可以该输入看作是为该模块定义了一种语言,这可能会如同使常数成为输入参数一样简单;也可能如同把该模块实现为解释程序,并使输入参数成为解释程序的语言中的程序一样复杂。模块越通用,越有可能通过调整语言而非修改模块来进行请求变更。

⑷限制可能的选择。修改(尤其是在产品线中的修改)的范围可能非常大,因此可能会影响很多模块。限制可能的选择将会降低这些修改所造成的影响。例如,产品线的某个变化点可能允许处理器的变化。将处理器变更限制为相同家族的成员就限制了可能的选择。

2> 防止连锁反应。

修改所产生的一个连锁反应就是需要改变该修改并没有直接影响到的模块。例如,改变了模块A以完成某个特定的修改,那么必须改变模块B,这仅仅是因为改变了A,在某种意义上来说,是因为它依赖于模块A。

原文地址:https://www.cnblogs.com/lwq666/p/11050275.html