第5章 软件架构及软件质量

 
4.以测试为驱动
“Only testing can prove the ultimate software quality” 是一句大家都知道的名言。这
 
意味着,作为衡量一个系统品质的最好方法就是进行髙质量的系统测试。虽然进行测试这 项工作,并不是架构师和设计人员分内的职责。但是,作为系统的主要技术负责人,架构 师和设计人员有着其应该担负的责任。这就是在许多公司的架构和设计文档中,我们可以 看到有关测试章节的主要原因。
 
在实际工作中,架构和设计人员在系统测试方面所应该担负的职责可以归纳为下面三 个主要方面:
首先,架构和设计人5在每进行一次系统架构增量、提炼及重构之前,会进行初步的测试准备工作。所谓测试准备,就是他们必须首先明确如何进行系统质量的检测, 再进行增量、提炼和重构。但是,往往这些准备不可能非常完善。在进行系统架构 增量、提炼及重构之后,已经完全掌握了当前阶段系统所能提供的核心功能、接口 分布、功能协作以及系统/功能质量要求的实现等明确的信息。这样,架构和设计人员就可以帮助解决如何测试这些系统的特性,即制定测试条件、测试规則、測试用 例等详细完善的测试规约。
 
其次,上述这些由架构和设计人员帮助制定的测试规约,会被后续的架构、设计、 编码、测试等不同的工作人员参考或执行。架构和设计人员必须确保后续工作完全 遵循这些测试规约。这也是架构和设计人员除去架构构建和系统设计外,在系统开 发阶段的另外一个主要任务,即实施的监督工作。
 
最后,当后续的编码开发进行到一定阶段后,测试人员会运行很多前期制定的测试 用例。当所有涉及的测试完成之后,相关的测试信息和测试报告会成为架构和设计 人员定位架构和设计缺陷的有力参考。针对系统的这些被检测出来的缺陷,系统架 构应该进行必要的重构。
 
上述三个方面的工作,第一方面的工作是最困难的。因为一个系统质量的如何,完全 取决于你怎样去检测它,即架构和设计人员帮助制定的测试规约的质量。如果我们没有原 则地胡乱制定一些测试规约,势必造成系统的质量检测不可能系统化地进行。
 
为了解决这样的问题,我们可以考虑参考Andy Hunt和David Thomas提出的著名的 “A-TRIP”和“Right-BICEP”原则来帮助架构和设计人员制定测试规约。
 
2) Thorough 原则:
 
•尽量进行全面的测试,能够测试到多细,就进行多细的测试。尤其是那些Bug最容
易出现的地方(经验和统计数据告诉我们,那些你最关切、最担心的核心热点部分, 往往就是Bug最多的地方)。
 
•尽量借用类似NCover或Clover这样的工具进行test coverage测试和分析。
 
3) Repeatable 原则:
 
*测试的结果应该是稳定和一致的。否则代表系统依然存在问题。例如:即便是你打 乱测试的顺序,测试结果也是一样的。
 
•测试不能依赖于那些不可控的外界因素^
 
•利用mock手段来进行割裂和模拟。
 
4) Independent原则:
•测试不能形成互相依赖的恶性循环。
 
•每次只进行系统一个方面的测试。
 
•测试最好不要依赖于测试环境,例如:某个特定的硬件机型^
 
5)    Professional原则:
 
•单元测试同样也要尽量考虑系统质量方面的要求。
 
•不要把测试重点放在那些无足轻重的方面。例如:为了提高测试通过率,去测试那 些系统自动产生的getter或setter代码。
•为确保系统质量,尽量使测试的代码和产品的代码一样多或更多。
 
SEI组织对于系统质量的分类,只是—个参考体系,并不是-个世界标准。在实际工 作中’我们在进行架构设计时经常考虑的各种质量可以分为如下两个方面:
•系统运维质量:当系统运行时的质量要求。这可以包括Functionality、 Concurrency、Performance、Security、Availability, Fault Tolerance、Usability、 Interoperability 等。
 
