微服务调用失败时常用处理手段

简介: 【10月更文挑战第27天】在微服务架构中,服务调用面临诸多不确定性,如服务提供者的硬件故障、网络问题等。因此,需要采取超时、重试、双发和熔断等策略来确保服务的稳定性和可靠性。超时机制避免长时间等待,重试机制应对偶发错误,双发机制提高成功率,熔断机制防止故障扩散。这些策略共同作用,保障了系统的高可用性。

一个是调用的执行是在服务提供者一端,即使服务消费者本身是正常的,服务提供者也可能由于诸如 CPU、网络 I/O、磁盘、内存、网卡等硬件原因导致调用失败,还有可能由于本身程序执行问题比如 GC 暂停导致调用失败。


另一个不确定因素是调用发生在两台机器之间,所以要经过网络传输,而网络的复杂性是不可控的,网络丢包、延迟以及随时可能发生的瞬间抖动都有可能造成调用失败。


单体应用改造为微服务架构后,要针对服务调用失败进行特殊处理。常用的处理手段如下:

1、超时

单体应用被改造成微服务架构后,一次用户调用可能会被拆分成多个系统之间的服务调用,任何一次服务调用如果发生问题都可能会导致最后用户调用失败。而且在微服务架构下,一个系统的问题会影响所有调用这个系统所提供服务的服务消费者,如果不加以控制,严重的话会引起整个系统雪崩。


所以在实际项目中,针对服务调用都要设置一个超时时间,以避免依赖的服务迟迟没有返回调用结果,把服务消费者拖死。这其中,超时时间的设定也是有讲究的,不是越短越好,因为太短可能会导致有些服务调用还没有来得及执行完就被丢弃了;当然时间也不能太长,太长有可能导致服务消费者被拖垮。根据我的经验,找到比较合适的超时时间需要根据正常情况下,服务提供者的服务水平来决定。具体来说,就是按照服务提供者线上真实的服务水平,取 P999 或者 P9999 的值,也就是以 99.9% 或者 99.99% 的调用都在多少毫秒内返回为准。


2、重试

虽然设置超时时间可以起到及时止损的效果,但是服务调用的结果毕竟是失败了,而大部分情况下,调用失败都是因为偶发的网络问题或者个别服务提供者节点有问题导致的,如果能换个节点再次访问说不定就能成功。而且从概率论的角度来讲,假如一次服务调用失败的概率为 1%,那么连续两次服务调用失败的概率就是 0.01%,失败率降低到原来的 1%。


所以,在实际服务调用时,经常还要设置一个服务调用超时后的重试次数。假如某个服务调用的超时时间设置为 100ms,重试次数设置为 1,那么当服务调用超过 100ms 后,服务消费者就会立即发起第二次服务调用,而不会再等待第一次调用返回的结果了。


3、双发

假如一次调用不成功的概率为 1%,那么连续两次调用都不成功的概率就是 0.01%,根据这个推论,一个简单的提高服务调用成功率的办法就是每次服务消费者要发起服务调用的时候,都同时发起两次服务调用,一方面可以提高调用的成功率,另一方面两次服务调用哪个先返回就采用哪次的返回结果,平均响应时间也要比一次调用更快,这就是双发。


但是这样的话,一次调用会给后端服务两倍的压力,所要消耗的资源也是加倍的,所以一般情况下,这种“鲁莽”的双发是不可取的。我这里讲一个更为聪明的双发,即“备份请求”(Backup Requests),它的大致思想是服务消费者发起一次服务调用后,在给定的时间内如果没有返回请求结果,那么服务消费者就立刻发起另一次服务调用。这里需要注意的是,这个设定的时间通常要比超时时间短得多,比如超时时间取的是 P999,那么备份请求时间取的可能是 P99 或者 P90,这是因为如果在 P99 或者 P90 的时间内调用还没有返回结果,那么大概率可以认为这次请求属于慢请求了,再次发起调用理论上返回要更快一些。


