SpringCloud Feign 全局Fallback实现【SpringBoot2.7+】

本文档介绍了如何利用Resilience4J创建Feign的全局Fallback和断路器。通过配置`FeignExtensionAutoConfiguration`,在断路器开启时自动启用全局fallback,当断路器关闭时可以手动关闭。`FeignCircuitBreakerTargeter`类负责处理 fallback 实例的创建,而`GlobalFallBackFactory`和`GlobalFallback`则提供了全局fallback的实现。通过这种方式,可以在服务调用失败时提供备用响应,增强系统的容错能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

在微服务架构中,服务之间的通信是至关重要的。随着微服务规模的扩大,服务调用的复杂性也在不断增加。Spring Cloud Feign作为Spring Cloud生态中的一个核心组件,提供了一种简洁、声明式的方式来进行服务间的调用。通过 Feign,开发者可以像调用本地方法一样调用远程服务,大大减少了编写复杂 HTTP 请求代码的工作量。Feign与Spring Cloud的其他组件(如Eureka、Ribbon)结合使用,能够实现更加高效、灵活的服务调用机制。本文将介绍Spring Cloud Feign的基本概念、使用方式及其在微服务架构中的优势,帮助开发者更好地理解并应用Feign进行服务间通信。

一、Feign 什么是?

Spring Cloud Feign(现在更准确地说是 OpenFeign)是 Spring Cloud 提供的一个 声明式 HTTP 客户端。它的作用是:

让你调用其他微服务时,就像调用本地 Java 接口一样,不需要手写 RestTemplate 或 WebClient 的代码。

二、Feign主要功能

功能
✅ 声明式 HTTP 调用写接口 + 注解即可发 HTTP 请求
🔄 服务发现集成和 Spring Cloud Eureka/Nacos 无缝集成
⚙️ 负载均衡支持默认支持 Ribbon / LoadBalancerClient
💥 支持断路器可和 Sentinel / Resilience4j 配合使用
🔧 支持拦截器可配置请求头、日志、认证等
📦 支持请求/响应压缩、超时、重试等配置

三、全局Fallback实现

制作一个sarter实现 Feign 全局Fallback 断路器使用Resilience4J,默认情况开启断路器就启用全局fallback,可通过feign.circuitbreaker.globalfallback.enabled=true关闭全局fallback。

1.新建FeignExtensionAutoConfiguration

自动启动类

@AutoConfiguration(before = FeignAutoConfiguration.class)
@ConditionalOnClass(Feign.class)
public class FeignExtensionAutoConfiguration {


    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Resilience4JCircuitBreakerFactory.class)
    @Conditional(GlobalFallbackCondition.class)
    protected static class CircuitBreakerConfiguration {

        @Bean
        public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory,
                @Value("${feign.circuitbreaker.group.enabled:false}") boolean circuitBreakerGroupEnabled,
                CircuitBreakerNameResolver circuitBreakerNameResolver) {
            return new FeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerGroupEnabled, circuitBreakerNameResolver);
        }

    }

    static class GlobalFallbackCondition extends AllNestedConditions {

        GlobalFallbackCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }

        @ConditionalOnProperty(prefix = "feign.circuitbreaker",name = "enabled",havingValue = "true")
        static class FeignCircuitbreakerEnabled {

        }

        @ConditionalOnProperty(prefix = "feign.circuitbreaker",name = "globalfallback.enabled", havingValue = "true", matchIfMissing = true)
        static class FeignGlobalCircuitbreakerEnabled {

        }

    }

}

2.新建FeignCircuitBreakerTargeter

代理目标

class FeignCircuitBreakerTargeter implements Targeter {
    private final Map<String,Method> map = new HashMap<>();

    private final CircuitBreakerFactory circuitBreakerFactory;
    private final boolean circuitBreakerGroupEnabled;
    private final CircuitBreakerNameResolver circuitBreakerNameResolver;


