任务不断失败重试导致雪崩效应

 

大家在开发工程中,一般都使用过类似Mq的消息中间件产品,或者自己开发处理数据的定时任务。

它们一般的流程都是:每隔一段时间,去数据库获取有效的任务,然后执行,执行完成之后,删除任务或者将任务设置为失效。

那么这就可能存在一个潜在的风险:“雪崩效应”

试想一下如下场景:我有个定时任务,每隔1s去数据库获取最早创建的并且有效的任务,然后执行任务。之所以获取最早创建的,是因为如果获取最新的任务,那么旧的任务可能就一直没机会执行了。

如果这个任务有问题,执行失败了,就会导致上游调度系统不断的获取到这条问题任务,不断的进行重试。

这会导致两个问题,第一,这个问题任务会一直占用计算资源,影响其他任务的正常执行。第二点,如果重试间隔时间很短,这个任务会产生雪崩效应,导致系统崩溃。

那么我们怎么避免呢?

最暴力的方法当然是,获取任务不是获取最早创建的,而是采用随机的方式。

但是,一般来说,我们还是希望任务能够尽量的顺序执行的,只是不希望被这样的问题任务给卡住,失败一次两次可能是网络的原因,但是失败多次,一般就是有错误了,就没必要不断的重试了,就需要人为的去处理。

所以,我们希望能做到:随着任务重试次数的增加,任务重试的机会越来越小。

那么,比较好的做法是:结合任务的重试次数,对重试时间进行非线性处理。让任务重试的间隔时间是刚开始是常数,然后是线性的,达到一定次数后变为非线性

具体的sql如下:

select * from task where valid=1 and update_time <= DATE_SUB(now(), INTERVAL (retry_count -1)*( retry_count -2)*(retry_count -3)+2*retry_count+1 SECOND) order by id asc limit 100

间隔时间t和重试次数r参考如下表达式:t(r) = a(r-1)*(r-2)*(r-3) + b*r + c

第一次r=0,所以间隔是常数c,后面三次重试,前面的多项式都是0,然后重试间隔是按照b线性增长的,之后就是三次方的非线性快速增长了,这样就优雅的实现了咱们的需求了。

或者是:新加个字段,next_retry_time,并建上索引,当任务失败时,在代码中通过上面的公式计算间隔时间,然后加上当前时间,写进next_retry_time,后面获取任务时候就改为:

select * from task where valid=1 and next_retry_time<now() order by id asc limit 100

战斗结束!!

猜你喜欢

转载自blog.csdn.net/zhanht/article/details/88702915