【韧性设计模式】韧性设计模式:重试、回退、超时、断路器

2023-11-03

什么是韧性?


软件本身并不是目的:它支持您的业务流程并使客户满意。如果软件没有在生产中运行,它就无法产生价值。然而,生产性软件也必须是正确的、可靠的和可用的。

当谈到软件设计中的弹性时,主要目标是构建健壮的组件,这些组件既可以容忍其范围内的故障,也可以容忍它们所依赖的其他组件的故障。虽然自动故障转移或冗余等技术可以使组件具有容错性,但如今几乎每个系统都是分布式的。即使是一个简单的 Web 应用程序也可以包含 Web 服务器、数据库、防火墙、代理、负载平衡器和缓存服务器。此外,网络基础设施本身由许多组件组成,因此总是会在某处发生故障。

除了完全失败的情况外,服务也可能需要更长的时间来响应。实际上,尽管他们的响应格式是正确的,但他们甚至可能以错误的方式回答语义。同样,系统拥有的组件越多,发生故障的可能性就越大。

可用性通常被认为是一个重要的质量属性。它表示一个组件实际可用的时间量,与该组件应该可用的时间量相比。可以用以下公式表示:

9e8c5aff2da19257a4e9a2f5bf0c6c87.png

传统方法旨在增加正常运行时间,而现代方法旨在减少恢复时间,从而减少停机时间。这很有用,因为它允许我们处理故障,而不是不惜一切代价阻止它们,并且在它们发生时长时间不可用。Uwe Friedrichsen 将弹性设计模式分为四类:松散耦合、隔离、延迟控制和监督(Loose coupling, isolation, latency control, and supervision)。

6c07f46727c9c67604d64ddb1a1c8948.jpeg

在这篇博文中,我们想看看延迟控制类别中的四种模式:重试、回退、超时和断路器。在理论介绍之后,我们将看到如何使用 Eclipse Vert.x 在实践中应用这些模式。我们将通过讨论替代实现并总结调查结果来结束这篇文章。

模式


示例场景


为了说明模式的功能,我们将使用一个非常简单的示例用例。想象一下作为购物平台一部分的支付服务。当客户想要付款时,支付服务应确保没有欺诈意图。为此,它要求提供欺诈检查服务。

在这种情况下,我们的服务提供基于 HTTP 的接口。为了检查交易,支付服务向欺诈检查服务发送 HTTP 请求。如果一切正常,将会有一个 200 响应,其中的布尔值指示交易是否是欺诈性的。但是,如果欺诈检查服务没有回答怎么办?如果它返回内部服务器错误(500)怎么办?

aa8d6f05d7a2f43e6cb67de4f17e3723.png

现在让我们看一下解决可能的通信问题的四种具体模式。虽然这是一个具体示例,但您可以想象任何其他涉及通过不可靠信道与不可靠服务进行通信的星座。

重试


每当我们假设可以通过再次发送请求来修复意外响应(或没有响应)时,使用重试模式会有所帮助。这是一种非常简单的模式,失败的请求会在失败的情况下重试可配置的次数,然后才会将操作标记为失败。

下面的动画说明了支付服务试图发出欺诈支票。由于欺诈检查服务中的内部服务器错误,第一个请求失败。支付服务重试请求并收到交易不是欺诈的答案。

f0584ba6028c3e67e6d8e63ac7d8b64f.gif

重试在以下情况下很有用

  • 丢包等临时网络问题

  • 目标服务的内部错误,例如由数据库中断引起

  • 由于对目标服务的大量请求而没有响应或响应缓慢

但是请记住,如果问题是由目标服务过载引起的,重试可能会使这些问题变得更糟。为避免将弹性模式转变为拒绝服务攻击,可以将重试与其他技术结合使用,例如指数退避或断路器(见下文)。

倒退(Fallback)


回退模式使您的服务能够在对另一个服务的请求失败的情况下继续执行。我们不会因为缺少响应而中止计算,而是填写一个备用值。

下面的动画再次描绘了支付服务向欺诈检查服务发出请求。同样,欺诈检查服务返回内部服务器错误。然而,这一次,我们有一个备用方案,它假设交易不是欺诈性的。

db4d506ff86d21c4e7541541c45b472f.gif

