微服务架构如何避免大规模故障?

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 微服务架构通过一种良好的服务边界划分,能够有效地进行故障隔离。但就像其他分布式系统一样,在网络、硬件或者应用级别上容易出现问题的机率会更高。服务的依赖关系,导致在任何组件暂时不可用的情况下,就它们的消费者而言都是可以接受的。为了能够降低部分服务中断所带来的影响,我们需要构建一个容错服务,来优雅地应对特定类型的服务中断。

微服务架构通过一种良好的服务边界划分,能够有效地进行故障隔离。但就像其他分布式系统一样,在网络、硬件或者应用级别上容易出现问题的机率会更高。服务的依赖关系,导致在任何组件暂时不可用的情况下,就它们的消费者而言都是可以接受的。为了能够降低部分服务中断所带来的影响,我们需要构建一个容错服务,来优雅地应对特定类型的服务中断。


本文基于一些在RisingStack的顾问咨询与开发经验,介绍了如何运用一些最常用的技术和架构模型,去构建与维护一个高可用的微服务系统。


如果你不熟悉本文中的模式,并不意味着你做错了什么。毕竟构建一个高可用的系统需要很多额外的付出。


*微服务架构的风险 The Risk of the Microservices Architecture


微服务的架构将应用的逻辑移动到一个服务里面,服务之间通过网络层进行通信交互。通过网络通信交互的方式取代了内存的调用,同时需要多个物理和逻辑组件之间的相互协作,给系统带来了额外的延迟性与复杂性。分布式系统复杂性的增加,导致了特定网络故障的可能性变得更大。


微服务允许你实现优雅的服务降级,因为组件可以被单独的设置为失败。


团队可以独立地设计、开发与部署他们的服务,是微服务的最大优点之一。他们完全拥有整个服务的生命周期,这也意味着团队无法控制他们的服务依赖,因为这些服务更有可能是不同的团队在管理。我们需要记住,提供者的服务由于发布中断、配置等等其他的改变而暂时不可用,他们是由别人控制,并且组件之间独立活动。


*优雅的服务降级 Graceful Service Degradation


微服务最佳优势之一,当某个组件单独失败时,你可以实现优雅的服务降级,进行故障隔离。例如,一个照片共享的应用,由于中断,用户可能无法上传新的照片,但他们仍然可以浏览、编辑和分享他们现有的照片。


image.png


在大多数情况下,在一个分布式系统中,应用程序之间互相依赖,实现一种优雅的服务降级,这是很困难的,你需要采取多种故障切换逻辑(其中一些会在本文后面进行讨论),应对临时的故障与中断。


image.png


*变更管理 Change management


谷歌网站的可靠性团队(SRE)发现,大约70%的中断是由一个实时系统的改变而引起。当你在服务中更改某些内容时——你部署了新版本的代码或更改了一些配置——总会导致更高的失败机率或者引入一个新的bug。


在微服务架构中,服务之间彼此依赖。这就是为什么你应该尽量减少失败,并限制它们的负面影响。如果要处理来自变更的问题,你可以使用变更管理策略和自动升级。


例如,当需要部署新代码或者更改某些配置时,你应该逐渐地将这些更改应用于实例的子集,监控它们,甚至当你看到关键指标有负面影响时,它们会自动回滚恢复。


image.png


另一个解决方案,就是运行两个生产环境。只部署其中一个,并且在验证新版本的运行符合预期之后,才会将负载均衡指向新版本。这被称为蓝绿色部署,或红黑色部署。


恢复代码不是一件坏事情。你不应该把坏的代码留在生产中,然后再思考哪里出了问题。必要的时候,总是要恢复你的改变(回滚),越快越好。


*健康检查与负载均衡 Health-check and Load Balancing


实例会因为失败、部署或自动伸缩,而不断地启动、重新启动和停止。这会导致服务暂时或永久不可用。为了避免问题,你的负载均衡应该跳过不健康的实例,因为它们不能满足你的用户或子系统的需要。


应用实例健康可以通过外部观察来决策。你可以反复调用 GET /health 请求埋点或自身报告。现代服务发现解决方案,将不断从实例中收集健康信息,并配置负载均衡以保证健康的组件路由流量。


*自愈 Self-healing


