你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

Azure 上任务关键型工作负荷的应用程序设计

设计应用程序时,功能和非功能性应用程序要求都至关重要。 此设计区域介绍体系结构模式和缩放策略,可帮助应用程序复原故障。

重要

本文是 Azure 架构良好的框架任务关键型工作负荷系列的一部分。 如果不熟悉此系列,建议从什么是任务关键型工作负荷开始

缩放单元体系结构

解决方案的所有功能方面都必须能够进行缩放以满足需求的变化,理想 情况下是自动缩放 以响应负载。 建议使用缩放单元体系结构通过隔离来优化端到端可伸缩性,并标准化添加和删除容量的过程。 缩放单元是可以独立缩放的逻辑单元或函数。 单元可以由代码组件、应用程序托管平台、 涵盖相关组件的部署标记 ,甚至由支持多租户要求的订阅组成。

建议使用此方法,因为它解决了单个资源和整个应用程序的规模限制。 它有助于处理复杂的部署和更新方案,因为缩放单元可以部署为一个单元。 此外,可以在将用户流量定向到单元之前,在单元中测试和验证特定版本的组件。

假设任务关键型应用程序是联机产品目录。 它具有用于处理产品评论和分级的用户流。 流使用 API 来检索和发布注释和评分,并支持 OAuth 终结点、数据存储和消息队列等组件。 无状态 API 终结点表示必须适应按需更改的精细功能单元。 基础应用程序平台还必须能够相应地缩放。 为了避免性能瓶颈,下游组件和依赖项还必须适当地缩放。 它们可以独立缩放,作为单独的缩放单元,也可以作为单个逻辑单元的一部分一起缩放。

示例缩放单位

下图显示了缩放单元的可能范围。 范围从微服务 Pod 到群集节点和区域部署标记不等。

显示缩放单元的多个范围的关系图。

设计注意事项

  • 范围。 缩放单元的范围、缩放单元与其组件之间的关系应根据容量模型定义。 考虑性能的非功能要求。

  • 缩放限制Azure 订阅规模限制和配额 可能会影响应用程序设计、技术选择和缩放单元的定义。 缩放单元可帮助你绕过服务的缩放限制。 例如,如果一个单元中的 AKS 群集只能有 1,000 个节点,则可以使用两个单元将限制增加到 2,000 个节点。

  • 预期负载。 使用每个用户流的请求数、预期的峰值请求速率(每秒请求数)和每日/每周/季节性流量模式,以通知核心规模要求。 还要考虑流量和数据量的预期增长模式。

  • 可接受的性能下降。 确定负载下是否可以接受具有高响应时间的降级服务。 对所需容量进行建模时,负载下解决方案所需的性能是一个关键因素。

  • 非功能性要求。 技术和业务方案具有复原能力、可用性、延迟、容量和可观测性的不同注意事项。 在关键端到端用户流上下文中分析这些要求。 在用户流级别,在设计、决策和技术选择方面具有相对的灵活性。

设计建议

  • 定义缩放单元的范围和将触发单元缩放的限制。

  • 确保所有应用程序组件都可以独立缩放,也可以作为包含其他相关组件的缩放单元的一部分进行缩放。

  • 根据容量模型和非功能要求定义缩放单元之间的关系。

  • 定义区域部署标记,以将区域应用程序资源的预配、管理和操作统一到异性但相互依赖的规模单元中。 随着负载的增加,可以在同一 Azure 区域或不同区域中部署额外的标记,以水平缩放解决方案。

  • 使用 Azure 订阅作为缩放单元,以便单个订阅中的缩放限制不会限制可伸缩性。 此方法适用于具有大量流量的大规模应用程序方案。

  • 围绕已识别的流量模式建模所需的容量,以确保在高峰时间预配足够的容量,以防止服务降级。 或者,在非高峰时段优化容量。

  • 测量进行横向扩展和横向扩展操作所需的时间,以确保流量的自然变化不会造成不可接受的服务降级级别。 跟踪缩放操作持续时间作为操作指标。

注意

在 Azure 登陆区域中部署时,请确保登陆区域订阅专用于应用程序,以提供明确的管理边界,并避免 干扰邻居反模式

全球分布

无法避免在任何高度分布式环境中发生故障。 本部分提供缓解许多故障方案的策略。 应用程序必须能够承受区域和区域故障。 它必须部署在活动/活动模型中,以便负载在所有区域之间分布。

观看此视频,大致了解如何规划任务关键型应用程序中的故障并最大程度地提高复原能力:

