在这里插入图片描述

01前言

什么时候用 fallback,什么时候用 fallbackFactory,以及怎样写才不会踩坑。

02 5 行对比表,一眼看懂差异

在这里插入图片描述

03代码实战

3.1 fallback 极简示例

@FeignClient(name = "payment-svc", fallback = PaymentFallback.class)
public interface PaymentClient {
    @PostMapping("/pay")
    PaymentResult pay(@RequestBody PaymentRequest req);
}

@Component
public class PaymentFallback implements PaymentClient {
    @Override
    public PaymentResult pay(PaymentRequest req) {
        // 固定返回失败,简单粗暴
        return PaymentResult.error("支付服务开小差了~");
    }
}

注解:

  1. @FeignClient 直接指向降级类 PaymentFallback。
  2. 整个容器里只有一个 PaymentFallback Bean,高并发时别忘了保证无状态。
  3. 无法感知异常类型,适合提示类兜底文案。

3.2 fallbackFactory 进阶示例

@FeignClient(name = "user-svc", fallbackFactory = UserFallbackFactory.class)
public interface UserClient {
    @GetMapping("/user/{id}")
    User getUser(@PathVariable Long id);
}

@Component
public class UserFallbackFactory implements FallbackFactory<UserClient> {
    private static final Logger log = LoggerFactory.getLogger(UserFallbackFactory.class);

    @Override
    public UserClient create(Throwable cause) {
        // 这里可以拿到异常,做各种骚操作
        return new UserClient() {
            @Override
            public User getUser(Long id) {
                log.warn("用户服务跪了, 原因: {}", cause.getMessage());

                if (cause instanceof FeignException.NotFound) {
                    return User.unknown(id);          // 404 → 未知用户
                } else if (cause instanceof TimeoutException) {
                    return User.temporary(id);        // 超时 → 临时用户
                }
                return User.defaultUser(id);          // 其他 → 默认用户
            }
        };
    }
}

注解:

  1. 每次远程调用失败都会触发 create(Throwable),异常对象随便玩。
  2. 返回的是匿名内部类,天然线程封闭,再也不用加锁。
  3. 根据异常类型返回不同策略,让降级更智能。

04线程安全对比

为什么 fallbackFactory 更省心?

// ❌ 错误示范:fallback 单例里带状态
@Component
public class UnsafeFallback implements SomeClient {
    private int counter = 0;
    public String count() {
        counter++;                // 多线程并发会乱
        return "Count: " + counter;
    }
}

// ✅ 正确姿势:fallbackFactory 每次都 new
public class SafeFallbackFactory implements FallbackFactory<SomeClient> {
    public SomeClient create(Throwable cause) {
        return new SomeClient() {
            private int counter = 0;   // 线程私有,安全!
            public String count() {
                counter++;
                return "Count: " + counter;
            }
        };
    }
}

05混合打法

又想打日志又想高性能?

public class HybridFallbackFactory implements FallbackFactory<SomeClient> {
    // 预先 new 好一个单例,避免重复创建
    private static final SomeClient STATIC_FALLBACK = new StaticFallback();

    @Override
    public SomeClient create(Throwable cause) {
        // 只做日志记录,然后返回同一个单例
        log.error("远程调用失败", cause);
        return STATIC_FALLBACK;
    }

    private static class StaticFallback implements SomeClient {
        public String getData() {
            return "static-fallback";
        }
    }
}

06性能小贴士
高并发如何减轻 fallbackFactory 的 GC 压力?

public class CachingFallbackFactory implements FallbackFactory<SomeClient> {
    private final Map<Class<? extends Throwable>, SomeClient> cache = new ConcurrentHashMap<>();

    @Override
    public SomeClient create(Throwable cause) {
        return cache.computeIfAbsent(cause.getClass(),
            k -> new SomeClient() {
                public String get() {
                    return "Error: " + k.getSimpleName();
                }
            });
    }
}

注解:

  1. 用异常类型做 key,同一个异常类只 new 一次。
  2. ConcurrentHashMap 线程安全,并发无锁。
  3. 适合异常类型有限且 QPS 极高的场景。

总结

• 业务简单、返回固定文案 → 用 fallback,写完收工。
• 需要看异常、想玩策略、还要线程安全 → 直接上 fallbackFactory。

更多推荐