自我修复可以帮助恢复应用程序。我们谈论的自愈,是指应用程序可以做一些必要的步骤来恢复崩溃状态。在大多数情况下,这样的操作是经由一个外部系统来实现的,它会监控实例的健康,并在它们较长时间处于错误状态的情况下,重新启动应用程序。自愈是非常有用的,但是在某些情况下,不断地重启应用程序会引起麻烦。由于负载过高或者数据库连接超时,你的应用程序不停的重启,会导致无法提供一个正确的健康状态。


实现一种为微妙的情况而准备的高级自我修复解决方案,可能会很棘手,比如数据库连接丢失。在这种情况下,你需要为应用程序添加额外的逻辑来处理一些极端情况,并让外部系统知道不需要立即重启实例。


*故障切换缓存 Failover Caching


服务通常会因为网络问题和系统的变更而失败。由于自愈和先进的负载均衡,大多数中断只是暂时的,然而我们还应该找到一个解决方案,让我们的服务在这些故障中能够正常工作。这就是故障切换缓存,它可以帮助应用程序提供一些必要的数据。


故障切换缓存一般使用两个不同的过期时间。设置一个较短的时间,显示在正常情况下可以使用多长时间的缓存;设置另一个较长的时间,显示在发生故障期间,可供使用缓存数据的时间会有多久。


image.png


很重要的一点是,只有当过时的数据比什么都不做要好的情况出现时,才可运行故障切换缓。


可以通过使用HTTP中的标准响应头(response header)来设置缓存和故障转移缓存。


例如,通过设定 header 参数 max-age 来指定一个资源被刷新时最大时间;也可以通过设定 header 参数 stale-if-error 来决定,在服务失败的情况下,需要多长时间从缓存获取数据。


现代的CDN和负载均衡器提供了各种缓存和故障切换的方式,你也可以为公司建立一个包含了统一的可靠性解决方案的共享标准库。


*重试机制 Retry Logic


在某些特定的场景下,我们可能无法缓存数据,或者我们想对其做出一些更改,但是我们的操作最终还是会失败。在这些情况下,我们可以重新尝试我们的操作,因为我们可以预计资源在一段时间后会恢复,或者我们的负载均衡将我们的请求转发到一个健康的实例。


在应用程序和客户端添加重试逻辑需保持谨慎,因为大量的重试会让事情变得更糟,甚至会阻止应用程序的恢复。


在分布式系统中,微服务系统重试会触发多个其他的请求或重试,引起一个级联效应。为了尽量减少重试带来的影响,你应该最大限度限制它们的发生次数,并使用指数补偿算法来持续增加重试之间的延迟。


重试由客户端(浏览器,其他微服务等)发起,客户端不知道这个操作是在处理请求之前失败还是之后失败的,你应该准备好应用程序来处理幂等性(idempotency)。例如,当操作重试购买时,不应该对用户进行重复扣费。对于每个事务,使用唯一的 幂等令牌(idempotency-key ),可以帮助处理重试。


*限流与降级 Rate Limiters and Load Shedders


限流是指在一个时间段内,特定的用户或应用程序可以接收或处理多少请求的技术。例如,有了限流,你就可以找出引起流量高峰的用户和微服务,或者可以确保应用不会在负载过高情况下,发生自动扩容都不能拯救。


你还可以限制业务优先级较低的流量,以便为核心业务提供足够的资源。


image.png


另外一种类型的限速器称为并发请求限制。当有一些昂贵的端点不应该超过指定的调用次数,但你仍然希望提供流量服务时,选择这样的操作是很有用的。


快速降级可以确保总是有足够的可用资源去服务关键的事务。它为高优先级请求保留一些资源,并且不允许低优先级事务使用所有的资源。降级与否是根据系统的整个状态进行判断的,而不是基于单个用户的请求桶大小。服务降级用于帮助恢复系统,当发生一些事故时,它们可以保证核心功能仍然继续工作。


如需获取更多有关限流与降级的信息,推荐前往https://stripe.com/blog/rate-limiters,阅读Stripe的文章。


*快速且独立地失败 Fail Fast and Independently


在微服务体系结构中,我们希望我们的服务能够快速、独立地失败。为了在服务级别上隔离问题,我们可以采用舱壁模式(bulkhead pattern)。你稍后可以在这篇文章中读到更多关于舱壁的信息。


我们还希望我们的组件快速失败,因为我们不想等待坏的实例超时。没有什么比一个挂着的请求和一个没有响应的UI更令人失望的了。这样不仅浪费资源,而且还会对用户体验造成影响。我们的服务是相互调用的,所以更应该额外注意,在这些延迟结束之前,阻止挂起操作。