设计注意事项

  • 冗余。 应用程序必须部署到多个区域。 此外,在区域中,我们强烈建议使用 可用性区域 来允许数据中心级别的容错。 可用性区域在可用性区域之间延迟外围小于 2 毫秒。 对于跨区域“聊天”的工作负荷,此延迟可能会对跨区域数据传输造成性能损失。

  • 活动/活动模型。 建议使用主动/主动部署策略,因为它可最大程度地提高可用性并提供更高的复合服务级别协议(SLA)。 但是,对于许多应用程序方案,它可能会带来有关数据同步和一致性的挑战。 在考虑增加成本和工程工作量的权衡的同时,解决数据平台级别的挑战。

    跨多个云提供商的活动/主动部署是一种可能缓解单个云提供商中全局资源的依赖关系的方法。 但是,多云主动/主动部署策略在 CI/CD 周围引入了大量复杂性。 此外,鉴于云提供商之间的资源规范和功能差异,需要为每个云创建专用部署标记。

  • 地理分布。 工作负荷可能具有地理数据驻留、数据保护和数据保留的符合性要求。 考虑数据必须驻留的特定区域,还是需要部署资源的特定区域。

  • 请求源。 用户或依赖系统的地理邻近度和密度应告知有关全局分布的设计决策。

  • 连接。 用户或外部系统访问工作负荷的方式会影响你的设计。 请考虑应用程序是否通过公共 Internet 或专用网络提供,这些网络使用 VPN 或 Azure ExpressRoute 线路。

有关平台级别的设计建议和配置选项,请参阅 应用程序平台:全局分发

松散耦合事件驱动的体系结构

耦合 可以通过定义完善的接口实现服务间通信。 松散耦合允许应用程序组件独立运行。 微服务体系结构样式与任务关键型要求一致。 它通过防止级联故障来促进高可用性。

对于松散耦合,我们强烈建议你合并 事件驱动设计。 通过中介进行异步消息处理可以生成复原能力。

说明异步事件驱动通信的关系图。

在某些情况下,应用程序可以结合松散耦合和紧密耦合,具体取决于业务目标。

设计注意事项

  • 运行时依赖项。 不应限制松散耦合服务使用相同的计算平台、编程语言、运行时或操作系统。

  • 缩放。 服务应能够独立缩放。 优化基础结构和平台资源的使用。

  • 容错。 故障应单独处理,不应影响客户端事务。

  • 事务完整性。 考虑在单独的服务中发生的数据创建和持久性的影响。

  • 分布式跟踪。 端到端跟踪可能需要复杂的业务流程。

设计建议

  • 将微服务边界与关键用户流保持一致。

  • 尽可能使用事件驱动的异步通信来支持可持续的规模和最佳性能。

  • 使用“发件箱”和“事务会话”等模式保证一致性, 以便正确处理每条消息。

示例:事件驱动方法

Mission-Critical Online 参考实现使用微服务来处理单个业务事务。 它使用消息代理和辅助角色异步应用写入操作。 读取操作是同步的,结果直接返回到调用方。

显示事件驱动的通信的关系图。

应用程序代码中的复原模式和错误处理

任务关键型应用程序必须设计为具有复原能力,以便尽可能多地解决故障方案。 这种复原能力可最大程度地提高服务可用性和可靠性。 应用程序应具有自我修复功能,可以使用设计模式(如使用回退断路器重试)来实现。

对于无法在应用程序逻辑中完全缓解的非暂时性故障,运行状况模型和操作包装器需要采取纠正措施。 应用程序代码必须包含适当的检测和日志记录,以通知运行状况模型,并根据需要促进后续故障排除或根本原因分析。 需要实现 分布式跟踪 ,以便向调用方提供全面的错误消息,其中包括发生故障时的相关 ID。

Application Insights工具可帮助你查询、关联和可视化应用程序跟踪。

设计注意事项

  • 正确的配置。 暂时性问题导致级联故障并不少见。 例如,在没有适当的退让的情况下重试会加剧服务被限制时的问题。 可以线性地空间重试延迟,或者通过不断增加的延迟以指数方式增加延迟。

  • 运行状况终结点。 可以使用外部解决方案轮询以检索应用程序组件运行状况状态的运行状况终结点在应用程序代码中公开功能检查。

设计建议

下面是一些 适用于可复原应用程序的常见软件工程模式

