文章目录
前言
制作一个sarter实现 Feign 全局Fallback 断路器使用Resilience4J,默认情况开启断路器就启用全局fallback,可通过feign.circuitbreaker.globalfallback.enabled=true关闭全局fallback。直接上代码
一、新建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 {
}
}
}
二、新建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) {
//
}
}
}
三、新建GlobalFallBackFactory
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();
}
}
四、新建GlobalFallback
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);
}
}