第一个想到的想法是在每个服务调用上运用一个较好级别的超时时间。这种方法的问题在于,你不可能真正知道什么是一个好的超时时间值,因为在某些情况下,网络故障和其他问题只会影响到一两个操作。在这种情况下,如果只有少数几个请求超时,你可能不想拒绝这些请求。


我们可以说,在微服务中使用超时来实现快速失败的例子是一种反模式,你应该避免它。你可以依赖于操作成功/失败统计数据的断路器(circuit-breaker)模式,而不是超时。


*舱壁 Bulkheads


舱壁被用来将一艘船划分成多个部分,这样就可以在船体破裂的情况下对部分封闭。


隔离壁的概念可以应用于软件开发中,做到资源隔离。


通过采用舱壁模式,我们可以保护有限的资源不被耗尽。例如,如果我们有两种操作,它们与相同的数据库实例交互,我们的连接数量有限,那么我们可以使用两个连接池,而不是共享连接池。由于此客户端资源分离,当发生超时或者过度使用连接池的操作,不会导致所有其他操作的关闭。


泰坦尼克号沉没的主要原因之一,就是它的舱壁有一个设计上的失败,水可以通过舱壁顶部上的甲板注入,淹没整个船体。


image.png


*断路器 Circuit Breakers


为了限制操作的持续时间,我们可以使用超时。超时可以防止挂起操作并保持系统响应。然而,在微服务通信中使用静态的、微调的超时是一种反模式,因为我们处在一个高度动态的环境中,几乎不可能发现正确的时间限制,以确保在每个场景下都能很好地工作。


我们可以使用熔断来处理错误,而不是使用小的特定事务的静态超时。断路器是以真实世界电子元件命名的,因为它们的行为是相同的(简单的说,这种模式主要是参考电路熔断,如果一条线路电压过高,保险丝会熔断,防止火灾)。你可以保护资源,帮助他们用断路器恢复。它们在分布式系统中非常有用,因为重复的失败会导致滚雪球效应(snowball effect),导致整个系统瘫痪。


当一个特定类型的错误在短时间内多次出现时,断路器就会打开。断路器的打开,阻止了进一步的资源请求——就像真的阻止了电流的流动。断路器通常在一定时间后关闭,为基础服务提供足够的空间来恢复。


请记住,并非所有的错误都应该触发断路器。例如,你可能希望跳过客户端问题,比如跳过 4xx 状态码响应的请求,但不包括 5xx 服务器端错误的请求。一些断路器也可以有半开状态,在此状态下,服务发送第一个请求检测系统的可用性,同时让其他请求失败。如果第一个请求成功,它将断路器恢复到一个关闭状态,并允许流量进入。否则,它就会打开。


image.png


*测试失败 Testing for Failures


你应该不断地测试你的系统以防止常见问题,以确保你的服务能够承受住各种失败。你应该频繁地测试失败,让你的团队为发生事故而做好准备。


对于测试,你可以使用一个外部服务来标识实例组,并随机终止该组中的一个实例。有了这个,你就可以为单个实例的失败做准备,你甚至可以关闭整个可用区来模拟云提供商的中断。


最流行的测试解决方案之一是由Netflix提供的ChaosMonkey弹性工具。


*结尾


实现和运行可靠的服务并不容易。这需要你付出很大的努力,也要花费你的公司很多钱。


可靠性有很多的层次和方面,所以为你的团队找到最好的解决方案是很重要的。你应该将可靠性作为业务决策过程中的一个因素,并为它分配足够的预算和时间。


*主要收获


动态环境和分布式系统——比如微服务——会导致更大的失败机率。 服务应该单独失败,实现优雅的降级,用以改善用户体验。 70%的中断是由变更引起的,恢复代码并不是件坏事。 快速和独立的失败。团队无法控制他们服务的依赖。 架构模式和技术,如缓存、舱壁、限流、熔断,有助于建立可靠的微服务。


原文:https://blog.risingstack.com/designing-microservices-architecture-for-failure/

译文:https://blog.csdn.net/xushuai110/article/details/77726447


