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

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

相关推荐
天天向上杰2 分钟前
简识JVM的栈帧优化共享技术
java·jvm
方圆想当图灵21 分钟前
缓存之美:万文详解 Caffeine 实现原理(下)
java·redis·缓存
栗豆包35 分钟前
w175基于springboot的图书管理系统的设计与实现
java·spring boot·后端·spring·tomcat
等一场春雨1 小时前
Java设计模式 十四 行为型模式 (Behavioral Patterns)
java·开发语言·设计模式
萧若岚2 小时前
Elixir语言的Web开发
开发语言·后端·golang
Channing Lewis2 小时前
flask实现重启后需要重新输入用户名而避免浏览器使用之前已经记录的用户名
后端·python·flask
Channing Lewis2 小时前
如何在 Flask 中实现用户认证?
后端·python·flask
kerwin_code2 小时前
SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则
spring cloud·gateway·sentinel
微微%2 小时前
SpringCloud微服务Gateway网关简单集成Sentinel
spring cloud·微服务·gateway
酱学编程2 小时前
java中的单元测试的使用以及原理
java·单元测试·log4j