Feign集成Hystrix源码分析(Targeter实现类)
Targeter接口
上章详解了,加了@FeignClient
的类最后注入的bean类型是Targeter.target
返回的值
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 找到容器中bean类型为Targeter的bean
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
这里会根据Targeter.class
找到实现了Targeter
接口的bean
默认定好了两个实现了Targeter
接口的类,但只会注入一个到容器中
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
DefaultTargeter
DefaultTargeter
是默认的实现,不会进行降级处理
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {、
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
return 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 <T> T getOptional(String feignClientName, FeignContext context,
Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
}
是否开启feign.hystrix.enabled
判断当前Feign.Builder
类型是不是HystrixFeign.builder()
因为只有当feign.hystrix.enabled=true
时
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
而feign.hystrix.enabled=false
时
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
也就是说,没有开启降级处理时,直接执行return feign.target(target);
fallback
为Feign客户端的定义接口编写一个具体的接口实现类,比如为HelloService接口实现一个服务降级类HelloServicFallback,其中每个重写方法的实现逻辑都可以用来定义相应的服务降级逻辑
@FeignClient(value = "hello-service",fallback = HelloServiceFallback.class)
@Service
public interface HelloService {
@RequestMapping("/hello")
String hello();
@RequestMapping("/hello1")
String hello(@RequestParam("name") String name);
@RequestMapping("/hello2")
User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
@RequestMapping("/hello3")
String hello(@RequestBody User user);
}
@Component
public class HelloServiceFallback implements HelloService {
@Override
public String hello() {
return "error";
}
@Override
public String hello(String name) {
return "error";
}
@Override
public User hello(String name, Integer age) {
return new User("未知",0);
}
@Override
public String hello(User user) {
return "error";
}
}
在HystrixTargeter
中
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
fallbackFactory
需要自定义实现FallbackFactory
接口,重写T create(Throwable cause)
方法,然后在@FeignClient
中指定fallbackFactory
@FeignClient(name="spring-boot-user", fallbackFactory=HystrixClientFallbackFactory.class)
public interface UserFeignClient {
@RequestMapping(value="/simple/{id}", method=RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
public interface HystrixClientWithFallbackFactory extends UserFeignClient {
}
@Component
public class HystrixClientFallbackFactory implements FallbackFactory<UserFeignClient> {
@Override
public UserFeignClient create(Throwable arg0) {
// TODO Auto-generated method stub
return new HystrixClientWithFallbackFactory() {
@Override
public User findById(Long id) {
// TODO Auto-generated method stub
User user = new User();
user.setId(-1L);
return user;
}
};
}
}
在HystrixTargeter
中
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
没有指定fallback与fallbackFactory
如果在@FeignClient
没有指定fallback
与fallbackFactory
,则最后走return feign.target(target);
,即不降级处理
自定义全局降级处理
我们看到,要实现hystrix降级处理,必须指定fallback
与fallbackFactory
,但是每一个feign接口方法都要写相应的降级方法,太过有点繁琐了。
我们可以仿照之前写过的fallbackFactory
,最后执行的是重写的T create(Throwable cause)
方法,但是之前的fallbackFactory
很局限性,每个接口的方法都要重写。
我们可以自定义FallbackFactory
实现类,然后通过动态代理增强加了@FeignClient
注解的接口方法
然后在自定义Targeter
实现类中调用
MyFallbackFactory pigxFallbackFactory = new MyFallbackFactory (target);
return (T) builder.target(target, pigxFallbackFactory);
自定义FallbackFactory实现类
通过cglib动
动态代理增强加了@FeignClient
注解的接口方法,以前都是单独实现接口方法。
@AllArgsConstructor
public class TCloudFallbackFactory<T> implements FallbackFactory<T> {
private final Target<T> target;
@Override
@SuppressWarnings("unchecked")
public T create(Throwable cause) {
// @FeignClient注解的接口类型
final Class<T> targetType = target.type();
final String targetName = target.name();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetType);
enhancer.setUseCache(true);
enhancer.setCallback(new TCloudFeignFallback<>(targetType, targetName, cause));
return (T) enhancer.create();
}
}
create
是怎么被调用的,这一部分在源码中体现在HystrixInvocationHandler.java
cglib代理处理类
@Slf4j
@AllArgsConstructor
public class MyFeignFallback<T> implements MethodInterceptor {
private final Class<T> targetType;
private final String targetName;
private final Throwable cause;
@Nullable
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Class<?> returnType = method.getReturnType();
if (R.class != returnType) {
return null;
}
FeignException exception = (FeignException) cause;
byte[] content = exception.content();
String str = StrUtil.str(content, StandardCharsets.UTF_8);
// 降级信息
log.error("MyFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, str);
return R.builder().code(CommonConstants.FAIL)
.msg(str).build();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MyFeignFallback<?> that = (MyFeignFallback<?>) o;
return targetType.equals(that.targetType);
}
@Override
public int hashCode() {
return Objects.hash(targetType);
}
}
自定义Targeter实现类
@Primary
@Configuration
@ConditionalOnClass(HystrixFeign.class)
@ConditionalOnProperty("feign.hystrix.enabled")
class MyHystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof HystrixFeign.Builder)) {
return feign.target(target);
}
HystrixFeign.Builder builder = (HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
}
// 设置自定义的FallbackFactory
MyFallbackFactory pigxFallbackFactory = new MyFallbackFactory(target);
return (T) builder.target(target, pigxFallbackFactory);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder, Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
return 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;
}
@Nullable
private <T> T getOptional(String feignClientName, FeignContext context, Class<T> beanType) {
return context.getInstance(feignClientName, beanType);
}
注意,自定义Targeter
实现类一定要加上@Primary
。
因为在FeignClientFactoryBean
中是根据Targeter.class
类型获取Targeter
实例
而feign内部已经装配了其他Targeter
实现类,比如HystrixTargeter
下章节,Targeter
执行过程源码分析