Java 远程调用失败?如何优雅的进行重试? 您所在的位置:网站首页 安装远程过程调用失败怎么办 Java 远程调用失败?如何优雅的进行重试?

Java 远程调用失败?如何优雅的进行重试?

2024-06-21 20:55| 来源: 网络整理| 查看: 265

👇👇关注后回复 “进群” ,拉你进程序员交流群👇👇

作者丨鸭血粉丝Tang

来源丨Java极客技术(ID:Javageektech)

在日常开发的过程中我们经常会需要调用第三方组件或者数据库,有的时候可能会因为网络抖动或者下游服务抖动,导致我们某次查询失败。

这种时候我们往往就会进行重试,当重试几次后依旧还是失败的话才会向上抛出异常进行失败。接下来阿粉就给大家演示一下通常是如何做的,以及如何更优雅的进行重试。

常规做法

我们先来看一下常规做法,常规做法首先会设置一个重试次数,然后通过 while 循环的方式进行遍历,当循环次数没有达到重试次数的时候,直到有正确结果后就返回,如果重试依旧失败则会进行睡眠一段时间,再次重试,直到正常返回或者达到重试次数返回。

package com.example.demo.service; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import java.util.Random; import java.util.concurrent.TimeUnit; @Service public class HelloService {   public String sayHello(String name) {     String result = "";     int retryTime = 3;     while (retryTime > 0) {       try {         //         result = name + doSomething();         return result;       } catch (Exception e) {         System.out.println("send message failed. try again in 1's");         retryTime--;         try {           TimeUnit.SECONDS.sleep(1);         } catch (InterruptedException ex) {           throw new RuntimeException(ex);         }       }     }     return result;   }   private int doSomething() {     Random random = new Random();     int i = random.nextInt(3);     System.out.println("i is " + i);     return 10 / i;   } }

这里为了模拟异常的情况,阿粉在 doSomething 函数里面进行了随机数的生成和使用,当随机出来的值为 0 的时候,则会触发 java.lang.ArithmeticException 异常,因为 0 不能作除数。

接下来我们再对外提供一个接口用于访问,代码如下

package com.example.demo.controller; import com.example.demo.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController {   @Autowired   private HelloService helloService;   @GetMapping(value = "/hello")   public String hello(@RequestParam("name") String name) {     return helloService.sayHello(name);   } }

正常启动过后,我们通过浏览器进行访问

44c701652111bfc0b2916c05c3e2d8d7.jpeg

可以看到,我们第一次方法的时候就成功的达到了我们要的效果,随机数就是 0 ,在 1 秒后重试后结果正常。在多试了几次过后,会遇到三次都是 0 的情况,这个时候才会抛出异常,说明服务是真的有问题了。

1546e3ec641fefc5af07f9788a9d697c.jpeg 326630861c3ed05d37e89211ed5c283c.jpeg a3140966b2e1b2ad3f5c42050c61a606.jpeg

上面的代码可以看到是有效果了,虽然不是很好看,特别是在还有一些其他逻辑的情况,看上去会很臃肿,但是确实是可以正常使用的,那么有的小伙伴就要问了,有没有一种优雅的方式呢?总不能在很多地方都重复的这样写重试的代码吧。

注解重试

要知道我们普通人在日常开发的时候,如果遇到一个问题肯定是别人都遇到过的,什么时候当我们遇到的问题,没有人遇到过的时候,那说明我们是很前卫的。

因此小伙伴能想到的是不是有简单的方式来进行重试,有的人已经帮我们想好了,可以通过 @Retryable 注解来实现一样的效果,接下来阿粉就给大家演示一下如何使用这个注解。

首先我们需要在启动类上面加入 @EnableRetry 注解,表示要开启重试的功能,这个很好理解,就像我们要开启定时功能需要添加 @EnableScheduling 注解一样,Spring 的 @Enablexxx 注解也是很有意思的,后面我们再聊。

添加完注解以后,需要加入切面的依赖,如下

  org.aspectj   aspectjweaver   1.9.2

如下不加入这个切面依赖,启动的时候会有如下异常

c7629a9e527d7fdbd693fd9b7daae9ae.jpeg

添加的注解和依赖过后,我们需要改造 HelloService 里面的 sayHello() 方法,简化成如下,增加  @Retryable 注解,以及设置相应的参数值。

@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))   public String sayHello(String name){     return name + doSomething();   }

再次通过浏览器访问 http://127.0.0.1:8080/hello?name=ziyou 我们看到效果如下,跟我们自己写的重试一样。

7d20c38cb07a35afecf7793a51f1f6af.jpeg @Retryable 详解 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.retry.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Retryable {     String recover() default "";     String interceptor() default "";     Class


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有