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