写在前面
公司项目要从 spring boot 2.2.x 升级到 2.7,领导让我负责调研。
拿到手一看,好家伙,hystrix 停更了?feign 的 fallback 怎么报错了?ribbon 也不推荐用了?
网上的文档要么太深奥,要么太简单。没办法,只能自己一点点啃,顺便把踩坑经历记下来。
如果你也是新手,希望这篇笔记能帮到你。
环境信息
- spring boot: 2.2.x → 2.7.18
- spring cloud: hoxton.sr9 → 2021.x
- hystrix: 1.5.x(停更)→ resilience4j 1.7.x
- ribbon: 2.x(停更)→ spring cloud loadbalancer
- openfeign: 2.2.x → 3.1.x
坑 1:hystrix 停更了,用啥替代?
我当时以为很简单
直接升级 spring boot 和 spring cloud,心想依赖会自动更新…
<!-- 我当时看到这个配置,心想这不简单吗 --> <spring-boot.version>2.7.18</spring-boot.version> <spring-cloud.version>2021.x</spring-cloud.version>
结果启动失败:
*************************** application failed to run *************************** description: the spring cloud project contains incompatible modules: spring-cloud-starter-netflix-hystrix is not compatible with spring cloud 2021.x and later.
我当时就懵了。啥?hystrix 不兼容?
问题根源
后来查资料才明白:
- hystrix 停更:netflix hystrix 从 2020 年开始就进入维护模式,不再更新
- spring cloud 移除:spring cloud 2020.x(illford)开始移除了对 hystrix 的支持
- 官方推荐:spring 官方推荐使用 resilience4j 作为替代方案
好家伙,原来是时代的眼泪。
解决方案
用 resilience4j 替代 hystrix
<!-- 移除 hystrix 依赖 -->
<!--
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-netflix-hystrix</artifactid>
</dependency>
-->
<!-- 添加 resilience4j 依赖 -->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-circuitbreaker-resilience4j</artifactid>
</dependency>配置 resilience4j:
# application.yml
resilience4j:
circuitbreaker:
instances:
backenda: # 服务名
registerhealthindicator: true
slidingwindowsize: 10
failureratethreshold: 50
waitdurationinopenstate: 10000
permittednumberofcallsinhalfopenstate: 5
automatictransitionfromopentohalfopenenabled: true
timeout:
timeout: 3s
cancelrunningfuture: false使用方式:
@service
public class myservice {
@circuitbreaker(name = "backenda", fallbackmethod = "fallback")
public string callexternalservice() {
// 调用外部服务
return "success";
}
// fallback 方法
public string fallback(exception e) {
log.error("服务调用失败,执行 fallback: {}", e.getmessage());
return "fallback result";
}
}
教训:hystrix 已经停更了,别再用!官方推荐 resilience4j!
坑 2:feign 的 fallbackfactory 报错了
我只排除了部分依赖
升级后,feign client 的 fallback 全部报错:
// 原来的代码
@feignclient(name = "soa-protocol-control",
fallbackfactory = controlvehiclefeignservicefallback.class)
public interface controlvehiclefeignservice {
// ...
}
启动报错:
java.lang.illegalstateexception: fallbackfactory must return an instance of the feignclient interface
我当时又懵了。这又是啥?
原因分析
后来才明白:
- hystrix 移除:
feign.hystrix.fallbackfactory是 hystrix 时代的产物 - 包路径变更:spring cloud 2021.x 开始,fallbackfactory 的包路径变了
- 返回值要求:fallback 必须返回 feignclient 接口的实例
正确做法
1. 修改导入包
// ❌ 原来的导入(hystrix 时代) import feign.hystrix.fallbackfactory; // ✅ 现在的导入(resilience4j 时代) import org.springframework.cloud.openfeign.fallbackfactory;
2. 实现 fallbackfactory
@slf4j
@component
public class controlvehiclefeignservicefallbackfactory
implements fallbackfactory<controlvehiclefeignservice> {
@override
public controlvehiclefeignservice create(throwable cause) {
log.error("---控车接口回退异常:{}", cause);
return new controlvehiclefeignservice() {
@override
public baseresponsevo<long> executeinstruct(usercontrolvehicledto dto) {
return baseresponsevo.fail(protocol_control_error);
}
@override
public baseresponsevo<long> selectcurrentrideuseridbyvin(string vin) {
return baseresponsevo.fail(protocol_control_error);
}
// ... 其他方法都返回 fallback 结果
};
}
}
3. 使用 fallbackfactory
@feignclient(
name = "soa-protocol-control",
contextid = "usercontrolvehicle",
fallbackfactory = controlvehiclefeignservicefallbackfactory.class // 注意是 fallbackfactory
)
public interface controlvehiclefeignservice {
@postmapping("/protocol-control/user/control/vehicle")
baseresponsevo<long> executeinstruct(@requestbody usercontrolvehicledto dto);
@getmapping("/protocol-control/current/ride-user-id")
baseresponsevo<long> selectcurrentrideuseridbyvin(@requestparam string vin);
// ... 其他接口
}
教训:fallbackfactory 的包路径变了,别用错导入!
坑 3:ribbon 也不推荐用了?
我以为禁用就完事了
升级后发现 ribbon 也停更了,启动时各种警告:
warn: ribbon is now in maintenance mode.
consider using spring cloud loadbalancer instead.
这又是啥?
原因分析
后来才明白:
- ribbon 停更:netflix ribbon 也进入维护模式
- 官方推荐:spring cloud loadbalancer 作为替代
- 配置变更:loadbalancer 的配置方式和 ribbon 不一样
正确做法
1. 添加依赖
<!-- spring cloud loadbalancer -->
<dependency>
<groupid>org.springframework.cloud</groupid>
<artifactid>spring-cloud-starter-loadbalancer</artifactid>
</dependency>2. 配置 loadbalancer
# application.yml
spring:
cloud:
loadbalancer:
ribbon:
enabled: false # 禁用 ribbon
cache:
ttl: 30000 # 缓存过期时间(毫秒)
capacity: 256 # 缓存容量3. 自定义负载均衡策略
@configuration
public class loadbalancerconfig {
@bean
@loadbalanced
public resttemplate resttemplate() {
return new resttemplate();
}
@bean
public reactorloadbalancer<serviceinstance> randomloadbalancer(
loadbalancerclientfactory clientfactory) {
return new randomloadbalancer(
clientfactory.getlazyprovider("service-name", serviceinstancelistsupplier.class),
"service-name"
);
}
}
4. 使用 loadbalancer
@service
public class myservice {
@autowired
private resttemplate resttemplate;
public string callservice(string data) {
// 使用服务名调用,loadbalancer 会自动做负载均衡
string url = "http://service-name/api/" + data;
return resttemplate.getforobject(url, string.class);
}
}
教训:ribbon 已经停更了,赶紧换成 loadbalancer!
配置对比(hoxton vs 2021.x)
hoxton 配置(自动配置)
// 啥都不用配,hystrix 自动帮你搞定
@enablehystrix
@enablefeignclients
@springbootapplication
public class application {
// ...
}
好处:简单,省事
坏处:
- hystrix 停更了
- 性能不如 resilience4j
- 配置不灵活
2021.x 配置(手动配置)
// 启用 resilience4j + openfeign + loadbalancer
@enablefeignclients
@springbootapplication
public class application {
// ...
}
// resilience4j 配置
@configuration
public class resilience4jconfig {
@bean
public circuitbreakerconfig circuitbreakerconfig() {
return circuitbreakerconfig.custom()
.failureratethreshold(50)
.waitdurationinopenstate(duration.ofseconds(10))
.slidingwindowsize(10)
.build();
}
}
好处:
- 官方推荐,持续更新
- 性能更好
- 配置更灵活
坏处:配置多了点
我的感受:
- 虽然配置多了,但心里有底
- resilience4j 的 api 更现代
- 出了问题知道从哪查
- 推荐大家都这么配(虽然麻烦点)
fallback 和 fallbackfactory 的区别
fallback:简单场景
适用场景:只需要返回默认值
@feignclient(
name = "user-service",
fallback = userfeignclientfallback.class // 直接返回实例
)
public interface userfeignclient {
@getmapping("/user/{id}")
user getuser(@pathvariable long id);
}
@component
public class userfeignclientfallback implements userfeignclient {
@override
public user getuser(long id) {
return new user(0l, "default", "default@example.com");
}
}
好处:简单,代码少
坏处:拿不到异常信息
fallbackfactory:复杂场景(推荐)
适用场景:需要记录异常日志、根据异常类型做不同处理
@feignclient(
name = "user-service",
fallbackfactory = userfeignclientfallbackfactory.class // 工厂模式
)
public interface userfeignclient {
@getmapping("/user/{id}")
user getuser(@pathvariable long id);
}
@slf4j
@component
public class userfeignclientfallbackfactory
implements fallbackfactory<userfeignclient> {
@override
public userfeignclient create(throwable cause) {
log.error("feign 调用失败:{}", cause.getmessage()); // 可以记录日志
return new userfeignclient() {
@override
public user getuser(long id) {
// 根据异常类型做不同处理
if (cause instanceof connectexception) {
return new user(0l, "network-error", "连接失败");
} else {
return new user(0l, "unknown-error", "未知错误");
}
}
};
}
}
好处:
- 可以记录异常日志
- 可以根据异常类型做不同处理
- 更灵活
坏处:代码多了点
我的建议:
- 简单场景用 fallback
- 生产环境推荐用 fallbackfactory(可以记录日志)
验证步骤
1. 检查依赖
# 查看项目依赖树 mvn dependency:tree | select-string "hystrix|resilience4j|ribbon|loadbalancer"
预期结果:
- 不应该有
spring-cloud-starter-netflix-hystrix - 应该有
spring-cloud-starter-circuitbreaker-resilience4j - 不应该有
ribbon - 应该有
spring-cloud-starter-loadbalancer
2. 启动测试
启动应用,确认没有以下警告:
❌ hystrix is not compatible with spring cloud 2021.x ❌ ribbon is now in maintenance mode
3. 测试 fallback
// 手动触发异常,测试 fallback 是否生效
@test
public void testfallback() {
// 模拟服务不可用
string result = feignclient.callservice();
assertequals("fallback result", result);
}
我整理的一些配置
resilience4j 推荐配置
# 这些是我实测好用的
resilience4j:
circuitbreaker:
instances:
backenda:
registerhealthindicator: true
slidingwindowsize: 10
failureratethreshold: 50
waitdurationinopenstate: 10000
permittednumberofcallsinhalfopenstate: 5
automatictransitionfromopentohalfopenenabled: true
timelimiter:
timeoutduration: 3sloadbalancer 推荐配置
spring:
cloud:
loadbalancer:
ribbon:
enabled: false
cache:
ttl: 30000
capacity: 256监控指标
# resilience4j 的监控指标(要加到 prometheus 里) resilience4j.circuitbreaker.states resilience4j.circuitbreaker.failure.rate resilience4j.circuitbreaker.slow.call.rate
回滚方案
如果升级后出现问题,可以快速回滚:
- 恢复
pom.xml中的 spring boot 和 spring cloud 版本 - 恢复 hystrix 依赖
- 恢复 ribbon 依赖
- 恢复原来的 fallback 实现
- 重新编译部署
教训:升级前一定要留好退路!
总结
我踩过的坑
- 坑 1:以为 hystrix 还能用 → 启动报不兼容错误
- 坑 2:fallbackfactory 导入错误 → 报返回值类型错误
- 坑 3:ribbon 没换成 loadbalancer → 各种警告
核心要点
- 必须替换 hystrix:用 resilience4j,官方推荐
- 必须修改 fallbackfactory 导入:从
feign.hystrix换成org.springframework.cloud.openfeign - 必须替换 ribbon:用 spring cloud loadbalancer
- 必须测试 fallback:确保服务降级正常工作
最后说两句
其实也没多难,就是配置多了点。
我刚开始也懵,后来一点点试,总算是搞定了。
肯定有理解不对的地方,欢迎大佬指正。
如果你也遇到类似问题,希望这篇笔记能帮到你。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论