-->

左耳听风_048_47_弹力设计篇之重试设计

你好,我是陈浩网名左耳朵house.那今天我们要讲的内容呢是重试设计。

那关于重试呢,这个模式啊应该是一个很普遍的设计模式了当我们把单体应用服务化啊,尤其是微服务化。

本来在一个进程内的函数调用呢就成了远程调用。

那这样呢就会涉及网络上的问题。

网络上呢有很多的各式各样的组件,比如DNS服务、网卡、交换机、路由器,还有负载均衡等设备。

那这些设备啊都不一定是稳定的。

在数据传输的整个过程中呢,只要任何一个环节出了问题,最后啊都会影响系统的稳定性。

所以呢我们需要一个重试的机制。

但是呢我们需要明白的是,重试的语义呢是我们认为这个故障是暂时的,而不是永久的。

所以呢我们会去重试。

我认为呢设计重试的时候,我们需要定义出什么情况下需要重试。

比如调用超时或者被调用端,返回了某种可以重试的错误。

而对于一些别的错误呢,则最好不要重试,比如业务起的错误,还有技术上的错误。

那关于重试的设计呢,一般来说啊都需要有一个重试的最大值。

经过一段时间不断的重试之后啊,就没有必要再重试了啊,应该报故障。

在重试的过程中呢,每一次重试失败时啊,都应该休息一会儿再重试。

那这样呢可以避免因为重试过快而导致网络上的负担加重。

在重试的设计中呢,我们呢一般都会引入exponential back off的策略啊,也就是所谓的指数级退比。

在这种情况下呢,每一次从事所需要的休息事件啊都会成倍的增加。

那这种机制呢主要是用来让被调用方能够有更多的时间来从容处理我们的请求。

那这个呢其实和TCP的拥塞控制有点像。

那如果我们写成代码呢,应该是文中这个样子。

那首先呢我们定义一个调用返回的枚举类型。

那其中呢有五种返回错误,包括成功success维护中not ready,流控中too busy,没有资源,no resource,还有系统错误server error.那接下来呢我们定义一个exponential back of的函数,它返回二的指数。

那这样呢每多一次重试就需要多等一段时间。

比如第一次呢等两百毫秒,第二次啊要四百毫秒,第三次呢要等八百毫秒等等。

来看文中的这段代码。

那这个呢是真正的重试逻辑。

我们可以看到在成功的情况下呢,还有不属于我们定义的错误下,我们啊是不需要重试的。

而两次重试间所需要等的时间啊是以指数上升的那这段代码是非常基本的重试代码啊,没有什么新鲜的。

我们来看一看spring中所知识的一些重试策略。

Spring retry是一个单独实现重试功能的项目,我们可以通过annotation的方式来使用。

那具体呢你可以参考我在文中写的代码,我配置了一个retrivable注解,是对soql exception的异常进行重试,重试两次,每次延时五千毫秒。

呃,相关的细节呢可以看相应的文档。

我在这里呢只想让你看一下spring有哪些城市的策略。

那第一个呢是never try policy,只允许调用retry call back一次啊,不允许重试。

那第二个呢是always retry policy,允许无限重试,直到成功。

那这个方式啊逻辑不当会导致死循环。

那第三个呢是simple retry policy固定次数的重试策略。

那默认重试最大次数为三次,是try timplate默认使用的策略。

那第四个呢是time mettry try policy,基于超时时间的重置策略,呃,默认超时时间呢为一秒,在指定的超时时间内啊允许重试。

那第五个呢是circuit breaker, retry policy有熔断功能的城市策略,需要设置三个参数,open time out、 reset time out和delegate.那关于熔断呢,我会在后面描述。

那最后第六个策略是compostion to retry policy组合充置策略,那他有两种组合方式,乐观组合重事策略呢是指只要有一个策略,允许重事就可以了。

那悲观组合重事策略呢是指只要有一个策略,不允许重事就不可以。

但是不管哪种组合方式啊,组合中的每一个策略都会执行。

那关于back off的策略呢,有这样几个。

那第一呢是no back of policy,意思是无退币算法策略啊,也就是当重试的时候呢,是立即来重试。