备用值并不总是可行的,但如果小心使用,可以大大提高您的整体弹性。在上面的示例中,如果欺诈检查服务不可用,则回退到将交易视为非欺诈可能是危险的。它甚至为试图首先向服务发送垃圾邮件然后进行欺诈交易的欺诈交易打开了攻击面。

另一方面,如果后备是假设每笔交易都是欺诈性的,则不会进行任何付款,并且后备基本上是无用的。一个好的折衷方案可能是回退到一个简单的业务规则,例如简单地让相当少量的交易通过,以在风险和不失去客户之间取得良好的平衡。

Timeout(超时)


超时模式非常简单,许多 HTTP 客户端都配置了默认超时。目标是避免响应的无限等待时间,从而在超时内未收到响应的情况下将每个请求视为失败。

下面的动画显示了支付服务等待欺诈检查服务的响应并在超时后中止操作。

9f08e8885ff4f867fd314473d83d1a25.gif

几乎每个应用程序都使用超时,以避免请求永远卡住。然而,处理超时并非易事。想象一下在网上商店下订单超时。您无法确定订单是否成功下达,但如果订单创建仍在进行中或请求从未处理,则响应超时。如果将超时与重试结合起来,您可能会得到重复的订单。如果您将订单标记为失败,客户可能会认为订单没有成功,但也许确实成功了,他们会被收费。

此外,您希望您的超时时间足够高以允许较慢的响应到达,但又足够低以停止等待永远不会到达的响应。

断路器


在电子产品中,断路器是一种开关,可保护您的组件免受过载损坏。在软件中,断路器可以保护您的服务不被垃圾邮件发送,同时由于高负载已经部分不可用。

Martin Fowler 描述了断路器模式。它可以实现为一个有状态的软件组件,在三种状态之间切换:关闭(请求可以自由流动)、打开(请求被拒绝而不提交给远程资源)和半打开(允许一个探测请求决定是否再次关闭电路)。下面的动画说明了一个正在运行的断路器。

049ee657dd4fe9ae83f23c5ee61b6ebe.gif

从支付服务到欺诈检查服务的请求通过断路器传递。在两次内部服务器错误之后,电路打开并且后续请求被阻止。等待一段时间后,电路进入半开状态。在这种状态下,它将允许一个请求在失败的情况下通过并变回打开状态,或者在成功的情况下关闭。下一个请求成功,因此电路再次关闭。

断路器是一种有用的工具,尤其是在与重试、超时和回退结合使用时。回退不仅可以在发生故障的情况下使用,也可以在电路开路的情况下使用。在下一节中,我们将看一个用 Kotlin 编写的 Vert.x 代码示例。

Vert.x 中的实现


在上一节中,我们从理论的角度研究了不同的弹性模式。现在让我们看看如何实现它们。该示例的源代码可在 GitHub 上找到。我们将在这个展示中使用 Vert.x 和 Kotlin。下一节将讨论其他替代方案。

Vert.x 提供了 CircuitBreaker,这是一个强大的装饰器类,它支持重试、回退、超时和断路器配置的任意组合。您可以使用 CircuitBreakerOptions 类配置断路器,如下所示。

val vertx = Vertx.vertx()
val options = circuitBreakerOptionsOf(
    fallbackOnFailure = false,
    maxFailures = 1,
    maxRetries = 2,
    resetTimeout = 5000,
    timeout = 2000
)
val circuitBreaker = CircuitBreaker.create("my-circuit-breaker", vertx, options)


在这个例子中,我们正在创建一个断路器,它在将其视为失败之前重试操作两次。在一次故障后,我们打开电路,该电路将在 5000 毫秒后再次半开。操作在 2000 毫秒后超时。如果指定了回退,则仅在开路的情况下才会调用它。也可以将断路器配置为在发生故障时调用回退,即使电路已关闭。

为了执行命令,我们需要提供一段异步代码来执行 Handler<Future<T>> 类型以及处理结果的 Handler<AsyncResult<T>> 类型的处理程序。返回 OK 并在之后打印它的最小示例如下所示:

circuitBreaker.executeCommand(
    Handler<Future<String>> {
        it.complete("OK")
    },
    Handler {
        println(it)
    }
)


在 Kotlin 中使用 Vert.x 时,您还可以将挂起函数作为参数传递,而不是使用处理程序。更多细节请参考 CoroutineHandlerFactory 类及其用法。除了这些基本功能之外,Vert.x 断路器模块还提供以下高级功能:

  • 事件总线通知。断路器可以在每次状态更改时将事件发布到事件总线。如果您想以某种方式对这些事件做出反应,这很有用。

  • 指标。断路器可以发布要由 Hystrix 仪表板使用的指标,以可视化断路器的状态。

  • 状态更改回调。您可以配置在电路打开或关闭时调用的自定义处理程序。

