文章目录
前言
在微服务架构中,服务之间的通信是至关重要的。随着微服务规模的扩大,服务调用的复杂性也在不断增加。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作为微服务架构中的重要工具,通过简化服务间通信,提升了开发效率,减少了开发和维护的复杂度,是构建高效、可靠微服务系统的重要组成部分。