IBM Well-Architected Framework

一个带有白色底座的紫色 3D 柱体,侧面有一个圆形标志
概述

性能支柱专注于设计、开发、验证和运维满足解决方案性能非功能性需求的解决方案,这些非功能性需求通常与响应时间、容量(支持的负载规模、用户基数和实现的吞吐量)以及可扩展性(有机地适应变化需求和不断增长的负载的功能)相关。与由固定容量基础设施组成的“传统”计算环境不同,混合云环境使解决方案能够随着需求的涨落动态地扩展和缩减其容量和资源消耗——前提是解决方案的架构能够利用这些功能。

性能分析还可以通过基于证据的产品设计改进来提升用户体验,并通过内置的可扩展性和容量来实现业务目标。

原则

在产品定义阶段收集用户期望,量化业务需求,并将其作为后续产品架构和设计的基础

架构完善的解决方案中的组件可以独立扩展,例如添加服务的另一个实例,但添加或移除组件可能会对解决方案的其他部分产生连锁反应;例如,添加另一个 Web 服务器以应对网络流量高峰,可能需要更多消息队列来与后端服务通信。事先了解扩展依赖关系有助于理解解决方案的运行行为,并避免因单个资源过度扩展而导致资源耗尽

架构完善的混合云解决方案利用多平台架构来制定扩展和突发策略,以优化性能以及安全性、运营成本和最终用户期望;例如,在具有强性能保证和固定运营成本的本地基础设施上运行工作负载,并在工作负载高峰期间突发/扩展到公有云服务提供商。

移动数据成本高昂。架构完善的解决方案利用容器化工作负载的可移植性和移动性,将服务尽可能靠近它们所消费的数据。

解决方案必须选择合适的平台和资源,才能最大化其架构的价值。混合云解决方案能够跨越多个云(包括本地基础设施),这赋予了架构师选择最佳资源组合以满足其解决方案性能需求的自由。

解决方案设计实践

性能必须在解决方案设计之初就“内置”其中。将性能考虑留到解决方案设计结束时,或者更糟的是在实施阶段才考虑,往往会导致性能欠佳,且若不重新审视解决方案架构的绝大部分内容,这些问题便无法修复。解决方案设计实践帮助架构师创建高性能的解决方案,并避免采用可能限制解决方案性能的设计方法。

解决方案的设计应通过添加或移除离散单元(服务器、服务、网络接口等)来增加或减少其处理功能,而不是通过改变现有单元的功能(例如,为服务器添加更多 CPU)。为实现此目标,解决方案应采用以下架构原则:

  • 无状态组件指在交互之间不保留客户端或会话状态(例如用户身份,或先前调用提供的数据输入)的组件,消除了客户端与任何特定组件实例之间的依赖关系。这种组件与其消费者之间无依赖关系意味着解决方案可以通过添加或移除组件实例来扩展和缩减,而不会影响组件服务的消费者。杂货店的收银员是一个很好的无状态组件示例:只要至少有一名收银员在岗,顾客就能结账,而收银员数量可根据客流量的多少进行增减。与此相反的情况是,如果购物者在购物开始时就被分配了一个特定的收银员。如果被分配的收银员陷入困境,或者更糟,无法提供服务,购物者就必须等待或重新开始,杂货店的整体性能(以每小时服务的购物者数量衡量)就会受到影响。

  • 避免长时间运行的任务如果解决方案必须支持长时间运行的任务(例如执行复杂的科学计算),则应将任务设计为通过中断和检查点机制来支持扩展或缩减,使得解决方案能够在资源被添加或移除时关闭和重新启动任务。

  • 数据在两端理论上,无状态组件可以无限扩展,并且可以在客户端之间重用。高性能解决方案将状态(即用户和应用程序数据)推送到解决方案架构两端的客户端应用程序和数据库中,并在中间的架构层中不保留任何状态。

资源:
有状态与无状态

将解决方案设计为一组高度内聚、松散耦合的组件,使得组件能够根据其提供的服务需求独立扩展。面向服务的架构微服务等架构方法将这种实践作为核心设计原则,即一组高度内聚的服务通过高级别的、松散耦合的 API 进行通信。

在解决方案的组件之间移动数据通常是事务中最耗时的部分。组件设计应针对可用带宽优化通信的频率和数据量。例如,一个重复调用从数据库中检索单个值的应用程序,在本地网络上部署时可能表现得“足够好”,但当数据库组件重新定位到云服务提供商时,可能会产生延迟。

基于 Web 的应用程序中常用的表述性状态传递架构风格,很好地体现了架构完善的解决方案所展现的平衡类型;资源的完整表述状态以 JSON、XML 或其他文档的形式传输,这些文档在传输的信息量与基于 Web 交互的高延迟之间取得了平衡。