模式 总结
基于队列的负载调控 引入使用者与请求的资源之间的缓冲区,以确保负载级别一致。 当使用者请求排队时,工作进程会按照辅助角色设置的速度处理请求的资源,以及请求的资源处理请求的能力。 如果使用者需要回复其请求,则需要实现单独的响应机制。 应用优先顺序,以便首先执行最重要的活动。
断路器 通过等待恢复或快速拒绝请求来提供稳定性,而不是在等待不可用的远程服务或资源时阻止请求。 此模式还处理在连接到远程服务或资源时可能需要一段时间才能恢复的故障。
隔层 尝试根据负载和可用性要求将服务实例分区到组中,隔离服务功能维持故障。
一系列事件 通过确保服务通过定义的事件或消息通道相互更新,管理具有独立数据存储的微服务之间的数据一致性。 每个服务执行本地事务来更新其自己的状态,并发布事件以触发 saga 中的下一个本地事务。 如果服务更新失败,则 saga 将运行补偿事务来抵消之前的服务更新步骤。 单个服务更新步骤本身可以实现复原模式,例如重试。
运行状况终结点监视 在应用程序中实现外部工具可以定期通过公开的终结点访问的功能检查。 可以使用关键操作指标来解释终结点的响应,以通知应用程序运行状况并触发操作响应,例如引发警报或执行补偿回滚部署。
重试 以优雅透明的方式处理暂时性故障。
- 如果故障不太可能是暂时性的,并且如果再次尝试操作,则不太可能成功。
- 如果故障异常或罕见,则重试,如果再次尝试,则操作可能会成功。
- 如果故障由可能需要短时间恢复的条件(例如网络连接或高负载故障)导致,请在延迟后重试。 应用适当的退让策略,因为重试延迟增加。
限制 控制应用程序组件使用的资源消耗,防止资源过度占用。 当资源达到负载阈值时,它会延迟优先级较低的操作并降级非基本功能,以便基本功能可以继续,直到有足够的资源可供返回到正常操作。

下面是一些其他建议:

  • 使用供应商提供的 SDK(如 Azure SDK)连接到依赖服务。 使用内置复原功能,而不是实现自定义功能。

  • 在重试失败的依赖项调用时应用适当的退避策略,以避免自造成 DDoS 方案。

  • 为所有应用程序微服务团队定义通用工程条件,以在使用应用程序级复原模式时提高一致性和速度。

  • 使用经过验证的标准化包(如 适用于 C# 的 Polly用于 Java 的 Sentinel )实现复原模式。

  • 对所有跟踪事件和日志消息使用相关 ID 将其链接到给定的请求。 将所有调用(而不仅仅是失败的请求)的相关 ID 返回给调用方。

  • 对所有日志消息使用结构化日志记录。 为应用程序跟踪、指标和日志选择统一的操作数据接收器,使操作员能够轻松调试问题。 有关详细信息,请参阅 收集、聚合和存储云应用程序的监视数据。

  • 确保操作数据与业务要求一起使用,以通知 应用程序运行状况模型

编程语言选择

选择正确的编程语言和框架非常重要。 这些决策通常由组织中的技能集或标准化技术驱动。 但是,评估各种语言和框架的性能、复原能力和整体功能至关重要。

设计注意事项

  • 开发工具包功能。 Azure 服务 SDK 以各种语言提供的功能存在差异。 这些差异可能会影响你选择的 Azure 服务或编程语言。 例如,如果 Azure Cosmos DB 是可行的选项,则 Go 可能不是适当的开发语言,因为没有第一方 SDK。

  • 功能更新。 请考虑使用所选语言的新功能更新 SDK 的频率。 常用 SDK(如 .NET 和 Java 库)经常更新。 其他语言的其他 SDK 或 SDK 可能会更新频率较低。

  • 多种编程语言或框架。 可以使用多种技术来支持各种复合工作负荷。 但是,应避免蔓延,因为它引入了管理复杂性和操作挑战。

  • 计算选项。 旧版或专有软件可能无法在 PaaS 服务中运行。 此外,可能无法在容器中包含旧软件或专有软件。

设计建议

  • 评估所有相关的 Azure SDK,了解所需的功能和所选编程语言。 验证是否与非功能要求保持一致。

  • 优化微服务级别的编程语言和框架的选择。 根据需要使用多种技术。

  • 确定 .NET SDK 的优先级以优化可靠性和性能。 .NET Azure SDK 通常提供更多功能,并且经常更新。

下一步

查看应用程序平台的注意事项。