•系统演化质量:系统发展演化所要求的质量。例如Modifiability. Portability, Reusability. Configuration 等。
需要说明的是,我们无意在这里挑战SEI的质量分类。因为目前探讨的重点不是如何 将系统的一般质ft要求进行分类或者分为哪些类,而是如何高质量地完成每个质量要求这 样所谓的“战术设计”。
 
1.系统运维质置
 
一个系统架构需要考虑的运维质量往往很多。限于篇幅,我们只针对如下几个比较常 见而典型的质量要求,来看看如何进行战术设计。
 
Concurrency 
Fault tolerance 
Security
 
Resource Management
 
我们都知道什么是Client/Server体系结构的系统。这样体系的系统,在投入运维时, 必然会发生并发客户访问服务器端某个服务的并发问题。忽略这样的事实,会造成严重的 系统运维问题》因为并发问题,可以导致系统一连串的质量(Performance、Scalability、 Stability、Robustness、Fault Tolerances、Availability 等)出现问题。例如:系统并发处理 得不好,会使系统的Performance急剧下降,这可能是因为大家都在争夺和等待一个共享 的资源;系统的Availability也会受到影响,这可能是因为并发造成的死锁使系统不响应其 他访问等。
 
为了满足系统并发处理能力这样的运维质量要求,我们通常可以考虑在系统架构层面、 设计层面、编码层面等三个主要层面来解决系统并发问题:
 
首先,在系统架构层面,作为架构师,为了解决并发问題,我们需要知道一些所谓的 “Architecture Principle”,它们是基于经验提炼后的架构指导准则。通过这些指导准则,我们就能知道该如何选择适当的并发处理机制:
 
Client/Server之间的关系:举例来讲,客户端访问服务器端的服务,是只发送请求、 等待应答,还是必须要维护一个客户端和服务器间的session。,
 
服务请求的处理:服务器端的服务是处理客户端发来的數据流信息,还是一次性的 事件信息;服务端是采用必须等待客户端的应答、再进行后续处理这样的同步响应 方式,还是采用并不一味等待、继续进行后续处理这样的异步响应方式。
 
服务请求的顺序:服务器端同时收^多个服务请求,是否有服务请求优先级次序的要求。
 
服务种类:服务器端提供大量不同的服务、并且服务间是相互依赖的,.还是服务器 端只提供简单少量的、并且服务间是没有依赖关系的服务。
 
服务时限:服务器端的服务在执行时,一般需要长时间才能完成,还是服务执行的 时间通常比较短。
 
服务与资源:服务执行时,所利用的资源是否明确,这些资源是否受制于资源管理约束。
 
其次,作为设计层面上的设计人员,必须知道针对并发相关的问题,大多是集中体现 在了那些需要共享Object的设计和使用上。通常,有经验的设计人员会以这样的几个因素, 即“Design Principle”设计指导准则来考虑共享Object方面的问题。
 
请求的顺序:对一个共享Object的访问请求,是需要进行优先级排序,还是需要经 过其他方式的调度。
 
Object复杂度:这个共享Object,是一个简单完整的服务单元,还是只是一系列 Object组成的服务的入口 Object。
 
与客户端的同步:对一个服务器端共享Object的访问,是否需要与客户端透明地进
 
行同步动作。
 
协调机制:需要一个系统全局范围内所有共享Object的协调机制,还是要避免使用 全局协调机制。
 
同样,作为详细设计或编码人员,需要在详细设计或编码时,掌握解决并发问题的各 种“技术手段”,例如线程技术。通常,这个阶段与并发问题相关的重点已经转移到了线程 方面的实施经验上(这明显不属于架构需要解决的问题了)。
 
•避免直接访问底层的线程API。
 
•确保线程中使用各种lock的获取和释放动作已经正确完成。 
 
避免线程死锁现象。
 
 •尽量避免线程使用lock机制时所带来的性能上的负担。
除了 “Architecture Principle”、“Design Principle” 以及“技术手段”外,大童的“架
 
构模式和设计模式”也是我们在系统架构阶段和系统设计阶段强有力的经典经验支持:
 
