微服务架构下请求调用失败的解决方案

文章收录在我的 GitHub 仓库,欢迎Star Java-Interview-Tutorial

0 前言

相比单体架构,微服务架构下,服务调用从同一台机器内部的本地调用变成了不同机器间的远程方法调用,这就引入不确定因素:

  • 调用的执行是在服务提供者一端,即使服务消费者本身正常,服务提供者也可能由于诸如CPU、网络I/O、磁盘、内存、网卡等硬件原因导致调用失败,还可能因本身程序执行问题如GC暂停导致调用失败
  • 调用发生在两台机器间,所以要经过网络传输,而网络不可控:丢包、延迟及抖动都可能造成调用失败。

所以必须要针对服务调用失败进行特殊处理。

1 超时

微服务化后,一次用户调用可能会被拆分成多系统间的服务调用,任何一次服务调用若发生问题都可能导致用户请求最终是失败的。一个系统异常会影响所有依赖该系统所提供服务的服务消费者,可能导致服务雪崩。

所以针对服务调用,都要设置超时时间,避免依赖服务迟迟没有返回调用结果,把服务消费者拖死。 但超时时间的设定也需考量:

  • 太短,可能有些服务调用还没有来得及执行完,就被丢弃
  • 太长,可能导致服务消费者被拖垮

因此,需根据正常情况下,服务提供者的服务水平来决定。按服务提供者线上真实服务水平,取P999或P9999值,即以99.9%或者99.99%的调用都在多少ms内返回为准。

2 重试

虽然设置超时时间可及时止损,但是服务调用结果毕竟还是失败,而大部分情况下,调用失败只是因为偶发的网络问题或个别服务提供者节点有问题,若能换个节点再访问说不定就能成功。

假如一次服务调用失败概率为1%,则连续两次服务调用失败的概率0.01%,失败率大大降低。

所以,实际服务调用时,一般还设置一个服务调用超时后的重试次数。 若某服务调用的超时时间设置为100ms,重试次数设置为1,则当服务调用超过100ms后,服务消费者就会立即发起第二次服务调用,不会再等待第一次调用返回的结果。

3 双发

若一次调用不成功的概率1%,则连续两次调用都失败概率0.01%,可推得,一个简单的提高服务调用成功率的办法-双发,每次服务消费者要发起服务调用时,都同时发起两次服务调用:

  • 可提高调用成功率
  • 两次服务调用哪个先返回,就采用哪次的返回结果,平均响应时间也比一次调用更快

但这样,一次调用会给后端服务两倍压力,所消耗资源也加倍,所以一般“鲁莽”双发不可取。

更聪明的双发,“备份请求”(Backup Requests)。服务消费者发起一次服务调用后,在给定时间内,若没返回请求结果,则Consumer就立刻发起另一次服务调用。 注意该设定时间通常比超时时间短得多,如超时时间取P999,则备份请求时间可能取P99或P90,因为若在P99或P90时间内调用还没返回结果,大概率可认为这次请求属于慢请求,再次发起调用理论上返回要更快。

实际线上服务运行时,P999由于长尾效应,可能远大于P99和P90。 如一个服务的P999=1s,而P99=200ms、P90=50ms,这样,若备份请求时间取P90,则第二次请求等待的时间只有50ms。

不过注意,备份请求要设置一个最大重试比例,避免服务端异常时,大部分请求的响应时间都超过P90,导致请求量翻倍,给服务提供者造成更大压力。 经验之谈,最大重试比例可设置成15%:

  • 能尽量体现备份请求的优势
  • 不会给服务提供者额外增加太大的压力

4 熔断

前面的手段在服务Provider偶发异常时很有效,但若Provider故障,短时间内都无法恢复,无论是超时重试还是双发:

  • 无法提高服务调用成功率
  • 由于重试,还给Provider带来更大压力,加剧故障

这时,就需要服务Consumer能探测到Provider故障了,并短时间内停止请求,给Provider故障恢复的时间,待Provider恢复后,再继续请求。就如一条电路,电流负载过高,保险丝就会熔断。

4.1 熔断原理

把客户端的每次服务调用,通过断路器封装,使用断路器来监控每一次服务调用。若某段时间内,服务调用失败次数达到一定阈值,则断路器就会被触发,后续的服务调用就直接返回,也就不会再向Provider发起请求。

熔断之后,一旦Provider恢复,服务调用如何恢复呢?这牵扯到熔断器的状态转换:

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

断路器的最经典实现就是Hystrix。Hystrix就包含三种状态:关闭、打开、半打开。Hystrix会把每次服务调用都用HystrixCommand封装,实时记录每次服务调用的状态,包括成功、失败、超时还是被线程拒绝。

当一段时间内服务调用失败率>阈值,断路器就会进入打开状态,新的服务调用会直接返回,不会向 Provider发起调用。再等设定的时间间隔后,断路器又会进入半打开,新的服务调用又可重新发给Provider;若一段时间内服务调用的失败率依然>阈值,断路器会重新打开,否则,断路器被关闭。

决定断路器是否打开的失败率阈值通过如下参数设定:

HystrixCommandProperties.circuitBreakerErrorThresholdPercentage();

决定断路器何时进入半打开的时间间隔通过如下参数设定:

HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()

滑动窗口算法

统计指定时间段内服务调用失败率:

默认情况下,滑动窗口包含10个桶,每个桶时间宽度为1s,每个桶内记录这1s内所有服务调用中成功的、失败的、超时的以及被线程拒绝的次数。

当新的1s到来时,滑动窗口就会往前滑动,丢弃掉最旧的1个桶,把最新1个桶包进来。

任意时刻,Hystrix都会取滑动窗口内所有服务调用的失败率作为断路器开关状态的判断依据,这10个桶内记录:

滑动窗口内所有服务的调用失败率 =(失败的+超时的+被线程拒绝的调用次数)/总调用次数

5 总结

大部分服务调用都要设置超时时间及重试次数,但对非幂等的不可以重试,如大部分上行请求都是非幂等。

双发是在重试基础上的优化,减少超时等待的时间,对于长尾请求很有效。采用双发后,服务调用的P999能大幅减少,是提高服务调用成功率的有效手段。

熔断能很好地解决依赖服务故障引起的连锁反应,对于大规模服务调用的必不可少,尤其是对非关键路径的调用,即使调用失败也对最终结果影响不大的情况下,更应该引入熔断。

参考

  • https://martinfowler.com/bliki/CircuitBreaker.html
  • https://github.com/Netflix/Hystrix/wiki/How-To-Use

原文地址:https://cloud.tencent.com/developer/article/2181136

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340