如何配置 OpenFeign 超时重试机制?

本篇文章的代码 demo 是Nacos + OpenFeign:微服务中的服务注册、发现与调用 - 掘金 (juejin.cn)这一篇的,建议先阅读上一篇。

1.OpenFeign 的超时重试机制

在微服务架构中,服务之间是通过网络进行通信的,所以有的时候会经历网络异常或者服务响应超时。这时我们就需要给 OpenFeign 配置超时重试机制。OpenFeign 提供了一个 Retryer 接口,用于定义重试的逻辑和参数,例如重试的次数、间隔时间、最大间隔时间等。

1.1 配置超时时间

在模块consumer的配置文件中添加:

yml 复制代码
spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            connect-timeout: 1000 # 连接超时时间 1s
            read-timeout: 1000 # 读取超时时间 1s

1.2 配置重试机制

在配置类中定义一个RetryerBean

java 复制代码
@Configuration
public class RetryerConfig {
    @Bean
    public Retryer retryer(){
        return new Retryer.Default(
                1000, 		//重试间隔时间
                1000,       //最大重试间隔时间
                3           //最大重试次数
        );
    }
}

1.3 测试超时重试机制

在生产者模块pro_1中:

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getname")
    public String getNameById(Integer id) throws InterruptedException {
        System.out.println("---------超时重试中ing------------" + LocalDateTime.now());
        //睡眠 1.5s 模拟超时
        Thread.sleep(1500);
        return "lishi-"+id+"-pro_1";
    }
}

消费者consumer调用pro_1中的"/user/getname"接口:

service:

java 复制代码
@Service
@FeignClient("nacosRegisterDemo1") //表示调用 nacos 中的 nacosRegisterDemo1 服务
public interface UserService {

    /**
     * 调用"/user/getname接口"
     * @param id
     * @return
     */
    @RequestMapping("/user/getname" )
    String getNameById(@RequestParam("id") Integer id);
}

Controller:

java 复制代码
@RestController
public class BusinessController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getnamebyid")
    public String getNameById(Integer id){
        return userService.getNameById(id);
    }
}

访问"/getnamebyid"

显然访问失败。因为在pro_1我们设置睡眠1.5s,超过了read-timeout(读取超时时间)1s。这时会启动重试机制,不断尝试读取,可惜的是每次都将会超时,当重复最大重试次数过后还是超时的话就直接报错。

pro_1的日志:

重试了 3 次(第一次也算进重试次数),也印证了超时重试机制启动成功。

2.自定义超时重试机制

除了使用自带的超时重试机制,还可以自定义超时重试机制。

2.1 自定义超时重试类

实现 Retryer 接口,重写 continueOrPropagate 方法。

java 复制代码
public class CustomRetryer implements Retryer {

    private final int maxAttempts; //最大尝试次数

    private final long backoff;//重试间隔时间

    int attempt;//当前尝试次数

    public CustomRetryer(){
        this.maxAttempts = 3;
        this.backoff = 1000L;
        this.attempt = 0;
    }

    public CustomRetryer(int maxAttempts, long backoff) {
        this.maxAttempts = maxAttempts;
        this.backoff = backoff;
        this.attempt = 0;
    }

    
    @Override
    public void continueOrPropagate(RetryableException e) {
        if(attempt++ >= maxAttempts){
            throw e;
        }
        long interval = this.backoff; //重试间隔时间
        try {
            Thread.sleep(interval * attempt); //间隔时间线性增加
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public Retryer clone() {
        return new CustomRetryer();
    }
}

continueOrPropagate 方法是 Retryer 接口中最重要的方法,它接受一个 RetryableException 作为参数,没有返回值。当执行这个方法时,它要么抛出一个异常,要么成功退出(通常是在休眠一段时间后)。如果它没有抛出异常,Feign 将继续重试调用。如果抛出异常,它将被传播,从而有效地以错误结束调用。

Feign 客户端创建一个新的调用时,它会调用 clone 方法来获取一个新的 Retryer 实例,以避免重试状态的混乱。

这里的超时重试策略就是线性增加(每次增加 1s )。还有其它的常见策略:

  1. 固定间隔:每次重试之间的时间间隔固定不变。
  2. 指数间隔:每次重试之间的时间间隔按指数递增。
  3. 随机间隔:每次重试之间的时间间隔是随机的。

2.2 设置配置文件

yml 复制代码
spring:
  application:
    name: nacosRegisterDemo1
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        register-enabled: false # 不需要注册到注册中心
    openfeign:
      client:
        config:
          default:
            connect-timeout: 1000 # 连接超时时间 1s
            read-timeout: 1000 # 读取超时时间
            retryer: com.example.consumer.config.CustomRetryer # 设置自定义重试类

必须得指定该重试类才能生效。

2.3 测试自定义超时重试机制

根据时间变化可以看出我们自定义的超时重试机制生效了。

相关推荐
蓝澈112124 分钟前
迪杰斯特拉算法之解决单源最短路径问题
java·数据结构
Kali_0731 分钟前
使用 Mathematical_Expression 从零开始实现数学题目的作答小游戏【可复制代码】
java·人工智能·免费
rzl0243 分钟前
java web5(黑马)
java·开发语言·前端
时序数据说1 小时前
为什么时序数据库IoTDB选择Java作为开发语言
java·大数据·开发语言·数据库·物联网·时序数据库·iotdb
君爱学习1 小时前
RocketMQ延迟消息是如何实现的?
后端
guojl1 小时前
深度解读jdk8 HashMap设计与源码
java
Falling421 小时前
使用 CNB 构建并部署maven项目
后端
guojl1 小时前
深度解读jdk8 ConcurrentHashMap设计与源码
java
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端