缓存有助于限制对生产数据的资源和服务的需求。考虑对生命周期长、相对静态的数据和/或生产成本“高昂”的数据使用缓存。架构完善的解决方案在解决方案架构的所有层面实施缓存机制;将缓存逻辑上尽可能靠近消费者放置,限制消费者与缓存之间的通信,并改善整体响应时间。

架构师需谨记,缓存不可过度使用。设计不佳的缓存机制或过大的缓存可能会对解决方案的整体性能产生不利影响。架构师应评估缓存类型和策略,然后在性能测试和分析期间衡量缓存有效性。

使用消息队列、回调模型或其他方式的异步消息传递,使解决方案既能有效扩展,也能在资源耗尽时优雅地降级。架构完善的解决方案利用异步通信,特别是消息队列,为最终用户提供响应迅速的体验,并避免因组件故障而“丢失”用户请求。同样的机制也可用于互连具有不同服务级别或运行时间的系统;例如,一个全天候运行的 Web 应用通过消息队列连接到一个仅在办公时间运行的系统,这使得该 Web 应用即使在系统不可用时也能接受最终用户的请求。

解决方案随时间变化,其性能也可能随之改变。内置性能仪表化,允许开发、测试和运维团队非侵入式地收集应用的性能指标,有助于使用证据驱动的方法开发和测试稳健的产品。这种仪表化也有助于功能测试、缺陷分析,并且是维护解决方案性能、帮助查明生产环境中性能问题根源的宝贵工具。可配置且非侵入式的仪表化支持产品监控,确保解决方案在运维中的可观测性,从而支持 DevOps 和 SRE 团队。

规划与测试实践

性能规划、测试和分析是一系列应用于 IT 解决方案的实践和方法,旨在确保解决方案的质量和实现预期业务成果的功能。

通常,此类分析应用于解决方案的性能、容量、可扩展性等质量属性,以及可用性、业务连续性和可持续性的某些方面。分析包括识别和量化与质量相关的业务需求,设计并执行测试以获取特定指标,这些指标反映解决方案在响应时间、吞吐量或支持负载等方面相对于一组期望值的表现。

此外,在更广泛的意义上,性能范围包括分析解决方案的容量、解决方案可服务的总工作量单位、其可扩展性(应对需求变化的良好程度)。性能分析也用于证明产品在极端操作条件下保持功能正常和稳定。预期性能分析的目标不仅仅是捕获解决方案的性能状况,还要查明瓶颈,并与利益相关者协作以改进解决方案的质量和可用性。

考虑到产品性能和容量管理的复杂性和整体性,它应贯穿软件开发生命周期的不同阶段,从产品设计到运维支持和 SRE。这确保了妥善的客户需求管理、早期问题发现以及对生产事件的快速响应。

从业务角度来看,解决方案的整体性能很重要,这必须体现在整体的非功能性需求中。然而,对于单元级性能测试、实施左移范式的早期性能测试以及性能问题的根本原因分析,可能需要指定低层级需求,限制单个调用的持续时间、网络延迟等。

因此,高层级的性能要求通常在流程或事务层面提供,例如“贷款发起流程应在2分钟内完成”,而不考虑单个流程步骤和子流程的性能如何促成最终结果。在开发阶段创建一个性能预算,为流程内的每个步骤分配目标,为特性开发团队提供可衡量的目标,有助于识别潜在问题区域,并有助于将性能优化和修复工作集中在最有益的地方。

性能预算应涵盖解决方案从硬件到应用程序代码的所有层面。遗漏其中任何一个层面,都可能导致解决方案无法满足用户期望。

测试性能时,实践出真知,也就是说,只有通过测试端到端的解决方案,我们才能确信它满足要求。这反映了性能分析的整体性。测试单个组件(例如数据库、中间件等)为解决方案的性能分析提供了宝贵的见解,并有助于查明性能瓶颈。但仅靠组件测试是不够的,因为组件之间的交互可能导致意外的瓶颈或其他障碍,从而产生次优结果。

关注用户感知意味着主要关注感知响应时间和用户界面的整体响应功能。容量下降通常在影响产品性能之前对用户是不可见的。1968 年的一项研究发现,人机交互存在三个不同的数量级:

  • 100 毫秒的响应时间被认为是瞬时的。人类的平均反应时间为 250 毫秒,因此低于此值的任何时间都被认为非常快/瞬时。
  • 1 秒或更短的响应时间通常足够快,让用户感觉没有被系统性能拖慢。
  • 超过 10 秒的响应时间会导致用户完全失去注意力。

