本篇文章的代码 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 配置重试机制
在配置类中定义一个Retryer
的Bean
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 )。还有其它的常见策略:
- 固定间隔:每次重试之间的时间间隔固定不变。
- 指数间隔:每次重试之间的时间间隔按指数递增。
- 随机间隔:每次重试之间的时间间隔是随机的。
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 测试自定义超时重试机制
根据时间变化可以看出我们自定义的超时重试机制生效了。