替代实施方法


并非每个框架都支持开箱即用的弹性设计模式。Vert.x 也不支持所有可能的模式。有一些指定项目直接解决弹性主题,例如  Hystrix、resilience4j、failsafe和  Istio 的弹性特性。

Hystrix 已在许多应用程序中使用,但不再处于积极开发中。Hystrix、resilience4j 以及故障安全都是从应用程序源代码中直接调用的。例如,您可以通过实现接口或使用注释来集成它。

另一方面,Istio 是一个服务网格,因此是基础架构的一部分,而不是应用程序代码。它用于编排分布式服务系统并实现边车的概念。服务通信通过那个  sidecar 发生,这是一个与服务进程一起的专用进程。然后,sidecar 可以处理诸如重试之类的机制。

Sidecar 方法的优点是您不会将业务逻辑与弹性逻辑混为一谈。您可以在不涉及太多应用程序代码的情况下替换 sidecar 技术。此外,您可以轻松修改和调整 sidecar 配置,而无需重新部署服务。缺点在于无法使用特定模式,例如用于线程池隔离的隔板模式( bulkhead)。此外,后备值等模式在很大程度上取决于您的业务逻辑。扩展现有代码库也可能比添加新的基础架构组件更容易。

概括


在这篇文章中,我们看到了松散耦合、隔离、延迟控制和监督如何对系统弹性产生积极影响。重试模式可以处理可以通过多次尝试来纠正的通信错误。回退模式有助于在本地解决通信故障。超时模式提供了延迟的上限。断路器解决了在持续通信错误的情况下由于重试和快速回退而导致的意外拒绝服务攻击的问题。

像 Vert.x 这样的框架提供了一些开箱即用的弹性模式。还有可以与任何框架一起使用的专用弹性库。另一方面,服务网格作为在基础设施级别引入弹性模式的选项而存在。与往常一样,没有万能的解决方案,您的团队应该找出最适合他们的解决方案。

本文 :https://architect.pub/resilience-design-patterns-retry-fallback-timeout-circuit-breaker
讨论:知识星球【首席架构师圈】或者加微信小号【ca_cto】或者加QQ群【792862318】
公众号
 
【jiagoushipro】
【超级架构师】
精彩图文详解架构方法论,架构实践,技术原理,技术趋势。
我们在等你,赶快扫描关注吧。
微信小号
 
【ca_cea】
50000人社区,讨论:企业架构,云计算,大数据,数据科学,物联网,人工智能,安全,全栈开发,DevOps,数字化.
 

QQ群
 
【285069459】深度交流企业架构,业务架构,应用架构,数据架构,技术架构,集成架构,安全架构。以及大数据,云计算,物联网,人工智能等各种新兴技术。
加QQ群,有珍贵的报告和干货资料分享。

视频号 【超级架构师】
1分钟快速了解架构相关的基本概念,模型,方法,经验。
每天1分钟,架构心中熟。

知识星球 【首席架构师圈】向大咖提问,近距离接触,或者获得私密资料分享。  

喜马拉雅 【超级架构师】路上或者车上了解最新黑科技资讯,架构心得。 【智能时刻,架构君和你聊黑科技】
知识星球 认识更多朋友,职场和技术闲聊。 知识星球【职场和技术】
领英 Harry https://www.linkedin.com/in/architect-harry/
领英群组 领英架构群组 https://www.linkedin.com/groups/14209750/
微博‍‍ 【超级架构师】 智能时刻‍
哔哩哔哩 【超级架构师】

抖音 【cea_cio】超级架构师

快手 【cea_cio_cto】超级架构师

小红书 【cea_csa_cto】超级架构师  

网站 CIO(首席信息官) https://cio.ceo
网站 CIO,CTO和CDO https://cioctocdo.com
网站 架构师实战分享 https://architect.pub   
网站 程序员云开发分享 https://pgmr.cloud
网站 首席架构师社区 https://jiagoushi.pro
网站 开发者闲谈 https://blog.developer.chat
网站 CPO宝典 https://cpo.work