系统架构构建中,为了解决有关并发问题,我们可以利用这些架构模式来构建我们的 并发处理框架,例如:Half-Sync/Half-Async、Leader/Followers、Active Object 等。
 
在系统设计阶段,我们可以利用这些设计模式进行有效的并发处理,例如:
 
Component Access / Coordination: Active Object、Monitor Object、Balking、 Guarded Suspension、Thread-Specific Storage 等。
 
•在详细设计和编码阶段,我们依然可以参考一些经典的设计模式来进行并发的同步
 
工作,例如:Future、Scoped Locking.Strategized Locking、Thread-Safe Interface、 Double-Checked Locking 等。
 
2) Fauft tolerance
为了实现一个高可用性(Availability)的系统,架构和设计人员必须面对如何应对系 统可能面临的各种错误:用户错误的输入、传感器错误的输入、系统中硬件的failure、软 件的bug等。只有很好地应对了这些问题,才能保证系统始终有效。我们需要很好的架构来解决这些可能导致系统全面崩溃的错误,例如:可以让系统能够从错误中自我恢复,或 者将错误及时自动报瞀进行人工干预等。这也就是我们通常所说的一个系统构建具有很髙 容错能力的含义。
 
经典的提高系统容错能力的架构经验,即所谓的“Architecture Principle”,可以总结 为下述五个重要的方面。
系统Fault tolerance识别:系统架构构建时,需要考虑系统哪些部分需要实现Fault tolerance;这些需要进行Fault tolerance的部分需要实现怎样的容措动作或容错能 力;当某某错误发生时,相应的容错动作是怎样的。
 
系统错误检测:系统架构需要支持系统错误的发现或检测功能。例如:通过交叉对 比或轮询进行状态检查、利用heartbeat方法进行检测等。
 
•系统错误评估:系统发现或发生的错误,是临时性的、不需要特殊响应的错误,还 是致命永久的、必须响应和处理的错误,否則会造成系统瘫痪。
 
系统恢复原则:系统错误发生时,需要如何处理而自我恢复,是回滚到系统出错前 的一个已知状态,还是系统前滚到一个既定的状态。
 
系统服务持续性原则:如果出现系统瘫痪的情况,是被动地由备份进行替换,还是 由备份主动检测瘫痪并及时替換,或者主动备份与被动备份结合使用。
 
架构阶段已经为后续的设计工作奠定了解决系统Fault tolerance方面问题的基础。进 入设计阶段,设计人员同样可以参考很多经典的“架构模式和设计模式”来解决系统Fault tolerance等方面的问题。
 
举个例子来讲’为了使系统Fault tolerance中的“系统错误评估”能够高质量完成, 我们可以借用一个由James 0. Coplien、Robert Gamoke和Robert Hanmer提出的非常 经典的设计模式-“Leaky Bucket Counter”,如图5-11所示。
简单讲(本书不重点讲述设计模式,有关设计模式的问题将会在本系列丛书的《设计 模式的艺术》中进行详细探讨),在应用Leaky Bucket Counter设计模式时,我们可以用 一个error计数器来检测我们需要容错的系统单元。该计数器的初始值设定为0,当被测单 元发生一个错误时,error计数器的值加1;如果该被检测单元持续发生错误时,这个计数 器的值就会不断累计,当达到我们先前设定的一个阈值(Threshold)时,计数器就会向系 统发出警告信息。如果被检测单元在一个预设的时间段内没有新的错误发生,计数器会自 动将计数值减1。这个过程如同一个漏桶运作,因此我们称之为漏桶计数器“Leaky Bucket Counter”。
 
如果进行一下统计,我们就会知道,目前世界上为了解决系统Fault tolerance方面问 题的设计模式就超过了 30个,我们可以从PLoP’96和EuroPLoP’96大会所收录的大量论 文中看到部分设计模式。
 
