如何配置 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 测试自定义超时重试机制

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

相关推荐
FQNmxDG4S1 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全2 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje2 小时前
Java语法进阶
java·开发语言·jvm
uzong3 小时前
9 种 RAG 架构,每位 AI 开发者必学:完整实战指南
后端
rKWP8gKv73 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫3 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287923 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本3 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
止语Lab3 小时前
从手动到框架:Go DI 演进的三个拐点
开发语言·后端·golang
yaoxin5211233 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python