(7)弹力设计篇之“重试设计”

概要:

1、重试的场景,比如流控,并不是所有的失败场景都适合重试。

2、重试的策略,简单的指数退避策略,和 Spring 实现的多种策略。可以用 Java 的 Annotation 来实现,或者用 Server Mesh 的方式,不必写在业务逻辑里。

3、重试设计的重点。

关于重试。微服务化掉,远程调用,会涉及到网络上的问题。有很多的各式各样的组件,如:DNS 服务,网卡、交换机、路由器、负载均衡等设备,这些设备都不一定是稳定的,在数据传输的整个过程中,只要一个环节出了问题,都会导致问题。

一、重试的场景

" 重试 " 的语义是我们认为这个故障是暂时的,而不是永久的,所以,我们会去重试

要重试调用超时、被调用端返回了某种可以重试的错误(如繁忙中、流控中、维护中、资源不足等)。

不要重试业务级的错误(如没有权限、或是非法数据等错误),技术上的错误(如:HTTP 的 503 等,这种原因可能是触发了代码的 bug,重试下去没有意义)。

二、重试的策略

有个重试的最大值,经过一段时间不断的重试后,就没有必要再重试了,应该报故障了。休息一会儿再重试,这样可以避免因为重试过快而导致网络上的负担更重。

1.Exponential Backoff 指数级退避 :每一次重试所需要的休息时间都会翻倍增加。让被调用方能够有更多的时间来从容处理我们的请求。和 TCP 的拥塞控制有点像。

(1)定义一个调用返回的枚举类型,包括 5 种返回错误——成功 SUCCESS、维护中 NOT_READY、流控中 TOO_BUSY、没有资源 NO_RESOURCE、系统错误 SERVER_ERROR。

public enum Results {

    SUCCESS,

    NOT_READY,

    TOO_BUSY,

    NO_RESOURCE,

    SERVER_ERROR

}

(2)定义一个 Exponential Backoff 的函数,其返回 2 的指数。这样,每多一次重试就需要多等一段时间。如:第一次等 200ms,第二次要 400ms,第三次要等 800ms……

public static long getWaitTimeExp(int retryCount) {

    long waitTime = ((long) Math.pow(2, retryCount) );

    return waitTime;

}

(3)真正的重试逻辑。我们可以看到,在成功的情况下,以及不属于我们定义的错误下,我们是不需要重试的,而两次重试间需要等的时间是以指数上升的。

2.Spring 的重试策略

Spring Retry 是专门的一个项目:https://github.com/spring-projects/spring-retry,把 Spring 封装成了一个组件,以 AOP 的方式通过 Annotation 的方式使用。

@Service

public interface MyService {

    @Retryable(

      value = { SQLException.class },

      maxAttempts = 2,

      backoff = @Backoff(delay = 5000))

    void retryService(String sql) throws SQLException;

    ...

}

配置 @Retryable 注解,只对 SQLException 的异常进行重试,重试两次,每次延时 5000ms。相关的细节可以看相应的文档。我在这里,只想让你看一下 Spring 有哪些重试的策略。

NeverRetryPolicy:只允许调用 RetryCallback 一次,不允许重试。

AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环。

SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为 3 次,RetryTemplate 默认使用的策略。

TimeoutRetryPolicy:超时时间重试策略,默认超时时间为 1 秒,在指定的超时时间内允许重试。

CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置 3 个参数 openTimeout、resetTimeout 和 delegate;关于熔断,会在后面描述。

CompositeRetryPolicy:组合重试策略。有两种组合方式,乐观组合重试策略是指只要有一个策略允许重试即可以,悲观组合重试策略是指只要有一个策略不允许重试即不可以。但不管哪种组合方式,组合中的每一个策略都会执行。

Backoff 的策略如下。

NoBackOffPolicy:无退避算法策略,即当重试时是立即重试;

FixedBackOffPolicy:固定时间的退避策略,需设置参数 sleeper 和 backOffPeriod,sleeper 指定等待策略,默认是 Thread.sleep,即线程休眠,backOffPeriod 指定休眠时间,默认 1 秒。

UniformRandomBackOffPolicy:随机时间退避策略,需设置 sleeper、minBackOffPeriod 和 maxBackOffPeriod。该策略在 [minBackOffPeriod, maxBackOffPeriod] 之间取一个随机休眠时间,minBackOffPeriod 默认为 500 毫秒,maxBackOffPeriod 默认为 1500 毫秒。

ExponentialBackOffPolicy:指数退避策略,需设置参数 sleeper、initialInterval、maxInterval 和 multiplier。initialInterval 指定初始休眠时间,默认为 100 毫秒。maxInterval 指定最大休眠时间,默认为 30 秒。multiplier 指定乘数,即下一次休眠时间为当前休眠时间 *multiplier。

ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数,之前说过固定乘数可能会引起很多服务同时重试导致 DDos,使用随机休眠时间来避免这种情况。

三、重试设计的重点

要确定什么样的错误下需要重试;

(1)一些不是很重要的问题时,应该更快失败而不是重试。比如一个前端的交互需要用到后端的服务。应该快速度失败报错(比如:网络错误请重试)。比如流控,应该使用指数退避的方式,避免造成更多的流量。

(2)如果超过重试次数,或是一段时间,没有必要再进行重试了,新的请求直接返回错误就好了。但如果后端恢复了不知道,so需要熔断设计。

(3)如果没有幂等的设计,那么重试是不安全的,可能会导致一个相同的操作被执行多次。

(4)重试的代码比较通用,不用侵入到业务代码中。有两个模式。代码级的,像 Java 那样可以使用 Annotation 的方式。另外一种是走 Service Mesh 的方式.

(5)对于有事务相关的操作。我们可能会希望能重试成功,而不至于走业务补偿那样的复杂的回退流程。对此,需要一个比较长的时间来做重试,需要保存住请求的上下文,对程序的运行有比较大的开销,所以把这样的上下文暂存在本机或是数据库中,腾出资源来去做别的事,过一会再回来把之前的请求从存储中捞出来重试。

你实现过哪些场景下的重试?所采用的策略是什么?实现的过程中遇到过哪些坑?

评论1:

重试的场景:

1、服务timeout超时异常

2、服务不存在,配置问题,服务流控

3、对error错误不重试,如无权限、参数错误

重试的策略:

1、数据库中保存重试需要的上下文,目前通过json来保存,指定最大重试次数、当前重试次数,下次运行时间

重试需要注意的地方:

1、服务幂等性,在重试时需证调用服务的幂等性

2、重试数据的监控,邮件,短信及时通知

3、重试数据的结转,防止表数据量过大

评论2:

之前做的重试策略是:异常发生的时候,数据库记录当前上下文,依据重试次数来确定重试时间,推送给延迟消息队列控制重试

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341

推荐阅读更多精彩内容