3) Security
系统的安全机制,同样也是一个考评系统质量的重要方面。遗憾的是,现实中很多系 统的安全问题并没有得到很好的解决。这不但会使系统没有受到安全保障,甚至影响了其 他的系统质童。例如:缓慢的用户认证过程会影响系统的可用性、访问一个受控资源不合 理而复杂的认证过程会影响系统服务的性能等。
 
为了高质量地解决系统的安全问题,我们同样可以借鉴安全相关的“Architecture Principle”。通常我们可以分解为下述6个方面:
识别安全项:系统中哪些单元或资源是受控項。
 
安全要求:每个受控单元的安全要求。例如:Confidentiality、Integrity、Availability、 Accountability 等。
 
•成胁的种类:系统或受控单元可能受到什么类型的威胁。例如:Deception、 Disruption、Unauthorized Disclosure、 Usurpation 等。
 
成胁检测机制:针对受控单元所面临的威胁可以采用的相应检测机制。例如: Prevention、Detection、Response、Planning、Diligence、Mitigation 等。
 
安全应对机制:检测到威胁时,系统应该采取的相应应对手段。例如:Identification, Authorization、Deterrence、Accounting、Non-repudiation、Auditing 等0
 
•安全机制实现:具体落实各种威胁应对手段所采取的技术实现。例如:Encryption、 Scanners、Filters、Proxies、Logging、Passwords 等。
 
按照上述的“Architecture Principle”构建出来的系统安全架构,可以在很大程度上保 证整个系统架构范围内的安全性。设计人员在进行系统安全设计阶段,也可以按照上述的 安全“Architecture Principle”,结合可以利用的、超过50个安全相关的“Security Design Pattern”进行设计工作。设计人员可以采用一类设计模式来完成某一特定安全方面的问 题,例如:“Access Control”类型的模式、“Accounting”类型的模式、“Operating System Access Control”类型的模式等;他们也可以结合一些单个的设计模式来辅助工作,例如: “Account Lockout”、“Authentication Session”、“Client Input Fitters”、“Encrypted Storage” 等。
4) Resource Management
一个系统中合理的资源管理机制,在很大程度上会影响一个系统质量的许多方面,如 performance、scalability、availability、predictability、flexiility、stability、consistency等。所以在系统中采取切合实际的资源管理机制就显得非常重要。在整个系统架构层面, 我们通常会应用下述这样的“Architecture Principle”来帮助我们在系统最高层面解决资源 管理的问题。
•界定重用资源:哪些资源是系统可被重用的资源,哪些不是可重用资源。例如:内 存、CPU计算时间、某个object等。
 
资源访问频率:界定出来的资源被访问的频率。例如:一致被占用、频繁使用、很 少使用等。
 
•资源访问形式:资源是如何被访问和使用的。例如:每个系统资源只由某个特定用 户所使用,还是由多个用户共享使用等。
 
•资源的属性:资源自身的一些特定属性。例如:某个资源是stateful或者是stateless
 
资源获取/释放的成本:在获取使用该资源或释放资源使用权时的成本要求。例如: 时间成本要求等。
 
资源规模:资源的大小或范围。资源的规模决定了资源的管理原则。
资源间依赖:资源间有着怎样的依赖关系。资源间依赖也是管理原则必须考虑的重 要因素之一。
 
上述这些有关系统内资源的情况,很大程度上决定了构建我们系统架构中资源管理机 制的主要原则。但是,为了应对不同的系统资源情况,我们可以在战术上采取不同的设计。我们可以使用业界总结出来的超过20个经典的有关资源管理的“Resource Management Pattern” 模式。例如:Michael Kircher 的 “Lookup”、“Lazy Acquisition”、“Eager Acquisition”、“Partial Acquisition”、“Caching”、“Pooling”、“Coordinator、“Resource Lifecycle Manager”、“Leasing”、“Evictor” 等模式;Charles Weir 和 James Noble 也提 出过有关小内存系统的一系列设计模式等。
 
“Architecture Principle” 加上我们的战术手段 “ Resource Management Pattern ”,在
 