    FeignCircuitBreakerTargeter(CircuitBreakerFactory circuitBreakerFactory, boolean circuitBreakerGroupEnabled, CircuitBreakerNameResolver circuitBreakerNameResolver) {
        this.circuitBreakerFactory = circuitBreakerFactory;
        this.circuitBreakerGroupEnabled = circuitBreakerGroupEnabled;
        this.circuitBreakerNameResolver = circuitBreakerNameResolver;
        for (Method method : ReflectionUtils.getDeclaredMethods(FeignCircuitBreaker.Builder.class)) {
            map.put(method.getName(),method);
        }
    }

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
                        Target.HardCodedTarget<T> target) {
        if (!(feign instanceof FeignCircuitBreaker.Builder)) {
            return feign.target(target);
        }
        FeignCircuitBreaker.Builder builder = (FeignCircuitBreaker.Builder) feign;
        String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
        Class<?> fallback = factory.getFallback();
        if (fallback != void.class) {
            return targetWithFallback(name, context, target, builder, fallback);
        }
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
        }
        return builder(name, builder).target(target,new GlobalFallBackFactory<>(target));
    }

    private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
                                            Target.HardCodedTarget<T> target, FeignCircuitBreaker.Builder builder, Class<?> fallbackFactoryClass) {
        FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext("fallbackFactory",
                feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
        return builder(feignClientName, builder).target(target, fallbackFactory);
    }

    private <T> T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget<T> target,
                                     FeignCircuitBreaker.Builder builder, Class<?> fallback) {
        T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
        return builder(feignClientName, builder).target(target, fallbackInstance);
    }

    private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
                                 Class<?> beanType, Class<T> targetType) {
        Object fallbackInstance = context.getInstance(feignClientName, beanType);
        if (fallbackInstance == null) {
            throw new IllegalStateException(
                    String.format("No " + fallbackMechanism + " instance of type %s found for feign client %s",
                            beanType, feignClientName));
        }

        if (!targetType.isAssignableFrom(beanType)) {
            throw new IllegalStateException(String.format("Incompatible " + fallbackMechanism
                            + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
                    beanType, targetType, feignClientName));
        }
        return (T) fallbackInstance;
    }

    private FeignCircuitBreaker.Builder builder(String feignClientName, FeignCircuitBreaker.Builder builder) {
        invoke(map.get("circuitBreakerFactory"), builder, circuitBreakerFactory);
        invoke(map.get("feignClientName"), builder, feignClientName);
        invoke(map.get("circuitBreakerGroupEnabled"), builder, circuitBreakerGroupEnabled);
        invoke(map.get("circuitBreakerNameResolver"), builder, circuitBreakerNameResolver);
        return builder;
    }

    private void invoke(Method method, FeignCircuitBreaker.Builder builder, Object... args){
        ReflectionUtils.makeAccessible(method);
        try {
            method.invoke(builder,args);
        } catch (Exception e) {
            //
        }
    }

}

3.新建GlobalFallBackFactory

全局FallBack工厂类

public class GlobalFallBackFactory<T> implements FallbackFactory<T> {

    private final Target<T> target;

    public GlobalFallBackFactory(Target<T> target) {
        this.target = target;
    }

    @Override
    @SuppressWarnings("unchecked")
    public T create(Throwable cause) {
        final Class<T> targetType = target.type();
        final String targetName = target.name();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetType);
        enhancer.setUseCache(true);
        enhancer.setCallback(new GlobalFallback<>(targetType, targetName, cause));
        return (T) enhancer.create();
    }

}

4.新建GlobalFallback

全局FallBack

public class GlobalFallback<T> implements MethodInterceptor {

    private static final Logger log = LoggerFactory.getLogger(GlobalFallback.class);

    private final Class<T> targetType;
    private final String targetName;
    private final Throwable cause;

    public GlobalFallback(Class<T> targetType, String targetName, Throwable cause) {
        this.targetType = targetType;
        this.targetName = targetName;
        this.cause = cause;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String errorMessage = cause.getMessage();
        log.error("GlobalFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage);
        if (!(cause instanceof FeignException)) {
            return ExceptionHelper.error(errorMessage);
        }
        FeignException exception = (FeignException) cause;
        if(exception.contentUTF8().isEmpty()){
            return ExceptionHelper.error(errorMessage);
        }
        return ExceptionHelper.error(exception.contentUTF8());
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        GlobalFallback<?> that = (GlobalFallback<?>) o;
        return targetType.equals(that.targetType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(targetType);
    }

}

总结

Spring Cloud Feign使得微服务之间的通信变得简单而高效。通过声明式的接口方式,开发者不需要关心底层的HTTP请求细节,只需要定义接口,Feign会自动完成服务调用的相关操作。结合Spring Cloud的其他组件,Feign不仅提供了负载均衡、服务发现等功能,还能够与Spring Security、Spring Retry等进行集成,增强服务调用的稳定性和安全性。总的来说,Feign作为微服务架构中的重要工具,通过简化服务间通信,提升了开发效率,减少了开发和维护的复杂度,是构建高效、可靠微服务系统的重要组成部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kse_music

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值