由此推断,2 秒的响应时间是理想的,因此,在可能的情况下,对于混合云解决方案来说,2 秒或略多一点是一个良好的响应时间目标。当然,用户的期望取决于他们正在做什么,例如,没有人期望按钮按下动画持续 2 秒,但对于面向用户的应用程序来说,2-3 秒是一个良好的通用目标。

根据这一原则,解决方案应通过使用用户界面测试工具,将用户响应时间测试作为其质量保证周期的一部分。就像 API 调用延迟对整体产品性能很重要一样,用户感知的性能是吸引和留住用户群的关键。

用户通常对何为“良好”性能持有不同期望。例如,一个每天多次使用应用程序的“高级用户”,与一个可能每月才使用一次相同应用程序的人,其性能期望截然不同。用户也常常难以说清,什么样的性能对他们来说才算“好”,往往停留在诸如“足够快”(这是一个很难达成的目标)这样的要求上。此外,不同的人对面向用户的产品性能的个人感知也不同。例如,对某些人来说,10 秒的登录时间是可以接受的(尤其是如果这是一个单次事件),而对另一些人来说,这可能太慢了(尤其是如果登录是工作流程中的常见部分)。

为了帮助量化和管理用户期望,建议:

  •  在充分了解用户群及其典型应用使用模式的基础上,创建非功能性需求。

  •  在解决方案设计周期早期联系用户,了解他们经常使用哪些功能并期望高响应性,以及哪些功能使用频率较低,因此可以容忍较慢的响应时间

  • 使用百分位数来定义平均或中位数响应时间阈值。这允许产品响应性存在一些不可避免的随机变化,并确保少数异常值不会导致整个产品验收失败。

  •  在早期解决方案发布和预览中,包含切合实际的响应时间测试和反馈。将性能测试留到解决方案开发结束时进行,有时会导致团队在解决性能问题时,不得不“撤销”解决方案架构的很大一部分。在开发周期后期修复性能问题成本高昂。

  • 确保 UI 设计包含加载指示器和状态栏等元素,以便用户了解应用程序正在运行和处理中。这有助于避免因产品感知性能缓慢而产生不必要的挫败感。

  • 必要时,对类似的和“最佳”解决方案进行研究,分析行业趋势和出版物,并采访主题专家,制定适当的响应时间和容量目标。

监控性能边界并向客户传达。

产品或解决方案组件的误用或错误配置可能是导致性能不佳和负面用户体验的原因。为避免这种情况,架构师应:

  • 了解解决方案中的性能约束,并主动将其传达给用户。例如,如果解决方案使用缓慢/低带宽的通信通道,架构师应让最终用户知道下载高分辨率图像会受到影响。

  • 在可能的情况下,使系统能够在请求超出解决方案设计参数时进行检测和通信。例如,当用户尝试通过慢速通道下载大文件时,系统主动发出警告。

一种常见的性能测试方法是测试解决方案在预期的最大负载下是否满足其响应时间目标;其假设是,如果解决方案在最大预期负载下表现良好,那么对于低于该负载的情况也应是良好的。这种方法面临的挑战是,每个测试的峰值负载只提供一个数据点,几乎像是一次通过/失败练习。

一个架构完善的解决方案采用探索性方法进行测试,以了解其在不同规模负载、不同用户类型组合以及不同测试功能组合下的响应功能。这为解决方案团队提供了宝贵的多维度信息,帮助他们了解解决方案组件如何交互以影响性能、潜在的瓶颈,以及如何扩展解决方案以应对更少或更多的工作负载。

这种方法可以避免在预期负载目标发生变化以及需要在不同负载条件下收集性能指标时进行额外的测试。通过对性能与负载大小之间现有依赖关系进行内推/外推,可以计算初始测试负载范围内(从零到临界点大小及以上)任何负载下的性能指标。

典型的性能测试方法遵循简化的模式,即“测量给定负载/吞吐量下的响应时间”。这种方法回答了关键事务在峰值负载下的响应时间是否满足现有 SLA 的问题。并且,通常测试的负载量级仅限于“低负载”、“峰值负载”和“压力负载”。这种方法可以回答典型负载下响应时间的问题,但它不能提供系统在所有可能运行情况下的完整性能图像。

更高级的“探索性性能测试”方法旨在创建被测解决方案的“性能快照”。该快照包括在最广泛支持的负载范围内收集的一套全面的性能指标——从单用户测量到过载点后的负载(如果可能,且系统未崩溃的情况下)。这包括收集事务响应时间、事务和数据吞吐量以及在递增负载条件下收集的资源消耗数据。这里所说的“事务”是指系统完成的有限工作单元——从宏观事务(如登录或更新账户)到单个子事务(如登录事务中的身份验证调用),或简单的 HTTP 请求。