很大程度上,可以帮助我们提升系统内有关资源管理方面的系统质童问题。
2.系统演化质置
如果我们现在正构建一个家庭轿车车载电子系统平台(车载空调系统、车载电子系统、车载音响系统等),例如某公司帮助欧盟构建的AutoSar车载系统平台。这时我们或许会遇 到这样一个质童要求——“我们的车载电子平台必须适应未来使用不同的车载硬件,例如 不同的CPU”。这样的质量要求,就是我们通常谈到的系统演化所要求的质量要求。
 
限于篇幅,我们不可能针对每一个系统演化的质量要求进行详尽的探讨。所以,我们 以两个比较常见的质量要求为例进行分折。
 
1) Flexibility
 
为适应未来的变化,系统架构应该具有相当强的灵活性,以便于系统演化过程中能够 满足所需要的各种质量要求,例如系统的适应能力、系统的可维护能力、方便的功能演化 等。当然,过度的灵活也同样会降低系统未来的质量,使系统更难以配置和维护,这样系 统的适应能力也会受到影响。当我们进行系统架构构建时,必须采取适当的动作,使系统 具备恰当的灵活性。通常,我们会以下面这些“Architecture Principle”来帮助我们判断如何构建灵活的系统。
 
系统演化单元:界定哪些系统单元会在未来发生变化,粒度可以是子系统、系统层、 构件、class、服务、算法等。
 
演化类型:这些可能演化的单元会发生怎样的变化,例如:扩展、替换、剔除等。
 
演化频率:这些演化单元所发生变化的频率。针对不同的变化頻率,可以采用不同 的应对策略。
 
变化时刻:系统单元变化发生的时间,例如:开发时、编译时、连接时、启动时、
 
运行时等。
 
♦执行变化动作:未来变化的因素所造成的系统变化是由谁担负执行的动作,例如: 开发人员、服务人员、系统维护人员、用户,或者系统自己等。
在处理系统灵活性时,除了遵循上述的架构原则外,我们通常也会利用大量的 “Flexibility Pattern”架构和设计模式,配合系统在不同层面和方向上所要实现的灵活 性:
一些架构模式可以帮助我们将核心系统分割为不同的单元,从而避免系统变化所带 来的震撼会波及其他无关的系统单元上。这样的分割避免了不必要的变更工作,使 我们可以只专注于变化的单元,例如:“Model-View-Controller”架构模式、 **Presentation-Abstraction-Control架构模式、“Layer” 架构模式、“Pipes and Filters”架构模式等。
 
一些设计模式可以帮助我们将系统运行时的灵活性构建得非常经典,例如: “Component Configurator"设计模式可以让我们构建一个系统配置机制,在系统 运行时灵活地替换系统中不同的可配置单元,进行系统单元替換。
 
另外一些设计模式可以帮助我们方便地进行系统功能单元的自由扩展、变更,从而 实现系统自适应。例如:“Interceptor”设计模式、“Visitor”设计模式、“ Strategy” 设计模式、“Extension Object”设计模式、“Decorator”设计模式等。
 
除此之外’还有一些设计模式帮助我们将系统功能隐藏,从而分割开dient端对系 统功能的直接依輛。这样即使系统功能变化了,也不会波及client端。例如: **Extension Interface"设计模式' “Facade” 设计模式、“Adapter"设计模式、 “Mediator”设计模式、“Proxy”设计模式等。
2) Configuration
在实践中,一些系统项目或产品线系统经常会要求系统具有非常强的可配置能力,即 根据具体要求能够重新配置的能力。这可能包括重新配置和部署不同的系统构件、不同的 界面、不同的操作系统、不同的算法、不同的数据结构等,如图5-12所示。
 
构建这样一个系统架构,支持这样的系统可配置、可重新部署的能力,对我们来说确
实是一个很大的挑战:
•系统可配置单元往往不是随机组织在一起的,经常有着很多复杂的结构和语义上的 依赖关系。这为我们实现系统可配置增加了相当的复杂度。
系统可配置单元往往在配置安装到系统时,需要遵循非常严格的配置次序。 在配置过程中,必须确保这些可配置单元和系统其他单元的功能协同。
为了解决系统要求的可配置能力,我们通常可以遵循这样的“Architecture Principle” 如图5-13所示:
系统配置机制:构建一个全系统范围内的系统配置机制,从而使系统可以利用这样 一个配置管理机制进行统一的系统单元增加、替換、移出等配置和部署动作。
 
