🍓 简介:java系列技术分享(👉持续更新中…🔥)
🍓 初衷:一起学习、一起进步、坚持不懈
🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏
🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝

🍓 更多文章请点击
在这里插入图片描述在这里插入图片描述

前言

在微服务架构中,服务之间的调用不可避免会出现失败的情况(超时、异常、服务宕机等)。Feign 作为 Spring Cloud 中的声明式 HTTP 客户端,提供了强大的熔断降级机制,当远程调用失败时可以执行降级逻辑,返回兜底数据,避免整个链路崩溃。

本文将详细介绍 Feign 的三种降级策略,并通过完整代码示例进行对比。

一、环境准备

1.1 依赖引入

<!-- Spring Cloud OpenFeign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!-- 熔断器支持(可选,Spring Cloud 2020+ 需要) -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

1.2 配置开启

# application.yml
feign:
  circuitbreaker:
    enabled: true  # 开启 Feign 熔断器

# 可选:配置超时时间
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000

1.3 启动类注解

@SpringBootApplication
@EnableFeignClients  // 开启 Feign 客户端扫描
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

二、统一返回类型封装

为了方便演示,先定义一个统一的返回类型:

@Data
public class BaseResponse<T> {
    private Integer code;
    private String message;
    private T data;
    
    public static <T> BaseResponse<T> success(T data) {
        BaseResponse<T> response = new BaseResponse<>();
        response.setCode(200);
        response.setMessage("success");
        response.setData(data);
        return response;
    }
    
    public static <T> BaseResponse<T> failed(String message) {
        BaseResponse<T> response = new BaseResponse<>();
        response.setCode(500);
        response.setMessage(message);
        return response;
    }
}

三、策略一:fallback 方式(直接指定降级类)

3.1 Feign 客户端接口

@FeignClient(
    name = "user-service",
    fallback = UserFeignClientFallback.class
)
public interface UserFeignClient {
    
    @GetMapping("/user/{id}")
    BaseResponse<User> getUserById(@PathVariable("id") Long id);
    
    @GetMapping("/user/list")
    BaseResponse<List<User>> getUserList();
    
    @PostMapping("/user/save")
    BaseResponse<Void> saveUser(@RequestBody User user);
}

3.2 降级类实现

@Component
@Slf4j
public class UserFeignClientFallback implements UserFeignClient {
    
    @Override
    public BaseResponse<User> getUserById(Long id) {
        log.error("获取用户信息失败,userId: {}", id);
        return BaseResponse.failed("用户服务不可用,获取用户信息失败");
    }
    
    @Override
    public BaseResponse<List<User>> getUserList() {
        log.error("获取用户列表失败");
        return BaseResponse.failed("用户服务不可用,获取用户列表失败");
    }
    
    @Override
    public BaseResponse<Void> saveUser(User user) {
        log.error("保存用户失败,user: {}", user);
        return BaseResponse.failed("用户服务不可用,保存用户失败");
    }
}

3.3 特点说明

  • 代码简单,容易理解
  • 无法获取原始异常信息(不知道失败的具体原因)
  • 适合不需要记录异常详情的场景

四、策略二:fallbackFactory 方式 + 手动实现

4.1 Feign 客户端接口

@FeignClient(
    name = "order-service",
    fallbackFactory = OrderFeignClientFallbackFactory.class
)
public interface OrderFeignClient {
    
    @GetMapping("/order/{id}")
    BaseResponse<Order> getOrderById(@PathVariable("id") Long id);
    
    @GetMapping("/order/user/{userId}")
    BaseResponse<List<Order>> getOrdersByUserId(@PathVariable("userId") Long userId);
}

4.2 降级工厂类(手动实现每个方法)

@Component
@Slf4j
public class OrderFeignClientFallbackFactory implements FallbackFactory<OrderFeignClient> {
    
    @Override
    public OrderFeignClient create(Throwable cause) {
        return new OrderFeignClient() {
            
            @Override
            public BaseResponse<Order> getOrderById(Long id) {
                log.error("获取订单失败,订单ID: {},异常原因: {}", id, cause.getMessage(), cause);
                if (cause instanceof TimeoutException) {
                    return BaseResponse.failed("订单服务超时,请稍后重试");
                }
                return BaseResponse.failed("订单服务不可用,获取订单失败");
            }
            
            @Override
            public BaseResponse<List<Order>> getOrdersByUserId(Long userId) {
                log.error("获取用户订单列表失败,用户ID: {},异常原因: {}", userId, cause.getMessage());
                return BaseResponse.failed("订单服务不可用,获取订单列表失败");
            }
        };
    }
}

