重试设计:超时时间和重试次数怎么设置比较合理

当我们把单体应用微服务化,本来一个进程内的函数调用就成了远程调用,这样就会涉及到网络上的问题。网络上有很多各式各样的组件,如:DNS服务、网卡、交换机、路由器、负载均衡等设备,这些设备都不一定是稳定的,在设计传输的整个过程中,只要一个环节出了问题,那么都会导致问题。
所以我们需要一个重试的机制。但是,我们需要明白的是,“重试”的语义是我们认为这个故障时暂时的,而不是永久的,所以,我们会去重试。所以,我们设计重试的时候,需要定义什么情况下需要重试,例如,调用超时、被调用端返回了某种可以重试的错误(如繁忙中、流控中、维护中、资源不足等)。而对于一些别的错误,则最好不要重试,比如:业务级的错误(如没有权限、或是非法数据等错误),技术上的错误(如:HTTP的503等,这种原因可能是触发了代码的bug,重试下去没有意义)。

重试的设计重点:

  1. 要确定什么样的错误下需要重试;
  2. 重试的时间和重试的次数。这种在不同的情况下要有不同的考量。有时候,对一些不是很重要的问题时,我们应该更快速失败而不是一段时间重试若干次。比如一个前端的交互需要用到后端的服务。这种情况下,在面对错误的时候,应该快速失败报错(比如:网络错误请重试)。而面对其他的一些错误,比如流控,那么应该使用指数退避的方式,以避免造成更多的流量。
  3. 如果超过重试次数,或是一段时间,那么重试就没有意义了。这个时候,说明这个错误不是一个短暂的错误,那么我们对于新来的请求,就没有必要再进行重试了,这个时候对新的请求直接返回错误就好了。但是,这样一来,如果后端恢复了,我们怎么知道呢,熔断
  4. 重试还需要考虑被调用方是否有幂等的设计。

视情况不同,重试策略可能不同
1.被调用方是集群,例如微服务调用,当一次调用失败时,一般不会采用backoff(退避)策略,而是会换一台被调用机器立即自动发起一次重试。不采用backoff的原因是,RPC调用通常对响应时间比较敏感。
2.被调用方是单机(或者是集群,但是请求会打到master一台机器时)而且对超时时间不敏感的调用,通常会采用backoff策略。在这种情况下,由于被调用方只有一台机器,调用超时时马上重试多半还会超时,而且连续重试会进一步加大被调用机器的压力,进一步加大调用失败的可能。

只有暂时性的故障并且不是可以快速失败的才需要做重试
重试依赖被调用方做了良好的幂等设计和接口返回码规范,知道什么情况下应该重试,什么情况下直接报故障。重试也需要做避让设计,防止被调用方压力过大,压垮系统。

那么问题来了,超时时间和重试次数怎么设置比较合理

连续重试对超时时间的影响:
A–>B–>C
A调用B设置的超时时间位5s,结果连续重试3次,每次都耗时2s,那最终这个请求的耗时少6s,超出用户设置的超时时间,解决这个问题的方式就是在每次重试后都重置一下请求的超时时间。

超时:
首先你要知道的是,单体应用被改造成微服务后,一次调用可能会被拆分成多个系统之间的服务调用,任何一次服务调用如果发生问题都可能会导致最后用户调用失败。而且在微服务架构下,一个系统的问题会影响所有调用这个系统所提供服务的服务消费者,如果不加以控制,严重的话会引起整个系统雪崩。
所以在实际项目中,针对服务调用都要设置一个超时时间,以避免依赖的服务迟迟没有返回调用结果,把服务拖死。这其中,超时时间的设定也是有讲究的,不是越短越好,因为太短可能会导致有些服务调用还没有来得及执行完就被抛弃了;当然时间也不能太长,太长有可能导致服务消费者被拖垮。根据老胡的经验,找到比较适合的超时时间需要根据正常情况下,服务提供者的服务水平来决定。具体来说,就是按照服务提供者线上真实的服务水平,取P999或者P9999的值,也就是99.9%或者99.99%的调用都在多少毫秒内返回为准。
重试
虽然设置超时时间可以起到及时止损的效果,但是服务调用的结果毕竟是失败了,而大部分情况下,调用失败都是因为偶发的网络问题或者个别服务提供者节点有问题导致的,如果能换个节点再次访问说不定就能成功。而且从概率论的角度来讲,假如一次服务调用失败的概率为1%,那么连续两次服务调用失败的概率就是0.01%,失败率降低到原来的1%。
所以,在实际调用服务时,经常还要设置一个服务调用超时后的重试次数。假如某个服务调用的超时时间设置为100ms,重试次数设置为1,那么当服务调用超过100ms后,服务消费者就会立即发起第二次服务调用,而不会再等待第一次调用返回的结果了。

超时与重试(Timeout and Retry)
超时模式,是一种最常见的容错模式,在工程实践中大量存在。常见的有设置网络连接超时时间,一次RPC的响应超时时间等。在分布式服务调用的场景中,它主要解决了当依赖服务出现建立网络连接或响应延迟,不用无限等待的问题,调用方可以根据事先设计的超时时间中断调用,及时释放关键资源,如Web容器的连接数,数据库连接数等,避免整个系统资源耗尽出现拒绝对外提供服务这种情况。

重试模式,一般和超时模式结合使用,适用于对下游服务等数据强依赖的场景(不强依赖的场景不建议使用),通过重试来保证数据的可靠性或一致性,常用于因网络抖动等导致服务出现超时的场景。与超时时间设置结合使用后,需要考虑接口的响应时间分布情况,超时时间可以设置为依赖服务接口99.5%响应时间的值,重试次数一般1-2次为宜,否则会导致请求响应时间延长,拖累整个系统。

https://tech.meituan.com/2016/11/11/service-fault-tolerant-pattern.html

服务A依赖于两个服务的数据完成此次操作。平时没有问题,假如服务B在你不知道的情况下,响应时间变长,甚至停止服务,而你的客户端超时时间设置过长,则你完成此次请求的响应时间就会变长,此时如果发生意外,后果会很严重。
Java的Servlet容器,无论是Tomcat还是Jetty都是多线程模型,都用Worker线程来处理请求。这个可配置有上限,当你的请求打满Worker线程的最大值之后,剩余请求会被放到等待队列。等待队列也有上限,一旦等待队列都满了,那这台Web Server就会拒绝服务,对应到Nginx上返回就是502。如果你的服务是QPS较高的服务,那基本上这种场景下,你的服务也会跟着被拖垮。如果你的上游也没有合理的设置超时时间,那故障会继续向上扩散。这种故障逐级放大的过程,就是服务雪崩效应。
解决方法:

首先要调研被依赖服务自己调用下游的超时时间是多少。调用方的超时时间要大于被依赖方调用下游的时间。
统计这个接口99%的响应时间是多少,设置的超时时间在这个基础上加50%。如果接口依赖第三方,而第三方的波动比较大,也可以按照95%的响应时间。
重试次数如果系统服务重要性高,则按照默认,一般是重试三次。否则,可以不重试。
mtyun.com/library/Trade-High-Availability-in-Action

https://mp.weixin.qq.com/s/6IkTnUbBlHjM3GM_bT35tA

https://gitee.com/weishaoying/thrift-learn/blob/master/thrift.md

https://www.heapdump.cn/article/2024830

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值