配置接口:构建一个配置接口(Configuration Interface),从而使所有的配置和部 署单元经由该配置接口注入到系统里。    
#参数传递:系统配置机制作为控制方和调度方,将可配置选项以参數传递的方式, 经由配置接口传递进系统中。
 
#系统整合:将系统内可配置的功能单元构建为可以接受由配置接口传递来的配置选 项参数作为配置项的系统接受单元,从而实现传递进来的配置项与系统整合协同的 功能。
为了实现上述“Architecture Principle”所讲述的几个方面,我们可以配合使用一些 “Configuration Pattern” 设计模式:    
为构建一个系统全局范围内的配置机制,我们可以考虑使用“Component Configurator”设计模式来构建一个应用配置管理中心。
 
•为构建一个配置接口( Configuration Interface),我们可以利用经典的“Extension Interface”设计模式来割裂配置接口与系统内各个可配置单元之间的耦合关系。
 
为实现系统内可配置单元的各种可配置单元选项,我们可以参考使用“Abstract Factory” 设计模式、“Builder” 设计模式、“Factory Method” 设计模式、“Disposal Method”设计模式等。
通过对实践中经常遇到的质量问题进行分析,我们可以淸楚地看到:为了解决系统所 要求的形形色色的质量要求,我们的战术动作通常包含了 “Architecture Principle”、“架构和设计模式”、“技术手段”等几个层面的实践经验和工具。一个系统的质量,其实就 是根据系统的质量要求和系统存在的环境,通过正确使用那些经典的实践手段来真正得 以保障。
 
1T5.3衡量系统架构的质量
 
正如 Frank Buschmann 说过的 “A quality architecture exposes most, if not all, of the quality of system一个高质童的系统架构,在很大程度上代表了该系统的质量。所以, 我们如果看到一个系统的架构在构建时充分考虑了系统所要求的质量,并且使用了适当的 手段来实现系统的质量要求。那么,当这个系统编码开发后投入运维时,系统的质量在一 定程度上是有保障的。所以,我们可以把系统架构的质量看做一个很好的出发点’用它来 判断整个系统的质量。
如何衡童系统架构的质量,我们不妨从如图5-14所示的5个方面入手:系统架构中所 使用的架构和设计模式的密度(Density)、系统架构的匀称性(Symmetry)、架构是否实用简洁(Simplicity)、架构的表述能力(Expressiveness)以及架构是否考虑了系统自适 应能力(Adaption)。我们将其简称为衡量系统架构质量的DESSA五维模型。.
1.架构和设计模式的密度(Density)
 
如果我们看到一个系统架构使用了高密度的架构和设计模式,这也就意味着该系统的 构建工作是基于前人验证、总结、提炼出来的实践经验之上的系统设计。在现实中,如果 熟悉J2EE或者消息中间件的人,就知道可以被我们借鉴的核心经验之一,就是J2EE架 构中高密度地使用了的15个设计模式和消息中间件中高密度地使用了专用的、超过30个 设计模式。
 
我们可以用一个“仓储管理系统”为例(虽然这不是一个什么大规模的复杂系统),来 看看一个髙密度的系统架构会给我们带来怎样的震撼。
 
图&15中绿色部分是仓储管理系统的“核心功能”部分,为了完成该核心功能,架构 人员使用了大量的设计模式,这包括“Composite and Chain of Responsibility”、| “Flyweight'“Proxy'“Iterator”、“Strategy'“Extension Interface'“Wrapper Facade “Active Object”、“Half Object plus Protocol”、“Observer” 等 10 个设计模式。
为了解决系统“并发”问题,系统使用了包括“Active Object”、“Proxy”、“Command”、 “Composite”、“Memento”、“Command Processor”、“Singleton”、“Strategy” 等 6 个
设计模式。
 