4.3 特点说明

  • 可以获取原始异常信息(通过 Throwable cause)
  • 可以为不同方法定制不同的降级逻辑
  • 代码量较大,接口方法多时重复代码多
  • 适合返回类型不统一的场景

五、策略三:fallbackFactory 方式 + 动态代理统一处理

5.1 Feign 客户端接口

@FeignClient(
    name = "logis-service",
    fallbackFactory = LogisBaseFeignClientFallbackFactory.class
)
public interface LogisBaseFeignClient {
    
    @GetMapping("/logis/queryTrace")
    BaseResponse<TraceInfo> queryTrace(@RequestParam("orderNo") String orderNo);
    
    @PostMapping("/logis/createOrder")
    BaseResponse<String> createOrder(@RequestBody LogisOrder order);
    
    @GetMapping("/logis/getCompany")
    BaseResponse<Company> getCompany(@RequestParam("code") String code);
}

5.2 降级工厂类(动态代理统一处理)

/**
 * 基础服务Feign降级工厂
 * 当Feign远程调用失败时,通过动态代理统一处理降级逻辑,
 * 记录错误日志并返回失败响应
 */
@Component
@Slf4j
public class LogisBaseFeignClientFallbackFactory implements FallbackFactory<LogisBaseFeignClient> {
    
    @Override
    public LogisBaseFeignClient create(Throwable cause) {
        return (LogisBaseFeignClient) Proxy.newProxyInstance(
            this.getClass().getClassLoader(),
            new Class[]{LogisBaseFeignClient.class},
            (Object proxy, Method method, Object[] args) -> {
                final String className = this.getClass().getName();
                log.error("Feign[{}#{}]失败,参数:{}", className, method.getName(), args, cause);
                return BaseResponse.failed(cause.getMessage());
            }
        );
    }
}

5.3 特点说明

  • 无论接口有多少方法,只需写一次降级逻辑
  • 可以获取原始异常信息
  • 接口新增方法时无需修改降级代码
  • 适合统一返回类型的场景

六、策略对比总结

维度fallbackfallbackFactory + 手动fallbackFactory + 动态代理
获取异常信息不能
代码量最少
灵活性
方法返回类型要求必须统一
新增方法维护成本需要添加需要添加无需改动
适用场景简单降级复杂差异化降级统一返回类型

七、Spring Cloud 支持的三种熔断器依赖

Spring Cloud Circuit Breaker 提供了断路器抽象层,支持多种实现

实现方案依赖坐标状态
Resilience4Jspring-cloud-starter-circuitbreaker-resilience4j✅ 推荐(主流)
Sentinelspring-cloud-starter-alibaba-sentinel✅ 推荐(阿里生态)
Hystrixspring-cloud-starter-netflix-hystrix❌ 已停止维护
Spring Retryspring-cloud-starter-circuitbreaker-spring-retry备选

如果项目有Sentinel则直接使用,参考我之前的文档:Sentinel

如果项目中没有Sentinel 那么选hystrixResilience4j 那个好呢

对于新项目,选 resilience4j,不要选 Hystrix

对比维度HystrixResilience4j
维护状态停止维护(Netflix 2018年宣布进入维护模式)持续活跃(定期发布新版本)
性能较重(线程池隔离开销大,存在跨线程上下文切换)轻量级(基于Java 8函数式编程,默认信号量隔离)
编程风格面向对象(继承 HystrixCommand)函数式/声明式(装饰器模式 + 注解)
功能丰富度熔断、降级、隔离熔断、重试、限流、舱壁、超时(一体化解决方案)
配置灵活性较固定(硬编码或属性文件,调整需重启)高度可定制(支持动态配置刷新)
与Spring Boot 2.x/3.x兼容性需额外配置,易出问题(Spring Cloud 移除了自动装配)原生支持良好(Spring Cloud Circuit Breaker 抽象)
学习曲线平缓稍陡但值得(功能更丰富,需要时间掌握)

熔断器降级增加下面两处配置即可

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>

feign:
  circuitbreaker:
    enabled: true

在这里插入图片描述在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