网站首页 > 技术教程 正文
对于“接口/方法 重试”,相信很多小伙伴都听说过,但是在实际项目中却很少真正去实践过,在本篇文章中,Debug将给各位小伙伴介绍一种“重试”机制的实现,即Guava_Retrying,相对于传统的Spring_Retrying或者动态代理实现的重试功能而言,本文要介绍的Guava_Retrying机制使用起来将更加容易、灵活性更强!
老赵:“这个 接口/方法 调用又失败了,老李啊,你去写个重试功能吧!”。
老李:“他娘的,这接口调用咋又不行了。。。行吧,老子立马给你撸一个重试功能” 。
这样的对话,相信有些小伙伴会感觉似曾相识!特别是当自己在工位上安安静静的写代码时,会突然性的接到技术老大分配给自己的这种需求。。。没啥好说的,只能潜下心,去研究研究了!
对于“重试”,那可是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(因为要考虑到写是否幂等)都不适合重试。
而诸如“远程调用超时”、“网络突然中断”等业务场景则可以进行重试,在微服务 治理框架中,通常都有自己的重试与超时配置,比如Dubbo可以设置retries=1,timeout=500调用失败只重试1次,超过500ms调用仍未返回则调用失败(详情可以观看学习Debug录制的“分布式服务调度Dubbo实战教程 http://www.fightjava.com/web/index/course/detail/2 ”)
对于“外部 RPC 调用”,或者“数据入库”等操作,如果一次操作失败,则可以进行多次重试,从而提高调用成功的可能性。
下面我们基于前面搭建的SpringBoot多模块企业级项目,基于Guava_Retrying初步实现所谓的“重试功能”。工欲善其事必先利其器,首先当然是需要加入Guava_Retrying的依赖Jar,如下所示:
<!--guava-retrying--> <dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>
之后,我们来写个简单的入门案例,先来 过一把“接口调用重试”的瘾!
/** * Guava_retrying机制实现重试 * @Author:debug (SteadyJack) * @Link: weixin-> debug0868 qq-> 1948831260 * @Date: 2019/12/1 16:17 **/ public class RetryUtil { private static final Logger log= LoggerFactory.getLogger(RetryUtil.class); private static Integer i=1; public static Integer execute() throws Exception{ log.info("----重试时 变量i的叠加逻辑----"); return i++; } public static void main(String[] args) { //TODO:定义任务实例 Callable<String> callable= () -> { Integer res=execute(); //当重试达到3 + 1次之后 我们就不玩了 if (res>3){ return res.toString(); } return null; }; //TODO:定义重试器 Retryer<String> retryer=RetryerBuilder.<String>newBuilder() //TODO:当返回结果为Null时 - 执行重试 .retryIfResult(Predicates.isNull()) //TODO:当执行核心业务逻辑抛出RuntimeException - 执行重试 .retryIfRuntimeException() //TODO:还可以自定义抛出何种异常时 - 执行重试 .retryIfExceptionOfType(IOException.class) .build(); try { retryer.call(callable); } catch (ExecutionException | RetryException e) { e.printStackTrace(); } } }
运行该main方法,可以得到如下的结果:
从该上述代码中,我们得知“重试机制”功能实现的核心在于定义Retryer实例以及Callable任务运行实例 ,特别是Retryer实例,可以设置“什么时机重试”。
除此之外,对于 Retryer实例 我们还可以设置“重试的次数”、“重试的时间间隔”、“每次重试时,定义Listener监听一些操作逻辑”等等。如下代码所示:
public static void main(String[] args) { //TODO:定义任务实例 Callable<String> callable= () -> { return null; }; //TODO:每次重试时 监听器执行的逻辑 RetryListener retryListener=new RetryListener() { @Override public <V> void onRetry(Attempt<V> attempt) { Long curr=attempt.getAttemptNumber(); log.info("----每次重试时 监听器执行的逻辑,当前已经是第 {} 次重试了----",curr); if (curr == 3){ log.error("--重试次数已到,是不是得该执行一些补偿逻辑,如发送短信、发送邮件..."); } } }; //TODO:定义重试器 Retryer<String> retryer=RetryerBuilder.<String>newBuilder() //TODO:当返回结果为Null时 - 执行重试 .retryIfResult(Predicates.isNull()) //TODO:当执行核心业务逻辑抛出RuntimeException - 执行重试 .retryIfRuntimeException() //TODO:还可以自定义抛出何种异常时 - 执行重试 .retryIfExceptionOfType(IOException.class) //TODO:每次重试时的时间间隔为5s .withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS)) //TODO:重试次数为3次,3次之后就不重试了 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) //TODO:每次重试时定义一个监听器listener,监听器的逻辑可以是 "日志记录"、"做一些补偿操作"... .withRetryListener(retryListener) .build(); try { retryer.call(callable); } catch (ExecutionException | RetryException e) { e.printStackTrace(); } }
其中,我们加入了“监听器Listener”、“定义了重试次数”、“定义了每次重试的时间间隔”,这三个才是Guava_Retrying提供给开发者重量级的玩意,如下代码所示!
//TODO:每次重试时的时间间隔为5s .withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS)) //TODO:重试次数为3次,3次之后就不重试了 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) //TODO:每次重试时定义一个监听器listener,监听器的逻辑可以是 "日志记录"、"做一些补偿操作"... .withRetryListener(retryListener)
其中,“重试次数策略StopStrategies”、“重试的时间间隔设置策略WaitStrategies”中Guava_Retrying提供了许多种选择,比如“重试次数可以是一个随机数”、“重试的时间间隔也可以设置为某个区间范围内的随机数”等等,下图为运行结果截图:
下面,我们来撸一个真实的业务场景,即“调用某个接口的方法,用于获取SysConfig配置表中某个字典配置记录,如果该字典配置记录不存在(即返回Null),那我们就重试3次,如果期间获取到了,那么就返回结果;3次过后,依旧为Null时,则执行一些补偿性的措施:即发送邮件通知给到指定的人员,让他们上去检查检查相应的数据状况!”
下图为 系统字典配置表SysConfig存储的字典记录,其中,没有id=11的记录,我们将拿着这个 id=11 来进行测试:
如下代码为正常项目开发过程中我们自定义的Service及其方法:
/** * Guava_Retrying重试机制的 小型真实案例 * @Author:debug (SteadyJack) * @Link: weixin-> debug0868 qq-> 1948831260 * @Date: 2019/12/1 17:51 **/ @Service public class RetryService { private static final Logger log= LoggerFactory.getLogger(RetryService.class); @Autowired private SysConfigMapper sysConfigMapper; @Autowired private EmailSendService emailSendService; //TODO:获取某个字典配置详情 public SysConfig getConfigInfo(final Integer id){ SysConfig config=sysConfigMapper.selectByPrimaryKey(id); if (config==null){ //TODO:当没有查询到该数据记录时,执行重试逻辑 doRetry(id); config=sysConfigMapper.selectByPrimaryKey(id); } return config; } //TODO:执行重试逻辑 private void doRetry(final Integer id){ //TODO:定义任务实例 Callable<SysConfig> callable= () -> { return sysConfigMapper.selectByPrimaryKey(id); }; //TODO:每次重试时 监听器执行的逻辑 RetryListener retryListener=new RetryListener() { @Override public <V> void onRetry(Attempt<V> attempt) { Long curr=attempt.getAttemptNumber(); log.info("----每次重试时 监听器执行的逻辑,当前已经是第 {} 次重试了----",curr); //当达到3次时 就执行一些补偿性的措施,如发送邮件通知某些大佬…. if (curr == 3){ log.error("--重试次数已到,是不是得该执行一些补偿逻辑,如发送短信、发送邮件..."); emailSendService.sendSimpleEmail("重试次数已到","请各位大佬上去检查一下sysConfig是否存在","1948831260@qq.com"); } } }; //TODO:定义重试器 Retryer<SysConfig> retryer= RetryerBuilder.<SysConfig>newBuilder() //TODO:当返回结果为 false 时 - 执行重试(即sysCofig为null) .retryIfResult(Objects::isNull) //TODO:当执行核心业务逻辑抛出RuntimeException - 执行重试 .retryIfRuntimeException() //TODO:还可以自定义抛出何种异常时 - 执行重试 .retryIfExceptionOfType(IOException.class) //TODO:每次重试时的时间间隔为10s (当然啦,实际项目中一般是不超过1s的,如500ms,这里是为了方便模拟演示) .withWaitStrategy(WaitStrategies.fixedWait(10L, TimeUnit.SECONDS)) //TODO:重试次数为3次,3次之后就不重试了 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) //TODO:每次重试时定义一个监听器listener,监听器的逻辑可以是 "日志记录"、"做一些补偿操作"... .withRetryListener(retryListener) .build(); try { retryer.call(callable); } catch (ExecutionException | RetryException e) { e.printStackTrace(); } } }
最后写个Java Unit Test,即Java单元测试案例,如下所示:
@Autowired private RetryService retryService; @Test public void method8() throws Exception{ final Integer id=11; SysConfig entity=retryService.getConfigInfo(id); log.info("---结果:{}",entity); }
点击运行该单元测试案例,啥事都不要做,等待运行结果,你会发现“重试”的效果我们已经实现了!如下所示:
我们再点击运行该单元测试案例,然后在它运行了第1次重试机会之后,我们赶紧手动到数据库将 id=12 的那条系统配置记录,调整为 id=11 !然后再来看运行的结果,如下图所示:
如下图为“补偿性措施”中的“发送邮件”:
好了,本篇文章我们就介绍到这里了,建议各位小伙伴一定要照着文章提供的样例代码撸一撸,只有撸过才能知道这玩意是咋用的,否则就成了“空谈者”。其他相关的技术,感兴趣的小伙伴可以关注Debug的技术公众号,或者私信Debug!
猜你喜欢
- 2024-09-25 屡败屡战RETRY第六关关卡攻略 屡屡败屡战的意思
- 2024-09-25 基于redis实现的分布式队列 基于 redis 实现分布式锁是?
- 2024-09-25 .NET 使用 mysql 时 EnableRetryOnFailure() 函数的作用及注意事项
- 2024-09-25 新连载漫画大作导视(十月篇) 十日十月漫画作家
- 2024-09-25 基于guava的重试组件Guava-Retryer
- 2024-09-25 我是怎样得到AO3内容的 我是怎样得到ao3内容的英文
- 2024-09-25 Spring Boot 中使用 spring-retry 轻松解决重试
- 2024-09-25 Java重试利器之Guava-Retryer retry java
- 2024-09-25 springCloud负载均衡机制 springcloud负载均衡策略有哪些
- 2024-09-25 LabVIEW调用DLL中一、二级指针参数及打包exe运行异常的解决办法
你 发表评论:
欢迎- 05-23不用羡慕Mac,Windows电脑可以直接访问iPhone相册了
- 05-23换了电脑上不去网怎么回事?看看如何修改mac地址!
- 05-23怎么查看打印机IP地址
- 05-23查看电脑端口号的方法
- 05-23怎么查电脑局域网中的其他电脑ip地址
- 05-23如何查看笔记本电脑尺寸
- 05-23【网络】IP地址冲突如何快速定位?
- 05-23电脑系统改mac地址的方法
- 最近发表
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)