什么是韧性?

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

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

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

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

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

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

模式

示例场景

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

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

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

重试

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

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

重试在以下情况下很有用

  • 丢包等临时网络问题

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

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

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

倒退(Fallback)

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

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

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

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

Timeout(超时)

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

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

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

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

断路器

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

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

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

断路器是一种有用的工具,尤其是在与重试、超时和回退结合使用时。回退不仅可以在发生故障的情况下使用,也可以在电路开路的情况下使用。在下一节中,我们将看一个用 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 这样的框架提供了一些开箱即用的弹性模式。还有可以与任何框架一起使用的专用弹性库。另一方面,服务网格作为在基础设施级别引入弹性模式的选项而存在。与往常一样,没有万能的解决方案,您的团队应该找出最适合他们的解决方案。

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

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

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

知识星球 向大咖提问,近距离接触,或者获得私密资料分享。

喜马拉雅 路上或者车上了解最新黑科技资讯,架构心得。 【智能时刻,架构君和你聊黑科技】
知识星球 认识更多朋友,职场和技术闲聊。 知识星球【职场和技术】
微博 【智能时刻】 智能时刻
哔哩哔哩 【超级架构师】

抖音 【cea_cio】超级架构师

快手 【cea_cio_cto】超级架构师

小红书 【cea_csa_cto】超级架构师

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

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

  1. .NET 云原生架构师训练营(设计原则设计模式)--学习笔记

    ▲ 点击上方"DotNet NB"关注公众号 回复"1"获取开发者路线图 学习分享 丨作者 / 郑 子 铭 这是DotNet NB 公众号的第180篇原创文章 ...

  2. 分布式面试 - 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?

    分布式面试 - 如何基于 dubbo 进行服务治理.服务降级.失败重试以及超时重试? 面试题 如何基于 dubbo 进行服务治理.服务降级.失败重试以及超时重试? 面试官心理分析 服务治理,这个问题如 ...

  3. 面试系列26 如何基于dubbo进行服务治理、服务降级、失败重试以及超时重试

    (1)服务治理 1)调用链路自动生成 一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,分布式系统由大量的服务组成.那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的 ...

  4. iOS开发 - 面向对象设计的设计模式(一):创建型模式(附 Demo UML类图)

    继上一篇的面向对象设计的设计原则,本篇是面向对象设计系列的第二个部分:面向对象设计的设计模式的第一篇文章. 另外,本篇博客的代码和类图都保存在我的GitHub库中:中的Chapter2. 最开始说一下 ...

  5. OpenShift 4 之Istio-Tutorial (6) 服务恢复能力(重试、超时、断路器)

    <OpenShift 4.x HOL教程汇总> 说明:本文已经在OpenShift 4.8环境中验证 文章目录 准备环境 重试(Fail Try) 超时(Timeout) 断路器(Circ ...

  6. 【韧性设计】节流模式

    控制应用程序实例.单个租户或整个服务使用的资源消耗.这可以使系统继续运行并满足服务水平协议,即使需求增加对资源造成极大负载. 背景和问题 云应用程序的负载通常会根据活动用户的数量或他们正在执行的活动类 ...

  7. 云计算设计模式(二)——断路器模式

    云带来的改变是显而易见的,云计算是一种按使用量付费的模式,这种模式提供可用的.便捷的.按需的网络访问, 进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供 ...

  8. python爬虫超时重试_Python爬虫实例(三):错误重试,超时处理

    错误重试 错误重试用到的方法之一是:@retry()装饰器html 装饰器实际是一个python函数,它的做用就是为被装饰的函数(或对象)进行装饰.包装,可让被装饰的函数(或对象)在不须要作任何代码改 ...

  9. 【Demo】配置重试和超时策略

    # 每台机器最大重试次数 feign-client.ribbon.MaxAutoRetries=2 # 可以再重试几台机器 feign-client.ribbon.MaxAutoRetriesNext ...

最新文章

  1. asp.net内置对象了解
  2. 本周个人总结(软件的初步开发)
  3. 微型统计分析系统README
  4. svgaps绘制时不能用中文命名吗_设计师需要了解的切图命名规范
  5. python迷宫小游戏大全_Python迷宫小游戏源代码、源程序
  6. 已解决:手动打包war包结果无法在Tomcat中部署
  7. log4j.properties文件示例
  8. 计算机在线考试系统监考,在线考试系统怎样实现监考功能
  9. 计算机三级嵌入式系统
  10. 网络安全实验室之基础关
  11. 信息学奥赛之数学一本通 pdf_整理青少年信息奥赛
  12. 监控显示服务器超时,监控连接服务器超时怎么解决
  13. 子图数正方形和长方形数量
  14. eclipse mars2 安装web插件
  15. hazelcast java_Hazelcast入门教程
  16. VS实现rar格式压缩包的解压详解
  17. java 3行4列二维数组_输入一个3行4列矩阵到二维数组中,并打印出来
  18. 八年级上册历史知识点(第3课 太平天国运动)
  19. 浏览器Tab页切换事件
  20. Java函数式编程教程(五):Java Steam API

热门文章

  1. 数据结构之有效的括号
  2. 《做有质感的民族》方文山
  3. java_批量处理图片转文字
  4. android rom 寿命,LineageOS ROM 彻底放弃安卓 9 Pie,华为P20 Pro/小米5s等24款手机不再更新...
  5. 鸿蒙系统的用途,华为高级副总裁谈鸿蒙系统:主要为工业用途
  6. GitHub开源了一款程序员摸鱼神器!上班摸鱼还不会被老板发现。。。
  7. 安装步骤_Saber 2016 安装步骤
  8. python PIL库中的getpixel函数
  9. android 信息添加附件功能,网易邮箱Android新版 添加附件可直接预览
  10. 打印机服务器端口无显示器,什么是打印机端口?无法找到打印机端口是怎么回事?...