当前位置: 首页 > news >正文

Springboot实现重试机制

     背景

        研发工作中时常遇到要和其他服务对接,依赖对方能力的情况,最恶心的是对方提供的服务不稳定,时灵时不灵的,进而影响到自己功能的稳定性。万一发生了这种事,做为研发,咱该怎么办?通过容错直接抛出异常,让用户再试一次?那多low啊!一个优秀的研发很少将问题抛出去,一般都是自己尝试多遍且没办法之后,才会选择将问题反馈给用户。这就要求咱们的相关功能得有重试的能力。今天雷袭实践的课题就是在Springboot项目中实现接口自动重试机制。

     代码实践

      一、 纯手撸的重试机制

        雷袭看到这个课题,第一时间想的是:“这么简单,还需要水个博客?写个try catch加while循环,不是分分钟解决问题吗? 以下是我的第一版代码:

    public <T> T retryMethodLocal(Supplier<T> method, int maxRetries) {int retryCount = 0;while (retryCount <= maxRetries) {try {// 尝试执行方法return method.get();} catch (Exception e) {retryCount++;if (retryCount > maxRetries) {// 超过最大重试次数后抛出异常throw e;}System.out.println("第 " + retryCount + " 次重试...");}}return null; // 理论上不会到达这里}//测试方法,用于模拟一个不稳定的接口。当随机小数小于0.8时,抛错,public String testMethod(){StringBuilder builder = new StringBuilder("进入到testMethod方法");if (date != null){builder.append(",与上一次的时间间隔为:" + ( new Date().getTime()-date.getTime()));}date = new Date();log.info(builder.toString());// 示例:随机失败double random = Math.random();if (random < 0.8) {log.error("调用失败 " + random);throw new RuntimeException("调用失败 " + random);}log.error("调用成功 " + random);return "成功";}

        Controller方法如下:

    @GetMapping("/retryOne")public Object retryOne() {return leixiRetryService.retryMethodLocal(leixiRetryService::testMethod, 5);}

        通过浏览器访问链接:http://127.0.0.1:19200/leixi/retryOne , 以下是控制台信息:

        根据控制台的日志可知,重试机制是生效的。

      二、通过guava-retrying 实现重试

        以上方法虽然能解决问题,但是它太原生了。像重试这样的基础机制,Springboot肯定提供了相关的能力或组件,咱们完全不用自己写。在咨询了同事后,我又剽来了一种常用的方法,实现如下:

        pom.xml增加依赖:

       <dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version></dependency>

        service层添加方法:

    public <T> T retryMethodByRetryer(Supplier<T> method, Integer maxRetries, Integer waitTime) {Retryer<T> retryer = RetryerBuilder.<T>newBuilder()//.retryIfResult(result -> !result.equals("成功"))   //还可以通过返回结果判定是否重试.retryIfException().withWaitStrategy(WaitStrategies.fixedWait(waitTime, TimeUnit.MILLISECONDS)).withStopStrategy(StopStrategies.stopAfterAttempt(maxRetries)).withRetryListener(new RetryListener() {@Overridepublic <V> void onRetry(Attempt<V> attempt) {log.info("第【{}】次重试,距离首次调用已过【{}】ms", attempt.getAttemptNumber(),attempt.getDelaySinceFirstAttempt());}}).build();try {return retryer.call(method::get);} catch (RetryException | ExecutionException e) {log.error(">>> 重试多次,远程获取批量报告文档url失败 <<<", e);}return null ;}

        Controller层增加方法:

    @GetMapping("/retryTwo")public Object retryTwo() {return leixiRetryService.retryMethodByRetryer(() -> leixiRetryService.testMethod(), 5, 100);}

        通过浏览器访问链接:http://127.0.0.1:19200/leixi/retryTwo , 以下是控制台信息:

        该方法相比于第一版就优雅了很多,它通过封装的重试对象,可以精准的捕获到调用的次数,间隔时间,调用结果,还可以手动设置调用间隔,对于触发调用的条件也更加灵活。

      三、 通过spring-retry实现重试

        除了上述方法,Spring 还提供了一种重试策略,通过注解来实现,代码如下:

        添加pom依赖:

        <!-- 重试 --><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency>

        Application.java中添加重试注解:@EnableRetry

        Service层添加方法:

    @Retryable(maxAttempts = 5, backoff = @Backoff(value = 100L, multiplier = 1.5))public String retryByTarget() {return testMethod();}

         Controller中增加方法:

    @GetMapping("/retryThree")public Object retryThree() {return leixiRetryService.retryByTarget();}

        通过浏览器访问链接:http://127.0.0.1:19200/leixi/retryThree , 以下是控制台信息:

        相比于前两种方式,这种方法更加优雅,代码量更少,且通过注解的方式,可以很容易的对功能进行大批量的修改。请各位留意@Retryable中的@Backoff注解,它控制接口调用的间隔时间梯度递增,如第一次间隔100ms,第二次间隔100*1.5ms,第三次间隔100*1.5*1.5,以此类推,如此很好的避免了有故障的服务在短时间内接到大量重试调用,从而导致问题恶化的情况。

     小记

        要注意的是,配置了重试机制的功能,需要保证它在事务上是幂等的。即无论第一次执行成功与否,再执行第二次时,其结果都不会改变。多次执行的结果是一致的。

        雷袭很喜欢在工作中捕捉到能令自己惊艳的东西,比较各种工具/方法的不同实现方式,以增长见闻,提升眼界。每每发现到自己又有新的发现,总是欣喜莫名,程序员的乐趣,不外如是。

相关文章:

  • ebook2audiobook开源程序使用动态 AI 模型和语音克隆将电子书转换为带有章节和元数据的有声读物。支持 1,107+ 种语言
  • 从新手到高手:全面解析 AI 时代的「魔法咒语」——Prompt
  • 鸿蒙 PC 发布之后,想在技术上聊聊它的未来可能
  • talk-linux 不同用户之间终端通信
  • 攻防靶场——没有Web怎么打
  • 关于maven的依赖下不下来的问题
  • 加速度策略思路
  • WebPageTest 多地域测试
  • 描述性统计工具 - AxureMost 落葵网
  • 【手表维修专用软件】佳易王手表钟表保养维护服务跟踪管理系统:保养维护登记,维修进度跟踪!#手表维修管理系统教程 #铭表设备维修记录软件#操作简单软件下载
  • EasyOps®5月热力焕新:三大核心模块重构效能边界
  • NLTK进行文本分类和词性标注
  • ai讲vite的vite.config.ts的server配置
  • 2025.5.13山东大学软件学院计算机图形学期末考试回忆版本
  • 当三维地理信息遇上气象预警:电网安全如何实现“先知先觉”?
  • 2025.05.11拼多多机考真题算法岗-第三题
  • 虹科技术 | ANDi软件应用:SOME/IP协议如何重塑车载通信架构?
  • 火山引擎发展初始
  • FFmpeg多路节目流复用为一路包含多个节目的输出流
  • day011-12-老男孩教育-用户管理与软件管理体系-习题
  • AI含量非常高,2025上海教育博览会将于本周五开幕
  • 吉林:消纳绿电,“氢”装上阵
  • 上海现有超12.3万名注册护士,本科及以上学历占一半
  • 书法需从字外看,书法家、学者吴本清辞世
  • 水豚“豆包”出逃已40天,扬州茱萸湾景区追加悬赏
  • 成都锦江区一在建工地起火,致2人遇难1人受伤