当前位置: 首页 > 后端技术 > Java

谈谈如何设计一个容错的微服务架构

时间:2023-04-01 15:33:54 Java

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