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
執行過程源碼分析