谢谢大家关注,转发,点赞和点在看。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【韧性设计模式】韧性设计模式:重试、回退、超时、断路器 的相关文章

  • Java中ArrayList的交集和并集

    有什么方法可以做到这一点吗 我正在寻找 但没有找到 另一个问题 我需要这些方法 以便我可以过滤文件 有些是AND过滤器 有些是OR过滤器 就像集合论中的那样 所以我需要根据所有文件和保存这些文件的联合 相交 ArrayList 进行过滤 我
  • 如何将本机库链接到 IntelliJ 中的 jar?

    我正在尝试在 IntelliJ 中设置 OpenCV 但是我一直在弄清楚如何告诉 IntelliJ 在哪里可以找到本机库位置 在 Eclipse 中 添加 jar 后 您可以在 Build Config 屏幕中设置 Native 库的位置
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 如何通过 javaconfig 使用 SchedulerFactoryBean.schedulerContextAsMap

    我使用 Spring 4 0 并将项目从 xml 移至 java config 除了访问 Service scheduleService 带注释的类来自QuartzJobBean executeInternal 我必须让它工作的 xml 位
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 过滤两次 Lambda Java

    我有一个清单如下 1 2 3 4 5 6 7 和 预期结果必须是 1 2 3 4 5 6 7 我知道怎么做才能到7点 我的结果 1 2 3 4 5 6 我也想知道如何输入 7 我添加了i gt i objList size 1到我的过滤器
  • 如何在 Spring 中禁用使用 @Component 注释创建 bean?

    我的项目中有一些用于重构逻辑的通用接口 它看起来大约是这样的 public interface RefactorAwareEntryPoint default boolean doRefactor if EventLogService wa
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • java for windows 中的文件图标叠加

    我正在尝试像 Tortoise SVN 或 Dropbox 一样在文件和文件夹上实现图标叠加 我在网上查了很多资料 但没有找到Java的解决方案 Can anyone help me with this 很抱歉确认您的担忧 但这无法在 Ja
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 在java中为组合框分配键

    我想添加一个JComboBox在 Swing 中这很简单 但我想为组合中的每个项目分配值 我有以下代码 JComboBox jc1 new JComboBox jc1 addItem a jc1 addItem b jc1 addItem
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会

