微服务架构通过一种良好的服务边界划分,可能无效地进行故障隔离。但就像其余分布式系统一样,在网络、硬件或者利用级别上容易呈现问题的机率会更高。服务的依赖关系,导致在任何组件临时不可用的状况下,就它们的消费者而言都是能够承受的。为了可能升高局部服务中断所带来的影响,咱们须要构建一个容错服务,来优雅地应答特定类型的服务中断。
本文基于一些在 RisingStack 的参谋征询与开发教训,介绍了如何使用一些最罕用的技术和架构模型,去构建与保护一个高可用的微服务零碎。
如果你不相熟本文中的模式,并不意味着你做错了什么。毕竟构建一个高可用的零碎须要很多额定的付出。
* 微服务架构的危险 The Risk of the Microservices Architecture
微服务的架构将利用的逻辑挪动到一个服务外面,服务之间通过网络层进行通信交互。通过网络通信交互的形式取代了内存的调用,同时须要多个物理和逻辑组件之间的相互协作,给零碎带来了额定的提早性与复杂性。分布式系统复杂性的减少,导致了特定网络故障的可能性变得更大。
微服务容许你实现优雅的服务降级,因为组件能够被独自的设置为失败。
团队能够独立地设计、开发与部署他们的服务,是微服务的最大长处之一。他们齐全领有整个服务的生命周期,这也意味着团队无法控制他们的服务依赖,因为这些服务更有可能是不同的团队在治理。咱们须要记住,提供者的服务因为公布中断、配置等等其余的扭转而临时不可用,他们是由他人管制,并且组件之间独立流动。
* 优雅的服务降级 Graceful Service Degradation
微服务最佳劣势之一,当某个组件独自失败时,你能够实现优雅的服务降级,进行故障隔离。例如,一个照片共享的利用,因为中断,用户可能无奈上传新的照片,但他们依然能够浏览、编辑和分享他们现有的照片。
在大多数状况下,在一个分布式系统中,应用程序之间相互依赖,实现一种优雅的服务降级,这是很艰难的,你须要采取多种故障切换逻辑(其中一些会在本文前面进行探讨),应答长期的故障与中断。
* 变更治理 Change management
谷歌网站的可靠性团队(SRE)发现,大概 70% 的中断是由一个实时零碎的扭转而引起。当你在服务中更改某些内容时——你部署了新版本的代码或更改了一些配置——总会导致更高的失败机率或者引入一个新的 bug。
在微服务架构中,服务之间彼此依赖。这就是为什么你应该尽量减少失败,并限度它们的负面影响。如果要解决来自变更的问题,你能够应用变更管理策略和主动降级。
例如,当须要部署新代码或者更改某些配置时,你应该逐步地将这些更改利用于实例的子集,监控它们,甚至当你看到要害指标有负面影响时,它们会主动回滚复原。
另一个解决方案,就是运行两个生产环境。只部署其中一个,并且在验证新版本的运行合乎预期之后,才会将负载平衡指向新版本。这被称为蓝绿色部署,或红彩色部署。
复原代码不是一件坏事件。你不应该把坏的代码留在生产中,而后再思考哪里出了问题。必要的时候,总是要复原你的扭转(回滚),越快越好。
* 健康检查与负载平衡 Health-check and Load Balancing
实例会因为失败、部署或主动伸缩,而一直地启动、重新启动和进行。这会导致服务临时或永恒不可用。为了防止问题,你的负载平衡应该跳过不衰弱的实例,因为它们不能满足你的用户或子系统的须要。
利用实例衰弱能够通过内部察看来决策。你能够重复调用 GET /health 申请埋点或本身报告。古代服务发现解决方案,将一直从实例中收集衰弱信息,并配置负载平衡以保障衰弱的组件路由流量。
* 自愈 Self-healing
自我修复能够帮忙复原应用程序。咱们议论的自愈,是指应用程序能够做一些必要的步骤来复原解体状态。在大多数状况下,这样的操作是经由一个内部零碎来实现的,它会监控实例的衰弱,并在它们较长时间处于谬误状态的状况下,重新启动应用程序。自愈是十分有用的,然而在某些状况下,一直地重启应用程序会引起麻烦。因为负载过高或者数据库连贯超时,你的应用程序不停的重启,会导致无奈提供一个正确的衰弱状态。
实现一种为奥妙的状况而筹备的高级自我修复解决方案,可能会很辣手,比方数据库连贯失落。在这种状况下,你须要为应用程序增加额定的逻辑来解决一些极其状况,并让内部零碎晓得不须要立刻重启实例。
* 故障切换缓存 Failover Caching
服务通常会因为网络问题和零碎的变更而失败。因为自愈和先进的负载平衡,大多数中断只是临时的,然而咱们还应该找到一个解决方案,让咱们的服务在这些故障中可能失常工作。这就是故障切换缓存,它能够帮忙应用程序提供一些必要的数据。
故障切换缓存个别应用两个不同的过期工夫。设置一个较短的工夫,显示在失常状况下能够应用多长时间的缓存;设置另一个较长的工夫,显示在产生故障期间,可供使用缓存数据的工夫会有多久。
很重要的一点是,只有当过期的数据比什么都不做要好的状况呈现时,才可运行故障切换缓。
能够通过应用 HTTP 中的规范响应头(response header)来设置缓存和故障转移缓存。
例如,通过设定 header 参数 max-age 来指定一个资源被刷新时最大工夫;也能够通过设定 header 参数 stale-if-error 来决定,在服务失败的状况下,须要多长时间从缓存获取数据。
古代的 CDN 和负载均衡器提供了各种缓存和故障切换的形式,你也能够为公司建设一个蕴含了对立的可靠性解决方案的共享规范库。
* 重试机制 Retry Logic
在某些特定的场景下,咱们可能无奈缓存数据,或者咱们想对其做出一些更改,然而咱们的操作最终还是会失败。在这些状况下,咱们能够从新尝试咱们的操作,因为咱们能够预计资源在一段时间后会复原,或者咱们的负载平衡将咱们的申请转发到一个衰弱的实例。
在应用程序和客户端增加重试逻辑需放弃审慎,因为大量的重试会让事件变得更糟,甚至会阻止应用程序的复原。
在分布式系统中,微服务零碎重试会触发多个其余的申请或重试,引起一个级联效应。为了尽量减少重试带来的影响,你应该最大限度限度它们的产生次数,并应用指数弥补算法来继续减少重试之间的提早。
重试由客户端(浏览器,其余微服务等)发动,客户端不晓得这个操作是在解决申请之前失败还是之后失败的,你应该筹备好应用程序来解决幂等性(idempotency)。例如,当操作重试购买时,不应该对用户进行反复扣费。对于每个事务,应用惟一的 幂等令牌(idempotency-key),能够帮忙解决重试。
* 限流与降级 Rate Limiters and Load Shedders
限流是指在一个时间段内,特定的用户或应用程序能够接管或解决多少申请的技术。例如,有了限流,你就能够找出引起流量顶峰的用户和微服务,或者能够确保利用不会在负载过高状况下,产生主动扩容都不能援救。
你还能够限度业务优先级较低的流量,以便为外围业务提供足够的资源。
另外一种类型的限速器称为并发申请限度。当有一些低廉的端点不应该超过指定的调用次数,但你依然心愿提供流量服务时,抉择这样的操作是很有用的。
疾速降级能够确保总是有足够的可用资源去服务要害的事务。它为高优先级申请保留一些资源,并且不容许低优先级事务应用所有的资源。降级与否是依据零碎的整个状态进行判断的,而不是基于单个用户的申请桶大小。服务降级用于帮忙复原零碎,当产生一些事变时,它们能够保障外围性能依然持续工作。
如需获取更多无关限流与降级的信息,举荐返回 https://stripe.com/blog/rate-…,浏览 Stripe 的文章。
* 疾速且独立地失败 Fail Fast and Independently
在微服务体系结构中,咱们心愿咱们的服务可能疾速、独立地失败。为了在服务级别上隔离问题,咱们能够采纳舱壁模式(bulkhead pattern)。你稍后能够在这篇文章中读到更多对于舱壁的信息。
咱们还心愿咱们的组件疾速失败,因为咱们不想期待坏的实例超时。没有什么比一个挂着的申请和一个没有响应的 UI 更令人悲观的了。这样不仅浪费资源,而且还会对用户体验造成影响。咱们的服务是互相调用的,所以更应该额定留神,在这些提早完结之前,阻止挂起操作。
第一个想到的想法是在每个服务调用上使用一个较好级别的超时工夫。这种办法的问题在于,你不可能真正晓得什么是一个好的超时工夫值,因为在某些状况下,网络故障和其余问题只会影响到一两个操作。在这种状况下,如果只有少数几个申请超时,你可能不想回绝这些申请。
咱们能够说,在微服务中应用超时来实现疾速失败的例子是一种反模式,你应该防止它。你能够依赖于操作胜利 / 失败统计数据的断路器(circuit-breaker)模式,而不是超时。
* 舱壁 Bulkheads
舱壁被用来将一艘船划分成多个局部,这样就能够在船体破裂的状况下对局部关闭。
隔离壁的概念能够利用于软件开发中,做到资源隔离。
通过采纳舱壁模式,咱们能够爱护无限的资源不被耗尽。例如,如果咱们有两种操作,它们与雷同的数据库实例交互,咱们的连贯数量无限,那么咱们能够应用两个连接池,而不是共享连接池。因为此客户端资源拆散,当产生超时或者适度应用连接池的操作,不会导致所有其余操作的敞开。
泰坦尼克号沉没的次要起因之一,就是它的舱壁有一个设计上的失败,水能够通过舱壁顶部上的甲板注入,吞没整个船体。
* 断路器 Circuit Breakers
为了限度操作的持续时间,咱们能够应用超时。超时能够避免挂起操作并放弃零碎响应。然而,在微服务通信中应用动态的、微调的超时是一种反模式,因为咱们处在一个高度动静的环境中,简直不可能发现正确的工夫限度,以确保在每个场景下都能很好地工作。
咱们能够应用熔断来处理错误,而不是应用小的特定事务的动态超时。断路器是以真实世界电子元件命名的,因为它们的行为是雷同的(简略的说,这种模式次要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,避免火灾)。你能够爱护资源,帮忙他们用断路器复原。它们在分布式系统中十分有用,因为反复的失败会导致滚雪球效应(snowball effect),导致整个零碎瘫痪。
当一个特定类型的谬误在短时间内屡次呈现时,断路器就会关上。断路器的关上,阻止了进一步的资源申请——就像真的阻止了电流的流动。断路器通常在肯定工夫后敞开,为根底服务提供足够的空间来复原。
请记住,并非所有的谬误都应该触发断路器。例如,你可能心愿跳过客户端问题,比方跳过 4xx 状态码响应的申请,但不包含 5xx 服务器端谬误的申请。一些断路器也能够有半开状态,在此状态下,服务发送第一个申请检测零碎的可用性,同时让其余申请失败。如果第一个申请胜利,它将断路器复原到一个敞开状态,并容许流量进入。否则,它就会关上。
* 测试失败 Testing for Failures
你应该一直地测试你的零碎以避免常见问题,以确保你的服务可能接受住各种失败。你应该频繁地测试失败,让你的团队为产生事变而做好筹备。
对于测试,你能够应用一个内部服务来标识实例组,并随机终止该组中的一个实例。有了这个,你就能够为单个实例的失败做筹备,你甚至能够敞开整个可用区来模仿云提供商的中断。
最风行的测试解决方案之一是由 Netflix 提供的 ChaosMonkey 弹性工具。
* 结尾
实现和运行牢靠的服务并不容易。这须要你付出很大的致力,也要花费你的公司很多钱。
可靠性有很多的档次和方面,所以为你的团队找到最好的解决方案是很重要的。你应该将可靠性作为业务决策过程中的一个因素,并为它调配足够的估算和工夫。
* 次要播种
动静环境和分布式系统——比方微服务——会导致更大的失败机率。
服务应该独自失败,实现优雅的降级,用以改善用户体验。
70% 的中断是由变更引起的,复原代码并不是件好事。
疾速和独立的失败。团队无法控制他们服务的依赖。
架构模式和技术,如缓存、舱壁、限流、熔断,有助于建设牢靠的微服务。
原文:https://blog.risingstack.com/…
译文:https://blog.csdn.net/xushuai…
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)
2. 别在再满屏的 if/ else 了,试试策略模式,真香!!
3. 卧槽!Java 中的 xx ≠ null 是什么新语法?
4.Spring Boot 2.5 重磅公布,光明模式太炸了!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!