在实际线上服务运行时,P999 由于长尾请求时间较长的缘故,可能要远远大于 P99 和 P90。在我经历的一个项目中,一个服务的 P999 是 1s,而 P99 只有 200ms、P90 只有 50ms,这样的话,如果备份请求时间取的是 P90,那么第二次请求等待的时间只有 50ms。不过这里需要注意的是,备份请求要设置一个最大重试比例,以避免在服务端出现问题的时,大部分请求响应时间都会超过 P90 的值,导致请求量几乎翻倍,给服务提供者造成更大的压力。我的经验是这个最大重试比例可以设置成 15%,一方面能尽量体现备份请求的优势,另一方面不会给服务提供者额外增加太大的压力。


4、熔断

假如服务提供者出现故障,短时间内无法恢复时,无论是超时重试还是双发不但不能提高服务调用的成功率,反而会因为重试给服务提供者带来更大的压力,从而加剧故障。


针对这种情况,就需要服务消费者能够探测到服务提供者发生故障,并短时间内停止请求,给服务提供者故障恢复的时间,待服务提供者恢复后,再继续请求。这就好比一条电路,电流负载过高的话,保险丝就会熔断,以防止火灾的发生,所以这种手段就被叫作“熔断”。


简单来讲,熔断就是把客户端的每一次服务调用用断路器封装起来,通过断路器来监控每一次服务调用。如果某一段时间内,服务调用失败的次数达到一定阈值,那么断路器就会被触发,后续的服务调用就直接返回,也就不会再向服务提供者发起请求了。


熔断之后,一旦服务提供者恢复之后,服务调用如何恢复呢?这就牵扯到熔断中断路器的几种状态。

  • Closed 状态:正常情况下,断路器是处于关闭状态的,偶发的调用失败也不影响。
  • Open 状态:当服务调用失败次数达到一定阈值时,断路器就会处于开启状态,后续的服务调用就直接返回,不会向服务提供者发起请求。
  • Half Open 状态:当断路器开启后,每隔一段时间,会进入半打开状态,这时候会向服务提供者发起探测调用,以确定服务提供者是否恢复正常。如果调用成功了,断路器就关闭;如果没有成功,断路器就继续保持开启状态,并等待下一个周期重新进入半打开状态。


关于断路器的实现,最经典也是使用最广泛的莫过于 Netflix 开源的 Hystrix 了。

image.png


Hystrix 的断路器也包含三种状态:关闭、打开、半打开。Hystrix 会把每一次服务调用都用 HystrixCommand 封装起来,它会实时记录每一次服务调用的状态,包括成功、失败、超时还是被线程拒绝。当一段时间内服务调用的失败率高于设定的阈值后,Hystrix 的断路器就会进入进入打开状态,新的服务调用就会直接返回,不会向服务提供者发起调用。再等待设定的时间间隔后,Hystrix 的断路器又会进入半打开状态,新的服务调用又可以重新发给服务提供者了;如果一段时间内服务调用的失败率依然高于设定的阈值的话,断路器会重新进入打开状态,否则的话,断路器会被重置为关闭状态。


其中决定断路器是否打开的失败率阈值可以通过下面这个参数来设定:

HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()

而决定断路器何时进入半打开的状态的时间间隔可以通过下面这个参数来设定:

HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()


大部分的服务调用都需要设置超时时间以及重试次数,当然对于非幂等的也就是同一个服务调用重复多次返回结果不一样的来说,不可以重试,比如大部分上行请求都是非幂等的。至于双发,它是在重试基础上进行一定程度的优化,减少了超时等待的时间,对于长尾请求的场景十分有效。采用双发策略后,服务调用的 P999 能大幅减少,经过我的实践证明是提高服务调用成功率非常有效的手段。而熔断能很好地解决依赖服务故障引起的连锁反应,对于线上存在大规模服务调用的情况是必不可少的,尤其是对非关键路径的调用,也就是说即使调用失败也对最终结果影响不大的情况下,更加应该引入熔断。