这套全面的性能数据,即性能快照,包括以下负载范围内的上述性能指标:低负载“线性”负载范围(其中独立处理的线程感觉不到彼此的存在,响应时间不随负载增加而增加),“非线性”范围(响应时间随负载增加而增长),饱和点(吞吐量停止随负载增加而增加,并达到饱和水平),以及过载后范围(吞吐量达到最大值后性能下降,响应时间超过 SLA 水平)。

覆盖完整的负载范围通常不会比仅仅测试“峰值”和“压力”量级以及进行耐力测试给测试团队带来更多的工作量,因为使用的是相同的测试脚本(主要工作量通常在于创建这些脚本)。但创建此类性能快照的优势如下:

  • 无需花费时间和精力去猜测能产生“正确”事务吞吐量(“峰值”或“压力”)的测试设置,只需递增负载,即可覆盖所有支持的负载量级和吞吐量。

  • 可以确定响应时间开始增长时的负载、超过 SLA 水平时的负载以及吞吐量达到最大水平时的负载。

  • 这使得能够直接测量系统的容量,例如达到临界点条件时的负载量级(响应时间超过 SLA,或吞吐量达到最大水平,或系统资源使用进入指定的“红色区域”,例如 CPU使用率达到 90%,或系统崩溃——以先发生的为准)。这意味着无需使用典型的基于猜测的方法来进行容量分析和规划。

  • 如果生产运行的预期条件发生变化(例如业务重新定义了预期的平均和峰值负载),则无需重新运行性能测试:可以通过对现有测试结果进行内推或外推,直接获得不同负载量级下所需的性能指标。

  • 覆盖所有负载范围,而不是少数预定义的量级,可以确保我们对系统性能有一个全面的了解,并且不会因为未在极端条件下测试产品而遗漏任何可能的性能问题。

  • 确保测试包含触及系统性能临界点,意味着我们了解性能瓶颈所在,知道随着负载增加,哪个链路、组件或层会最先失效。这使得能够向架构和设计团队提供有依据的、基于证据的反馈,以改进产品的稳健性和性能。

可以对解决方案运行多种类型的性能测试。一个架构完善的解决方案会充分利用所有这些测试。

  • 手动基准测试: 顾名思义,即手动运行解决方案功能,亲身体验解决方案对用户的响应情况。
  • 校准测试: 进行此类测试是为了将自动化测试工具的结果与其他来源(如手动测试或内置性能指标)进行比较,验证测试脚本和测试工具结果的正确性。
  •  浸泡测试(或耐力测试):在负载下长时间测试解决方案,确保解决方案在负载下保持稳定,不会随时间推移而性能下降,可以可靠运行,并表现出预期的资源消耗(即没有内存泄漏)。
  • 峰值测试: 在预期的峰值工作负载下测试解决方案,例如一年中最繁忙的一天,确保解决方案的稳定性,并收集关键指标,如在最大预期负载下的响应时间和资源消耗。
  • 压力和突发测试: 用于在短时间内以预期峰值负载的倍数(例如 2 倍或 3 倍)测试解决方案,或在极短时间内以更高的负载测试解决方案。这些测试有助于识别解决方案内的瓶颈,并帮助解决方案团队了解解决方案的扩展依赖关系。
  • 可变负载测试(或 临界点测试): 用于在一定范围的负载下测试解决方案,了解解决方案在不同负载量级下的性能,并帮助解决方案团队发现趋势和资源限制。这些测试还可以记录产品的临界点,测量解决方案容量,并检测产品中最薄弱的组件。

力求最大化利用测试中获得的各种性能数据:

  • 运用创造性的、探索性的方法来分析测试结果。使用*假设*场景分析来探索额外的场景。
  • 将性能结果映射到不同规模和架构的环境,预测解决方案在不同平台和部署中的性能。
  • 通过检测意外的模式和趋势(例如,负载增加导致事务处理率降低),捕捉所获性能数据中的任何不一致之处。
  • 使用建模技术,基于假定的使用模式,将测量到的系统容量与相应的用户群规模联系起来。
  • 始终记得评估可在生产中并发执行的不同工作流之间的相互性能影响。
  • 使用不同来源的性能数据:从试点项目到单元测试和功能测试,再到 DevOps 和 SRE 的黄金信号。任何数据在性能分析中都具有价值。
  • 更频繁地与利益相关者分享分析结果。这通常有助于从他人那里获得有用的输入,提高对性能测试和分析进展的认识,帮助他人更好地理解性能和容量分析这样的技术领域,并提高整体非功能性验证工作的可见性。
后续步骤