相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
4天前
|
敏捷开发 监控 数据管理
构建高效微服务架构的五大关键策略
【4月更文挑战第20天】在当今软件开发领域,微服务架构已经成为一种流行的设计模式,它允许开发团队以灵活、可扩展的方式构建应用程序。本文将探讨构建高效微服务架构的五大关键策略,包括服务划分、通信机制、数据管理、安全性考虑以及监控与日志。这些策略对于确保系统的可靠性、可维护性和性能至关重要。
|
5天前
|
消息中间件 监控 持续交付
构建高效微服务架构:后端开发的进阶之路
【4月更文挑战第20天】 随着现代软件开发的复杂性日益增加,传统的单体应用已难以满足快速迭代和灵活部署的需求。微服务架构作为一种新兴的分布式系统设计方式,以其独立部署、易于扩展和维护的特点,成为解决这一问题的关键。本文将深入探讨微服务的核心概念、设计原则以及在后端开发实践中如何构建一个高效的微服务架构。我们将从服务划分、通信机制、数据一致性、服务发现与注册等方面入手,提供一系列实用的策略和建议,帮助开发者优化后端系统的性能和可维护性。
|
15天前
|
Kubernetes 安全 Java
构建高效微服务架构:从理论到实践
【4月更文挑战第9天】 在当今快速迭代与竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性及容错性,成为众多企业转型的首选。本文将深入探讨如何从零开始构建一个高效的微服务系统,覆盖从概念理解、设计原则、技术选型到部署维护的各个阶段。通过实际案例分析与最佳实践分享,旨在为后端工程师提供一套全面的微服务构建指南,帮助读者在面对复杂系统设计时能够做出明智的决策,并提升系统的可靠性与维护效率。
|
2天前
|
监控 API 持续交付
构建高效微服务架构:后端开发的新趋势
【4月更文挑战第23天】 随着现代软件开发实践的不断演进,微服务架构已经成为企业追求敏捷、可扩展和弹性解决方案的首选。本文深入探讨了如何构建一个高效的微服务架构,涵盖了关键的设计原则、技术选型以及实践建议。通过分析微服务的独立性、分布式特性和容错机制,我们将揭示如何利用容器化、服务网格和API网关等技术手段,来优化后端系统的可维护性和性能。文章旨在为后端开发人员提供一套全面的指南,以应对不断变化的业务需求和技术挑战。
|
7天前
|
机器学习/深度学习 运维 Prometheus
探索微服务架构下的系统监控策略
【4月更文挑战第18天】在当今快速迭代和持续部署盛行的软件工程实践中,微服务架构因其灵活性和可扩展性受到企业青睐。然而,随着服务的细粒度拆分和网络通信的增加,传统的监控手段已不再适用。本文将探讨在微服务环境中实施有效系统监控的策略,包括日志聚合、性能指标收集、分布式追踪以及异常检测等关键技术实践,旨在为读者提供构建稳定、可靠且易于维护的微服务系统的参考指南。
12 0
|
7天前
|
监控 持续交付 开发者
构建高效微服务架构:后端开发的新趋势
【4月更文挑战第18天】在数字化转型的浪潮中,微服务架构已成为企业提升系统灵活性、加速产品迭代的关键。此文深入探讨了构建高效微服务架构的实践方法,包括服务划分原则、容器化部署、持续集成/持续部署(CI/CD)流程以及监控与日志管理等关键技术点。通过分析具体案例,揭示了微服务在提高开发效率、降低维护成本及促进团队协作方面的显著优势。
|
8天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
9天前
|
监控 JavaScript 安全
构建微服务架构下的API网关
【4月更文挑战第15天】在微服务架构中,API网关扮演着至关重要的角色。它作为系统的唯一入口,不仅负责请求的路由、负载均衡和认证授权,还涉及到监控、日志记录和服务熔断等关键功能。本文将探讨如何构建一个高效且可靠的API网关,涵盖其设计原则、核心组件以及实现策略,旨在为后端开发人员提供一套实用的指导方案。
22 4
|
10天前
|
监控 负载均衡 API
构建高性能微服务架构:后端开发的最佳实践
【4月更文挑战第14天】 在当今快速发展的软件开发领域,微服务架构已成为构建可扩展、灵活且容错的系统的首选方法。本文深入探讨了后端开发人员在设计和维护高性能微服务时需要遵循的一系列最佳实践。我们将从服务划分原则、容器化部署、API网关使用、负载均衡、服务监控与故障恢复等方面展开讨论,并结合实际案例分析如何优化微服务性能及可靠性。通过本文的阅读,读者将获得实施高效微服务架构的实用知识与策略。
|
12天前
|
运维 监控 自动驾驶
构建可扩展的应用程序:Apollo与微服务架构的完美结合
构建可扩展的应用程序:Apollo与微服务架构的完美结合
32 10