问题描述
java编程相关: 发送http请求出现异常,怎样设置重试机制?
问题出现的环境背景及自己尝试过哪些方法
1.使用for循环,通过try catch , 但是这样会一直占用一个线程的资源.
你期待的结果是什么?实际看到的错误信息又是什么?
现在有个想法是 把请求放入消息队列中,然后设置延时等级. 如果第二次重试再次失败,就把延时等级提高 ~ 不知道是否可行,或者有没有更好的解决方法.
消息队列不太好实现这种逻辑,一般都是用来解耦两个系统的。
用redis实现更简单点,可以使用redis的set或list数据结构。比如对应1分钟后,10分钟后,一小时后创建三个list。
用一个job轮训重试,成功了就删除,失败了就放到下一个list中。
可以用消息队列中间件比如rabbitmq,也可以用redis,看自己的需求使用消息队列各方面的可靠性要好些,看自己选择。rabbitmq有一个插件 rabbitmq_delayed_message_exchange 是专门来干这种事情的。
1.使用for循环,通过try catch , 但是这样会一直占用一个线程的资源.
你的意思是对每一个请求进行for循环?出现异常就保留这个线程?那这样的确是不合理的。
思路肯定还是要用到生产者-消费者模型。 出现异常时写入生产队列。至于实现,消息中间件当然最好,不过不想额外部署的话,关系型数据库或者JAVA内置的队列啥的都可以。
3 回答2.6k 阅读✓ 已解决
3 回答4.1k 阅读✓ 已解决
8 回答3.7k 阅读
4 回答2.8k 阅读✓ 已解决
2 回答2.7k 阅读✓ 已解决
3 回答2.5k 阅读✓ 已解决
4 回答1.9k 阅读
消息队列确实是ok的,毕竟题主的需求来看无碍乎就是一个循环的重试,重试间隔需要指定,所以用延迟消息队列绝对能够解决问题,消息队列中间件选择很多
BaLaLaLs提到的
rabbitmq
确实需要一个插件来做,也可以选择RocketMQ
,天生支持延迟消息,当然用redis
去模拟一种延迟效果也是可以的不过以上方案的选型,感觉需要额外多部署一些东西,假如系统之前并没有这方面的中间件,那可能为了做一个重试方案,显得有点大材小用了,毕竟确实真要实现,简单的循环,加上一些
if
判断就搞定了(至于占用线程资源倒是小事了),只是说这样的代码比较难看,而且也无法复用我记得在
Java
轮子百宝屋Spring
中,有一个工程:Spring Batch
,这里面有一个重试的注解@Retryable
,去看了一下,这一套API
现在已经独立出来,名叫:Spring-Retry
简单的
@Retryable
注解配置就可以让一个方法直接具有重试效果(即使我不解释注解属性的含义,也应该能简单看懂重试规则)执行该方法的效果
可以看到很简单就配置好了一套按照倍数增长间隔时间的重试方案
当然可以玩出很多效果,题主可以自己探索,但是为了解决题主的问题,我还是提前玩了一下,先说结论:
我自己反正试过已经有的
API
还不能支持题主30秒,3分钟,3小时的配置(或许我眼瞎没看到。。。)。毕竟他们之间的倍数没有特定的递增关系,可以看作是直接配置死了几次重试的间隔而已由于注解
@Retryable
的实现重试的方式是按照已有的Spring-Retry
配置的策略,所以我们得使用Spring-Retry
提供的另一种方式去定制一个重试模版先简单介绍一下,
@Retryable
的实现方式其实是基于一个拦截器的AnnotationAwareRetryOperationsInterceptor
,而拦截器里面会去为被注解的方法创建一个RetryTemplate
,这个RetryTemplate
中包含了注解中的规则以及被注解的方法所以定制重试模版的方式其实就是我们自己定义一个
RetryTemplate
,然后自己定义规则先说规则,因为我们只是为了把一列间隔时间直接取出来然后重试,所以我们的规则就很简单,实现类
CustomBackOffPolicy
(以下截图并不是完整代码)用一个
Queue<Long> intervalQueue
的方式简单粗暴的存储间隔时间,在需要取间隔时间直接调用poll
方法,取队列首位并删除然后我们就可以自定义
RetryTemplate
,间隔时间分别为1秒,5秒,10秒,其中SimpleRetryPolicy
是Spring-Retry
自带的,表示重试策略,参数就是重试次数,这里取的间隔数组长度+1(包括第一次失败,所以这里是4不是3)然后我们就可以去在需要重试的地方使用
RetryTemplate
了最后效果
当然当然,说实话哈,
Spring-Retry
也算是造了一个轮子,但是其实哈仔细去查看一下Spring-Retry
的实现模式,在题主的这个需求上,还是while
循环然后用Sleep
去停,哈哈哈哈,所以我最开始才说担心什么线程的资源占用,这都是小事,毕竟定时器或者什么定时任务这玩意儿,底层不都还是while
循环嘛,所以我觉得我都可以给你整一个简要版,弄一个CustomRetryTemplate
然后调用的话也很简单
最终效果也是一样的
所有代码我都放在了
github
上,稍微有点多,就不在这里发了,可以自取哈就酱,( ^_^ )/~~拜拜
最后再提一嘴,真要使用
Spring-Retry
,记得把spring-aspects
的配置要加上哈,我的pom
里是有的