摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/bloggit
https://blog.risingstack.com/designing-microservices-architecture-for-failure/github
微服务架构使得能够经过明肯定义的服务边界来隔离故障。可是像在每一个分布式系统中同样,发生网络、硬件、应用级别的错误都是很常见的。因为服务依赖关系,任何组件可能暂时没法提供服务。为了尽可能减小部分中断的影响,咱们须要构建容错服务,来优雅地处理这些中断的响应结果。算法
本文介绍了基于RisingStack 的 Node.js 咨询和开发经验构建和操做高可用性微服务系统的最多见技术和架构模式。数据库
若是你不熟悉本文中的模式,那并不必定意味着你作错了。创建可靠的系统老是会带来额外的成本。浏览器
微服务架构将应用程序逻辑移动到服务,并使用网络层在它们之间进行通讯。这种经过网络间通讯代替单应用程序内调用的作法,会带来额外的延迟,以及须要协调多个物理和逻辑组件的系统复杂度。分布式系统的复杂性增长也将致使更高的网络故障率。缓存
微服务体系结构的最大优点之一是,团队能够独立设计,开发和部署他们的服务。他们对服务的生命周期拥有彻底的全部权。这也意味着团队没法控制他们依赖的服务,由于它更有可能由不一样的团队管理。使用微服务架构,咱们须要记住,提供者服务可能会临时不可用,因为其余人员发行的错误版本,配置以及其余更改等。服务器
微服务架构的最大优势之一是您能够隔离故障,并在当组件单独故障时,进行优雅的服务降级。 例如,在中断期间,照片共享应用程序中的客户可能没法上传新图片,但仍能够浏览,编辑和共享其现有照片。网络
微服务容错隔离架构
在大多数状况下,因为分布式系统中的应用程序相互依赖,所以很难实现这种优雅的服务降级,您须要应用几种故障转移的逻辑(其中一些将在本文后面介绍),觉得暂时的故障和中断作准备。并发
服务间彼此依赖,再没有故障转移逻辑下,服务所有失败。
Google的网站可靠性小组发现,大约70%的中断是由现有系统的变化引发的。当您更改服务中的某些内容时,您将部署新版本的代码或更改某些配置 - 这总有可能会形成故障,或者引入新的bug。
在微服务架构中,服务依赖于彼此。这就是为何你应该尽可能减小故障并限制它的负面影响。要处理变动中的问题,您能够实施变动管理策略和自动回滚机制。
例如,当您部署新代码或更改某些配置时,您应该先小范围的进行部分的替换,以渐进式的方式替换服务的所有实例。在这期间,须要监视它们,若是您发现它们对您的关键指标有负面影响,应当即进行服务回滚,这称为“金丝雀部署”。
变动管理 - 回滚部署
另外一个解决方案多是您运行两个生产环境。您始终只能部署其中一个,而且在验证新版本是否符合预期以后才,将负载均衡器指向新的。这称为蓝绿或红黑部署。
回滚代码不是坏事。你不该该在生产中遗留错误的代码,而后考虑出了什么问题。若是必要,越早回滚你的代码越好。
实例因为出现故障、部署或自动缩放的状况,会进行持续启动、从新启动或中止操做。它可能致使它们暂时或永久不可用。为避免问题,您的负载均衡器应该从路由中跳过不健康的实例,由于它们当前没法为客户或子系统提供服务。
应用实例健康情况能够经过外部观察来肯定。您能够经过重复调用GET /health
端点或经过自我报告来实现。如今主流的服务发现解决方案,会持续从实例中收集健康信息,并配置负载均衡器,将流量仅路由到健康的组件上。
自我修复能够帮助应用程序从错误中恢复过来。当应用程序能够采起必要步骤从故障状态恢复时,咱们就能够说它是能够实现自我修复的。在大多数状况下,它由外部系统实现,该系统会监视实例运行情况,并在较长时间内处于故障状态时从新启动它们。自我修复在大多数状况下是很是有用的。可是在某些状况下,持续地重启应用程序可能会致使麻烦。 当您的应用程序因为超负荷或其数据库链接超时而没法给出健康的运行情况时,这种状况下的频繁的重启就可能就不太合适了。
对于这种特殊的场景(如丢失的数据库链接),要实现知足它的高级自我修复的解决方案可能很棘手。在这种状况下,您须要为应用程序添加额外的逻辑来处理边缘状况,并让外部系统知道实例不须要当即从新启动。
因为网络问题和咱们系统的变化,服务常常会失败。然而,因为自我修复和负载均衡的保障,它们中的大多数中断是临时的,咱们应该找到一个解决方案,使咱们的服务在这些故障时服务仍就能够工做。这就是故障转移缓存的做用,它能够帮助并为咱们的应用程序在服务故障时提供必要的数据。
故障转移缓存一般使用两个不一样的到期日期; 较短的时间告诉您在正常状况下缓存可使用的过时时间,而较长的时间能够在服务故障时缓存依旧可用的过时时间。
故障转移缓存
请务必说起,只有当服务使用过期的数据比没有数据更好时,才能使用故障转移缓存。
要设置缓存和故障转移缓存,能够在 HTTP 中使用标准响应头。
例如,使用 max-age
属性能够指定资源被视为有效的最大时间。使用 stale-if-error
属性,您能够明确在出现故障的状况下,依旧能够从缓存中获取资源的最大时间。
现代的 CDN 和负载均衡器都提供各类缓存和故障转移行为,但您也能够为拥有标准可靠性解决方案的公司建立一个共享库。
在某些状况下,咱们没法缓存数据,或者咱们想对其进行更改,可是咱们的操做最终都失败了。对于此,咱们能够重试咱们的操做,由于咱们能够预期资源将在一段时间后恢复,或者咱们的负载均衡器将请求发送到了健康的实例上。
您应该当心地为您的应用程序和客户端添加剧试逻辑,由于大量的重试可能会使事情更糟,甚至阻止应用程序恢复,如当服务超载时,大量的重试只能使情况更糟。
在分布式系统中,微服务系统重试能够触发多个其余请求或重试,并启动级联效应。为了最小化重试的影响,您应该限制它们的数量,并使用指数退避算法来持续增长重试之间的延迟,直到达到最大限制。
当客户端(浏览器,其余微服务等)发起重试,而且客户端不知道在处理请求以前或以后操做失败时,您应该为你的应用程序作好幂等处理的准备。例如,当您重试购买操做时,您不该该再次向客户收取费用。为每一个交易使用惟一的幂等值键能够帮助处理重试。
流量限制是在一段时间内定义特定客户或应用程序能够接收或处理多少个请求的技术。例如,经过流量限制,您能够过滤掉形成流量峰值的客户和服务,或者您能够确保您的应用程序在自动缩放没法知足时,依然不会超载。
您还能够阻止较低优先级的流量,为关键事务提供足够的资源。
限流器能够阻止流量峰值产生
有一个不一样类型的限流器,叫作并发请求限制器。当您有重要的端点,您不该该被调用超过指定的次数,而您仍然想要能提供服务时,这将是有用的。
负载降级的一系列使用,能够确保老是有足够的资源来提供关键交易。它为高优先级请求保留一些资源,不容许低优先级的事务使用它们。负载降级开关是根据系统的总体状态作出决定,而不是基于单个用户的请求量大小。负载降级有助于您的系统恢复,由于当你有一个偶发事件时(多是一个热点事件),您仍能保持核心功能的正常工做。
要了解有关限流器和负载降级的更多信息,我建议查看这篇Stripe的文章。
在微服务架构中,咱们想要作到让咱们的服务具有快速失败与相互独立的能力。为了在服务级别上进行故障隔离,咱们可使用舱壁模式。你能够在本文的后面阅读更多有关舱壁的内容。
咱们也但愿咱们的组件可以快速失败,由于咱们不但愿对于有故障的服务,在请求超时后才断开。没有什么比挂起的请求和无响应的 UI 更使人失望。这不只浪费资源,并且还会影响用户体验。咱们的服务在调用链中是相互调用的,因此在这些延迟累加以前,咱们应该特别注意防止挂起操做。
你想到的第一个想法是对每一个服务调用都设置明确的超时等级。这种方法的问题是,您不能知道真正合理的超时值是多少,由于网络故障和其余问题发生的某些状况只会影响一两次操做。在这种状况下,若是只有其中一些超时,您可能不想拒绝这些请求。
咱们能够说,在微服务种经过使用超时来达到快速失败的效果是一种反模式的,你应该避免使用它。取而代之,您能够应用断路器模式,依据操做的成功与失败统计数据决定。
工业中使用舱壁将船舶划分为几个部分,以便在船体破坏的状况下,能够将船舶各个部件密封起来。
舱壁的概念在软件开发中能够被应用在隔离资源上。
经过应用舱壁模式,咱们能够保护有限的资源不被耗尽。例如,对于一个有链接数限制的数据库实例来讲,若是咱们有两种链接它的操做,咱们采用能够采用两个链接池的方式进行链接,来代替仅采用一个共享链接池的方式。因为这种客户端与资源进行了隔离,超时或过分使用池的操做页不会使其余操做失败。
泰坦尼克号沉没的主要缘由之一是其舱壁设计失败,水能够经过上面的甲板倒在舱壁的顶部,致使整个船体淹没。
泰坦尼克号舱壁设计(无效的设计)
为了限制操做的持续时间,咱们可使用超时。超时能够防止挂起操做并保持系统响应。然而,在微服务中使用静态、精细的超时是一种反模式,由于咱们处于高度动态的环境中,几乎不可能提出在每种状况下都能正常工做的正确的时间限制。
替代这种静态超时的手段是,咱们可使用断路器来处理错误。断路器以现实世界的电子元件命名,由于它们的做用是相同的。您能够保护资源,并帮助他们使用断路器进行恢复。它们在分布式系统中很是有用,由于在分布式系统中,重复故障可能致使雪球效应并使整个系统瘫痪。
当特定类型的错误在短期内屡次发生时,断路器会被断开。开路的断路器能够防止进一步的请求 - 就像咱们平时所说的电路跳闸同样。断路器一般在必定时间后关闭,在这期间能够为底层服务提供足够的空间来恢复。
请记住,并非全部的错误都应该触发断路器。例如,您可能但愿跳过客户端问题,例如具备4xx响应代码的请求,但不包括5xx服务器端故障。一些断路器也具备半开状态。在这种状态下,服务发送第一个请求以检查系统可用性,同时让其余请求失败。若是这个第一个请求成功,它将使断路器恢复到关闭状态并使流量流动。不然,它保持打开。
断路器
您应该不断测试您系统的常见问题,以确保您的服务能够抵抗各类故障。您应常常测试故障,让您的团队具有故障处理的能力。
对于测试,您可使用外部服务来标识实例组,并随机终止此组中的一个实例。这样,您能够准备单个实例故障,但您甚至能够关闭整个区域来模拟云提供商的故障。
最流行的测试解决方案之一是 Netflix 的 ChaosMonkey 弹性工具。
实施和运行可靠的服务并不容易。 您须要付出不少努力,同时公司也要有相应的财力投入。
可靠性有不少层次和方面,所以找到最适合您团队的解决方案很重要。您应该使可靠性成为您的业务决策流程中的一个因素,并为其分配足够的预算和时间。