随机推荐

  • 【数据分析】数据分析方法(六):相关分析 & 群组分析

    数据分析方法 六 相关分析 群组分析 1 相关分析方法 当我们研究两种或者两种以上数据之间有什么关系的时候 就要用到相关分析 在解决问题的过程中 相关分析可以帮助我们扩大思路 将视野从一种数据扩大到多种数据 通过计算相关系数 我们可以看到两
  • 栈的应用——深度优先搜索(走迷宫)

    栈应用到走迷宫 寻路算法 的做法 迷宫就是下图所示的这种 这次主要是先用代码画出一个迷宫 利用二维数组 然后寻路走到出口 代码如下 在C 中运行 mystack h include
  • 统计文件数目

    编写一个程序 统计某个目录下 含子目录 里的所有目录数和文件数 import os path 在引号里加入需要统计的文件夹目录 def list files path file num 0 files num 0 for root dirs
  • 快手只发作品不直播的赚钱方法

    快手只发作品不直播的赚钱方法 玩快手也不开直播 只发段子 有这三种变现方式 我只告诉你可以往下看吗 以下几种方式 你千万记住点开左上角的三条杠 点击更多点击创作者中心 这个时候我们到里面找到什么任务中心点进来 这个时候到你该转米的时候了 随
  • spring 增强顺序改变的原因

    spring 增强顺序改变的原因 spring 5 2 7版本正式改变增强的顺序 网上的大部分文章主要从spring的代码层面的变动 来解释增强顺序的变动 而我想要了解的是这个变动 是因为导致了某些bug的出现吗 所以自己在github和s
  • Java中数据类型分类?

    转自 Java中数据类型分类 下文笔者讲述java中数据类型的分类 如下所示 基本数据类型boolean 布尔类型 short 短整型 int 整型 long 长整型 byte 字节型 char 字符型 float 单精度浮点型 doubl
  • 数据对象属性分类

    数据集由数据对象组成 一个数据对象代表一个实体 数据对象又称样本 实例 数据点或对象 属性 attribute 是一个数据字段 表示数据对象的一个特征 属性向量 或特征向量 是用来描述一个给定对象的一组属性 属性有不同类型 标称属性 nom
  • 线程基础---基础方法

    线程启动 在Thread类中注释标明有两种方式创建新的执行线程 一种是声明一个类是Thread的子类 这个子类应该重写类Thread的run方法 然后可以分配和启动子类的实例 创建线程的另一种方法是声明一个实现Runnable接口的类 这个
  • STM32F0开发笔记8: 在keil中使用不初始化变量

    我们进行程序设计的时候 都会知道 系统上电或复位时 会执行变量初始化操作 但是有些情况下 我们并不希望变量初始化 例如 在系统异常复位发生后 我们希望系统能够迅速恢复复位前的现场状况 这样就希望变量能够保留原先的值 而不被初始化 实际上 大
  • 第八届“泰迪杯”数据挖掘挑战赛C题“泰迪杯”奖论文(基于卷积神经网络及集成学习的网络问政平台留言文本挖掘与分析)

    目 录 第一章 引言 1 1挖掘背景 1 2挖掘意义 1 3问题描述 第二章 群众留言分类 2 1数据准备 2 1 1数据描述 2 1 2数据预处理 2 2特征提取 2 3建立模型 2 3 1卷积神经网络 2 3 2模型设计 2 3 3模型
  • [pg]数据库的并发控制

    参考 章 13 并发控制 数据库并发事务控制四 postgresql数据库的锁机制二 表锁 PostgreSQL 事务处理和并发控制 PostgreSQL并发控制 MVCC 事务 事务隔离级别 数据库中Select For update语句
  • Python使用Opencv图像处理方法完成手势识别(三)tkinter制作GUI界面

    前面对手势识别已经差不多完成 这一章来制作一个手势识别GUI界面和说一下精确度不够问题所在 首先是精确度不够的问题 让手势更规范 手掌张开点 首先应该调节Hsv阈值 因为手掌和环境颜色与我的可能有差异 调整面积 周长阈值 距离阈值 面积阈值
  • 2022黑马SpringBoot跟学笔记(一)

    2022黑马SpringBoot跟学笔记一 SpringBoot 1 SpringBoot简介 1 1 SpringBoot快速入门 1 1 1 开发步骤 1 1 1 1 创建新模块 1 1 1 2 创建 Controller 1 1 1
  • R语言调色板及填充实战:scale_colour_brewer与scale_fill_brewer函数

    R语言调色板及填充实战 scale colour brewer与scale fill brewer函数 在使用ggplot2进行数据可视化时 我们经常需要对图像的颜色进行调整以增强其视觉效果 R语言中提供了scale colour brew
  • 告别宽表,用 DQL 成就新一代 BI

    BI商业智能这个概念已经提出好几十年了 这个概念本身比较宽泛 不同人也有不同的理解和定义 但落实到技术环节 特别是面向业务用户的环节 所称的BI 基本就是指的多维分析或者自助报表 不管是叫自助报表还是多维分析 也都是一回事 都是让用户自己去
  • 数据库中查询的数据是多条,可是显示出来的只有一条,为什么?

    1 首先附上代码 public List
  • Redis学习笔记5:Jedis、RedisTemplate

    一 Jedis是什么 Jedis是Redis官方推荐的Java连接开发工具 要在Java开发中使用好Redis中间件 必须对Jedis熟悉才能写成漂亮的代码 详细了解 https www jianshu com p a1038eed6d44
  • nacos登录 提示权限认证失败 没有命名空间的访问权限

    前言 环境 centos7 9 nacos 2 2 2 问题描述 最近在部署nacos 2 2 2版本的时候 这是目前2023年4月份最新版本 发现按照start out日志给出的登录地址 http 192 168 158 128 8848
  • TensorFlow.js 和 Node-RED 图像识别应用程序

    在本文中 我们将看看您可以将这两种流行的开源软件工具组合起来做什么 使用 Node RED 创建示例图像识别流程 我们的目标是在 Node RED 中创建一个流来识别图像中的对象 如下面的屏幕截图所示 使用黄色节点组件从浏览器上传文件后 可
  • 【韧性设计模式】韧性设计模式:重试、回退、超时、断路器

    什么是韧性 软件本身并不是目的 它支持您的业务流程并使客户满意 如果软件没有在生产中运行 它就无法产生价值 然而 生产性软件也必须是正确的 可靠的和可用的 当谈到软件设计中的弹性时 主要目标是构建健壮的组件 这些组件既可以容忍其范围内的故障