相关文章
|
6月前
|
监控 Java 微服务
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
3月前
|
Kubernetes Cloud Native Docker
云原生之旅:从容器到微服务的架构演变
【8月更文挑战第29天】在数字化时代的浪潮下,云原生技术以其灵活性、可扩展性和弹性管理成为企业数字化转型的关键。本文将通过浅显易懂的语言和生动的比喻,带领读者了解云原生的基本概念,探索容器化技术的奥秘,并深入微服务架构的世界。我们将一起见证代码如何转化为现实中的服务,实现快速迭代和高效部署。无论你是初学者还是有经验的开发者,这篇文章都会为你打开一扇通往云原生世界的大门。
|
3月前
|
负载均衡 应用服务中间件 持续交付
微服务架构下的Web服务器部署
【8月更文第28天】随着互联网应用的不断发展,传统的单体应用架构逐渐显露出其局限性,特别是在可扩展性和维护性方面。为了解决这些问题,微服务架构应运而生。微服务架构通过将应用程序分解成一系列小型、独立的服务来提高系统的灵活性和可维护性。本文将探讨如何在微服务架构中有效部署和管理Web服务器实例,并提供一些实际的代码示例。
105 0
|
30天前
|
Kubernetes 安全 微服务
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
48 8
|
1月前
|
消息中间件 负载均衡 Cloud Native
云原生之旅:从容器到微服务的架构演变
在数字化转型的风潮中,云原生技术以其灵活性、可扩展性和弹性而备受青睐。本文将通过一个虚拟的故事,讲述一个企业如何逐步拥抱云原生,实现从传统架构向容器化和微服务架构的转变,以及这一过程中遇到的挑战和解决方案。我们将以浅显易懂的方式,探讨云原生的核心概念,并通过实际代码示例,展示如何在云平台上部署和管理微服务。
|
2月前
|
运维 Cloud Native Devops
云原生架构的崛起与实践云原生架构是一种通过容器化、微服务和DevOps等技术手段,帮助应用系统实现敏捷部署、弹性扩展和高效运维的技术理念。本文将探讨云原生的概念、核心技术以及其在企业中的应用实践,揭示云原生如何成为现代软件开发和运营的主流方式。##
云原生架构是现代IT领域的一场革命,它依托于容器化、微服务和DevOps等核心技术,旨在解决传统架构在应对复杂业务需求时的不足。通过采用云原生方法,企业可以实现敏捷部署、弹性扩展和高效运维,从而大幅提升开发效率和系统可靠性。本文详细阐述了云原生的核心概念、主要技术和实际应用案例,并探讨了企业在实施云原生过程中的挑战与解决方案。无论是正在转型的传统企业,还是寻求创新的互联网企业,云原生都提供了一条实现高效能、高灵活性和高可靠性的技术路径。 ##
166 3
|
2月前
|
存储 Java Maven
从零到微服务专家:用Micronaut框架轻松构建未来架构
【9月更文挑战第5天】在现代软件开发中,微服务架构因提升应用的可伸缩性和灵活性而广受欢迎。Micronaut 是一个轻量级的 Java 框架,适合构建微服务。本文介绍如何从零开始使用 Micronaut 搭建微服务架构,包括设置开发环境、创建 Maven 项目并添加 Micronaut 依赖,编写主类启动应用,以及添加控制器处理 HTTP 请求。通过示例代码展示如何实现简单的 “Hello, World!” 功能,并介绍如何通过添加更多依赖来扩展应用功能,如数据访问、验证和安全性等。Micronaut 的强大和灵活性使你能够快速构建复杂的微服务系统。
87 5
|
2月前
|
缓存 负载均衡 数据管理
深入探索微服务架构的核心要素与实践策略在当今软件开发领域,微服务架构以其独特的优势和灵活性,已成为众多企业和开发者的首选。本文将深入探讨微服务架构的核心要素,包括服务拆分、通信机制、数据管理等,并结合实际案例分析其在不同场景下的应用策略,旨在为读者提供一套全面、深入的微服务架构实践指南。**
**微服务架构作为软件开发领域的热门话题,正引领着一场技术革新。本文从微服务架构的核心要素出发,详细阐述了服务拆分的原则与方法、通信机制的选择与优化、数据管理的策略与挑战等内容。同时,结合具体案例,分析了微服务架构在不同场景下的应用策略,为读者提供了实用的指导和建议。