为了解决系统访问的“同步”问题,系统使用了包括“Active Object”、“Scoped Locking”、“Strategized Locking,,、“Thread-Safe Interface”、“Double-Checked Locking Optimization”等5个设计模式。
 
另外,系统架构并不是把这些设计模式作为一个个独立的单元进行堆积,而是按照在 系统中扮演角色的需要来使用设计模式。这使得整个系统架构中各角色切合得非常紧密, 很难区分出来使用了哪些单个的设计模式。这样以功能角色为设计出发点的设计,使每个设计模式互相紧密地结合在一起,甚至出现了类似于“AbstractStorage”这样一个class, 它其实是在6个不同的设计模式中扮演不同的角色,而这6个不同的角色却又巧妙地高度 融合在一个class中。
 
2.架构的匀称性(Symmetry)
 
什么是架构的匀称性?还是从两个我们都曾经遇到的情况入手,来看看客观的现实。 先来看看下面这段简单的代码片断:
 
从经典的角度上来看,大多数人可能会迷信参考使用经典的“(Objects for) State”设 计模式。但非常遗憾的是,在这4个与状态相关的解决办法中,最难以使用、复杂程度最 高的就是“(Objects for) State”设计模式。如果我们在设计中使用不好,带来的问题远比 解决“系统行为依赖于状态的变化”这个问题还要复杂得多。这就是运用复杂设计所带来 的额外复杂度。
 
分析不同的应用场景,洞悉系统自身的运行环境,应对不同的问题或系统需求,我们 其实应该尽量使用最简单的、够用的、能够实现要求的方案。这样的架构和设计就是最好 的设计!
 
5.自适应能力(Adaption)
 
有这样一个问题:在系统架构构建中,我们是否认真考虑过系统运行时会有哪些需要 系统自适应的行为?怎样设计才能保证这些系统自适应的行为能够正确满足我们的要求?
 
系统的自适应能力,是指系统内的构件可以自主协调协作和交互,以便于联合在一起 完成特定功能的能力。这样的能力不是用编程语言刻意规定或控制的行为,而是依靠架构 和设计来实现的动态的自主适应能力。这样的能力往往对提髙系统的Performance和 Scalability有着重要的作用。
 
举个现实中我们都能遇到的例子:我们的系统会有多个线程等待从一个接口传递过来 的不同事件。线程收到事件的触发,会执行相应的动作。这时问题就来了,系统在运行时, 我们无法控制事件到达的时间,我们也不知道哪个线程会第一个去从接口接受事件,哪个 线程又会成为下一个接受事件的线程。这明显是系统运行时不受控制的动态行为。
 
•方案1:我们可以考虑在系统架构和设计中增加一个中央线程决策控制或调度单元, 借此来协调多个线程的动作。当然,这需要用程序严格制定调度和控制的原则,并 用程序控制系统执行的动作。可是,这样的架构势必造成编程控制的中央单元会成 为系统中Performance和Sea丨ability的瓶颈。并且,如果该中控单元发生严重错误, 会造成整个系统的瘫痪。很明显,这是一个代价髙昂而安全性较低的办法。
 
•方案2:如图5-17所示,我们可以考虑在系统架构和设计中使用一个简单的线程队 列。最先进入线程队列的线程,成为了队列中线程的Leader,它可以优先取得事件 处理权。当系统收到事件时,Leader线程接受事件,变为处理状态的线程,当处理 完毕后,它就将自己的状态变为Follower,并把自己放进线程队列的末尾。当Leader 线程变为处理状态时,它后面的线程自动变为线程队列中下一个Leader,等待系统 传来的下一个事件。我们可以看到,这样周而复始的循环动作,并没有刻意用程序 制定哪个线程应该去获取事件,而是依靠系统自主适应能力所产生的动态行为,这 就是著名的“Leader/Follower”设计模式的应用。
 
 
 
 
 
 
 
posted @ 2019-12-05 22:10  mongotea  阅读(585)  评论(0编辑  收藏  举报