断路器的回退是被高估的弹性设计 - nurkiewicz


断路器中的回退是通过一些预先配置的响应来替换发生的故障,从而使故障的范围受到限制并且对最终用户隐藏。然而,在现实生活中,简单的回退往往过于简单,我建议采用更强大的方法来处理故障,补偿发生的故障。

什么是断路器?
断路器是代码和外部依赖关系之间的一层,具有很高的失败风险。每次调用其他服务,数据库,甚至访问自己的磁盘时,都有可能出现故障。如果没有断路器,这种简单的错误会迅速升级,暴露给最终用户。通常较小的依赖都会引发庞大的系统故障,导致503 HTTP响应或缓慢。断路器可快速发现错误电平的升高或响应时间延长,它不是减慢整个系统的速度,而是暂时切断整个依赖,您的代码仍然还是失败了  但是快速地失败。
快速失败很重要,在大多数情况下,立即显示错误页面的网站比在30秒后返回有效响应的网站要好得多。此外,会给你的依赖一些喘息的空间,也许它超载,有一个冷缓存或它正在重新启动。

什么是回退Fallback?
你的应用程序失败很快 ,是回退发挥作用的地方。resilience4jHystrix (R.I.P.) 是Java支持的回退断路器库。
想法很简单:当发生异常时,用一些预先配置的响应替换它。它可以是一个恒定值或另一个操作。回退类似:try catch

RecommendedMovies findRecommendations() {
    try {
        return riskyComplexAlgorithm();
    } catch(RuntimeException | TimeoutException e) {
        return bestsellersFallback;
    }
}

想象一下,您正在构建视频流平台并拥有复杂的机器学习驱动( )算法,以便找到下一个要观看的最相关的电影。那么当我们的算法中断时会发生什么 ?朴素的实现是传播异常并破坏整个用户界面,发生503 Internal Server Error。
但我们可以做得更好。我们可以在最近几天计算观看次数最多的电影,并将这些未经个性化的推荐回馈给每个人。

回退是天真的
如果没有准确的推荐,视频流媒体公司或电子商务仍然可以开展业务。他们仍然可以在没有任何建议的情况下开展业务。
但是,如果业务流程中更重要的一步失败怎么办?例如,您即将向新客户收取新订单。这个过程有两个基本部分:

  1. 欺诈检测
  2. 信用卡收费

这里使用断路器是个好主意:一方面,您不要让您的客户永远等待延迟的失败。断路器确保到达超时时间,并严格强制执行失败。另一方面,当断路打开时,您可以通过减少负载来为依赖者提供治愈的机会。
但我们可以在这里应用回退吗?
破坏欺诈检测系统的回退基本上是返回一个很简单的boolean:假设每笔交易都是合法的,则返回真(catch(e) {return true},但是还是可能在一些欺诈性订单上损失一些钱。当然暂时关闭欺诈检测听起来好像不错,直到你意识到骗子已经发现你竟然没有欺诈控制机制!

当信用卡支付网关的发生问题,回退失败会很快就出现异常,您无法向客户的信用卡收费,做了一单买卖但不实际收取任何费用是一种灾难。

怎么办?

现实生活中的回退
现在,当计算机系统出现故障时,许多企业停止运营。航空公司,证券交易所,甚至医院或汽车完全依赖于信息系统。但有些企业过去常常没有计算机就可以工作,而且技术上可以在没有它们的情况下运行一段时间:想想收银员在一张纸上记下您的杂货,这样他或她就可以在固定时将它们放回电脑;或者是在没有电子系统的情况下销售门票的票务代理。
即使一切都在线,航班也会超额预订!不知何故,人们学会了如何处理发生故障的机器和最终的一致性。也许我们的系统也应该学习它?

不要回退,试试补偿或恢复
再看一下这个欺诈检测和信用卡支付网关发生故障的例子,人类将如何以最诚恳的方式处理这个问题?当欺诈检测系统发生故障时,人类不会关闭业务,相反,他或她记下所有发生的交易,之后,当欺诈检测恢复时,有问题的交易将在大批量回顾中进行检查,商家知道可能只有0.1%的交易是欺诈性的,因此,商家接受该风险,并且当他或她随后发现欺诈时,立即采取某些行动。

但是,当支付网关发生故障时,我们肯定应该停止所有操作吗?
首先,人们仍然应该被允许浏览,搜索,添加产品到篮子等。如果一个损坏的支付组件破坏了你的整个系统,你就会陷入更深的麻烦。
但是,让我们再迈出一步。如果我们承担责任,只需假设所有付款都成功并继续处理在线购买,该怎么办?
如果你在销售实体商品,这实际上是相当安全的。尽管支付网关失败,假设信用卡已”虚假“地实现了收费,订单完成了,毕竟,需要几天或几个小时你才能发货,到那时支付网关已经恢复,并且您将有机会对整个停机期间使用的所有信用卡进行追溯。客户甚至不会注意到停电或延误。
如果有人真的提供假信用卡但我们很乐意接受这样的购买?不用担心,很可能其商品包装仍然没有离开我们的仓库,可以立即停止。即使它确实存在并且没有办法将其取回,与将商店完全关闭数小时相比,其损失的金钱是大巫见小巫。

如果实现?
在出现故障时,这就是我们应该如何设计我们的系统:

  • 不要让错误立即大范围传播爆炸而毁坏整个系统。这会导致无法承受任何故障的脆弱架构
  • 不要认为一切都很好。人们会很快发现您的欺诈检测系统可以被绕过或从未被执行过。
  • 尽可能做出乐观的商业决策,但事后要验证并补偿。

从技术角度来看,您的恢复代码应该或多或少看起来像这样:

try {
    businessAsUsual(thingy);
    return true;
} catch(Exception e) {
    scheduleCompensationLater(thingy);
    return true;
}

scheduleCompensationLater()方法应该在持久存储中记录失败的业务事务(banq:类似事件溯源,记录失败事件)。稍后,一些后台流程必须确保我们的乐观假设是正确的并做出相应的反应。
请记住:

  • 恢复和补偿的代码也可能失败。合理的是,如果您的业务流程因存储故障而失败,那么将恢复任务保留在同一存储中也很可能也会失败。
  • 否定验证可能发生得太晚。一旦故障组件重新联机,您必须快速行动。如果您已经发运了未收费的包装,那就太晚了
  • 需要对一切进行测量!故障数,恢复时间,待处理恢复任务数。如果数字令人担忧,请使用kill switch。

kill switch
在某些时候,你会意识到你太乐观了。交易数量的突然增加可能是一个标志。只要停电不会持续太长时间,后来保持乐观和补偿是好的。欺诈者会发现并滥用该系统。确保您的系统能够承受一定程度的故障,但具有自动安全措施。在发现太多可疑活动后,将乐观的恢复转变为悲观的失败处理方案。

结论
这都是关于风险评估的。我们的父母和祖父母在过去常常没有电脑控制和协调他们生活的方方面面。这些时代早已不复存在。但如果我们以容忍并可以补偿某些错误的方式设计我们的系统,我们将设计更强大,更好的用户体验。并节省了很多钱。