那第二呢是fix back of policy,意思是固定时间的退币策略。

那这个呢需要设置参数,slipper和bachale period.那slipper呢是指定等待策略,默认是随二点sleep啊,也就是线程休眠,而backle period参数呢用来指定休眠,时间默认是一秒。

那第三呢是uniform running back of policy,意思呢是随机时间的退比策略,需要设置slipper mean bacle period,还有max backle period.那这个策略在main backo period和max backo period之间啊取一个随机休练的时间。

那mean backo period呢,默认为五百毫秒,而max backo period默认为一千五百毫秒。

那第四呢是x porantial bacco policy,意思是指数退比策略需要设置参数,slipper initial interval maxiine toval还有multiplayer initial interval呢指定初始的休眠时间默认为一百毫秒,而max interval呢指定最大的休眠时间默认为三十秒。

那multiplier呢指定乘数,也就是下一次休眠时间为当前休眠时间乘以multiplier.那第五个呢是x financial random backropolicy,意思是随机指数的退币策略啊,引入随机的乘数。

那之前呢我们说过固定的乘数啊可能会引起很多服务同时重试,导致d dos,所以使用随机的休眠时间啊来避免这种情况。

那最后呢我们来谈一谈重试设计的重点。

那重试的设计重点呢主要有这么几个。

那第一呢是要确定什么样的错误下需要重试。

那第二呢是重试的时间和重试的次数。

那这种在不同的情况下呢要有不同的考量。

有时候呢面对一些不是很重要的问题的时候呢,我们应该更快失败,而不是重事一段时间若干次的。

比如一个前端的交互需要用到后端的服务。

那这种情况下,在面对错误的时候,应该快速失败来报错。

而面对其他的一些错误呢,比如流控,那么应该使用指数退币的方式,以避免造成更多的流量。

那第三呢,如果超过重试次数啊或者过了一段时间,那么重试呢就没有意义了。

那这个时候呢,说明这个错误不是一个短暂的错误。

那么我们对于新来的请求啊,就没有必要再进行重试了。

那这个时候对于新的请求直接返回错误就好了。

但是这样一来呢,如果后端恢复了,我们怎么知道呢?那此时呢需要使用我们的熔断设计了啊,这个我们在后面会说,那第四呢重试还需要考虑被调用方是否有密等的设计。

那如果没有呢,那么重试是不安全的啊,可能会导致一个相同的操作被执行多次。

那第五呢城市的代码比较简单,也比较通用,完全可以不用侵入到业务代码中。

那这里呢有两个模式,那一个呢是代码级的,像java那样可以使用annotation的方式啊。

如果没有注解呢,也可以包装在底层库或者SDK中啊,不需要让上层业务感知到。

那另外一种呢是走service matsh的方式,那这个呢也会在后面说到最后呢,重置设计的第六个重点对于有事务相关的操作,我们可能会希望能够重试成功,而不至于走业务补偿那样的复杂的回退流程。

对此呢我们可能需要一个比较长的时间来做重试。

但是呢我们需要保存请求的上下文,那这个可能会对程序的运行有比较大的开销。

因此呢有一些设计啊,会先把这样的上下文啊暂存到本机或者数据库中,然后腾出资源来做别的事儿。

那过一会儿呢,再回来把之前的请求从存储中捞出来,重试。

好了,我们来总结一下今天分享的主要内容。

首先呢我讲了城市的场景啊,比如流控,但是呢并不是所有的失败场景都是个城市。

那接着呢我讲了重试的策略,包括简单的指数退比策略和spring实现的多种策略。

那这些策略呢可以使用java的annotation来实现啊,或者使用server match的方式,从而不必写在业务逻辑里面。

那最后呢我总结了程市设计的重点。

在下一节课中呢,我们会讲述熔断设计,希望对你有帮助,也欢迎你来分享一下你实现过哪些场景下的重试呢?所采用的策略是什么呢?实现的过程中啊,遇到过哪些坑呢?文末呢我给出了分布式系统设计模式系列文章的目录,希望你能在这个列表里啊找到自己感兴趣的内容。