OpenFeign 提供了一种声明式的远程调用接口,它可以大幅简化远程调用的编程体验。调用其他服务接口像调用本地服务service方法一样丝滑顺畅。使用示例如下:
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.2.3.RELEASEversion>
dependency>
order服务调product服务接口
@FeignClient(name = "${micro-server.mall-product}", path = "/api/mall/product")
public interface ProductService {
@GetMapping("/sku/{skuId}")
ResponseVO<ProductSku> getSku(@PathVariable("skuId") Long skuId);
@PostMapping("/sku/price")
ResponseVO<List<ProductSku>> getSkuPrice(@RequestBody SkuQuery query);
}
扫描feignClient
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(basePackages = "com.shepherd.mallorder.dao")
@ComponentScan(basePackages = {"com.shepherd"})
@EnableFeignClients(basePackages = "com.shepherd.mallorder")
@EnableRabbit //要想监听队列接收消息必须开启此注解
public class MallOrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MallOrderServiceApplication.class, args);
}
}
基于以上操作就可以完美实现服务之间接口的调用了。
OpenFeign 使用了动态代理技术来封装远程服务调用的过程。通过上面示例我们知道远程服务调用的信息被写在了 FeignClient 接口中。在上面的代码里,你可以看到,服务的名称、接口类型、访问路径已经通过注解做了声明。OpenFeign 通过解析这些注解标签生成一个“动态代理类”,这个代理类会将接口调用转化为一个远程服务调用的 Request,并发送给目标服务。
当我们项目启动时,会通过@EnableFeignClients(basePackages = "com.shepherd.mallorder")
扫描相应包路径下的feignClient,然后使用动态代理技术生成代理类注入到spring容器之中,这样当我们使用feign接口方法发起远程调用,其实就是使用生成的动态代理类去实现请求。
上图中的步骤 1 到步骤 3 是在项目启动阶段加载完成的,只有第 4 步“调用远程服务”是发生在项目的运行阶段。
下面我来解释一下上图中的几个关键步骤。
首先,在项目启动阶段,OpenFeign 框架会发起一个主动的扫包流程,从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口。然后,OpenFeign 会针对每一个 FeignClient 接口生成一个动态代理对象,即图中的FeignProxyService,这个代理对象在继承关系上属于 FeignClient 注解所修饰的接口的实例。
接下来,这个动态代理对象会被添加到 Spring 上下文中,并注入到对应的服务里,也就是图中的 LocalService 服务最后,LocalService 会发起底层方法调用。实际上这个方法调用会被 OpenFeign 生成的代理对象接管,由代理对象发起一个远程服务调用,并将调用的结果返回给LocalService。
那么, OpenFeign 是如何通过动态代理技术创建代理对象,并放到spring容器上下文中呢?
这时候我们想起了FactoryBean
就是为了创建复杂的bean,我们这里生成代理类并放到spring容器中正好可以由它实现。
1.项目加载:在项目的启动阶段,EnableFeignClients 注解扮演了“启动开关”的角色,它使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类,开始了OpenFeign 组件的加载过程。
2.扫包:FeignClientsRegistrar 负责 FeignClient 接口的加载,它会在指定的包路径下扫描所有的 FeignClients 类,并构造 FeignClientFactoryBean 对象来解析FeignClient 接口。
3.解析 FeignClient 注解:FeignClientFactoryBean 有两个重要的功能,一个是解析FeignClient 接口中的请求路径和降级函数的配置信息;另一个是触发动态代理的构造过程。其中,动态代理构造是由更下一层的 ReflectiveFeign 完成的。
4.构建动态代理对象:ReflectiveFeign 包含了 OpenFeign 动态代理的核心逻辑,它主要负责创建出 FeignClient 接口的动态代理对象。ReflectiveFeign 在这个过程中有两个重要任务,一个是解析 FeignClient 接口上各个方法级别的注解,将其中的远程接口URL、接口类型(GET、POST 等)、各个请求参数等封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理;另一个重要任务是将这些MethodHandler 方法代理做进一步封装,通过 Java 标准的动态代理协议,构建一个实现了 InvocationHandler 接口的动态代理对象,并将这个动态代理对象绑定到FeignClient 接口上。这样一来,所有发生在 FeignClient 接口上的调用,最终都会由它背后的动态代理对象来承接。
MethodHandler 的构建过程涉及到了复杂的元数据解析,OpenFeign 组件将FeignClient 接口上的各种注解封装成元数据,并利用这些元数据把一个方法调用“翻译”成一个远程调用的 Request 请求。
那么上面说到的“元数据的解析”是如何完成的呢?它依赖于 OpenFeign 组件中的Contract 协议解析功能。Contract 是 OpenFeign 组件中定义的顶层抽象接口,它有一系列的具体实现,其中和我们实战项目有关的是 SpringMvcContract 这个类,从这个类的名字中我们就能看出来,它是专门用来解析 Spring MVC 标签的。
接下来对openfeign的核心源码实现类和方法进行浅浅的分析。从EnableFeignClients
注解类开始:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
* {@code @ComponentScan(basePackages="org.my.pkg")}.
* @return the array of 'basePackages'.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components.
*
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
*
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
* @return the array of 'basePackages'.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated components. The package of each class specified will be scanned.
*
* Consider creating a special no-op marker class or interface in each package that
* serves no purpose other than being referenced by this attribute.
* @return the array of 'basePackageClasses'.
*/
Class<?>[] basePackageClasses() default {};
/**
* A custom @Configuration
for all feign clients. Can contain override
* @Bean
definition for the pieces that make up the client, for instance
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
*
* @see FeignClientsConfiguration for the defaults
* @return list of default configurations
*/
Class<?>[] defaultConfiguration() default {};
/**
* List of classes annotated with @FeignClient. If not empty, disables classpath
* scanning.
* @return list of FeignClient classes
*/
Class<?>[] clients() default {};
}
这个类的作用就是引入FeignClientsRegistrar
类:
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// 注册动态代理类feignProxyService入口
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
}
registerFeignClients(metadata, registry)
方法就是实现注册动态代理类放到spring容器中的:
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//指定扫描类注解类型为@FeignClient的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(ClassMetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
//扫描定义的Feign组件包路径
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClient(registry, annotationMetadata, attributes)
:
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//定义Feign组件的创建工厂FeignClientFactoryBean
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
这里可以看到使用了factoryBean
创建了feignclient组件bean
//定义Feign组件的创建工厂
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
接下来看看Feign代理Bean实例是如何创建的,FeignClientFactoryBean
:
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
//从Spring Context中获取到Feign的Builder
Feign.Builder builder = feign(context);
//@FeignClient注解没有配置URL属性
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
//处理@FeignClient URL属性(主机名)存在的情况
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
//获取到调用客户端:Spring封装了基于Ribbon的客户端(LoadBalancerFeignClient)
//1、Feign自己封装的Request(基于java.net原生),2、OkHttpClient(新一代/HTTP2),3、ApacheHttpClient(常规)
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
//设置调用客户端
builder.client(client);
}
//DefaultTargeter或者HystrixTargeter,其中HystrixTargeter带熔断和降级功能
//主要用户在Builder中配置调用失败回调方法
Targeter targeter = get(context, Targeter.class);
//Bean创建实际目标封装,最终生成InvocationHandler
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
}
执行target
方法时,最终会来到ReflectiveFeign
的newInstance
方法:
public <T> T newInstance(Target<T> target) {
//核心方法,解析定义的@FeignClient组件中的方法和请求路径
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if(Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
//调用工厂Bean,创建执行Handler
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
请求方法和路径解析器ParseHandlersByName
:
static final class ParseHandlersByName {
private final Contract contract;
private final Options options;
private final Encoder encoder;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final SynchronousMethodHandler.Factory factory;
ParseHandlersByName(Contract contract, Options options, Encoder encoder, Decoder decoder,
ErrorDecoder errorDecoder, SynchronousMethodHandler.Factory factory) {
this.contract = contract;
this.options = options;
this.factory = factory;
this.errorDecoder = errorDecoder;
this.encoder = checkNotNull(encoder, "encoder");
this.decoder = checkNotNull(decoder, "decoder");
}
public Map<String, MethodHandler> apply(Target key) {
//核心方法:解析@FeignClient组件,Feign自带的则解析自己的注解格式,Spring提供了解析MVC注解的SpringMvcContract
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
}
到这里,feignClient和接口方法的解析、封装已完成,并放入到spring容器之中。
接下来就是使用feignClient调用远程服务了,根据创建流程中Targeter对象,有2个实现,一个为DefaultTargeter,源码如下:
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
//简单暴力,直接调用Feign Builer字段的设置目标对象方法
return feign.target(target);
}
}
上面的方法最终会调用Feign原生的InvocationHandlerFactory.Default工厂来创建FeignInvocationHandler:
//第一部分:封装处理请求的Hander
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object
otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FeignInvocationHandler) {
FeignInvocationHandler other = (FeignInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
}
//第二部分:解析出来的实际处理请求的方法
final class SynchronousMethodHandler implements MethodHandler {
private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;
private final MethodMetadata metadata;
private final Target<?> target;
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
private final Options options;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final boolean decode404;
private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
List<RequestInterceptor> requestInterceptors, Logger logger,
Logger.Level logLevel, MethodMetadata metadata,
RequestTemplate.Factory buildTemplateFromArgs, Options options,
Decoder decoder, ErrorDecoder errorDecoder, boolean decode404) {
this.target = checkNotNull(target, "target");
this.client = checkNotNull(client, "client for %s", target);
this.retryer = checkNotNull(retryer, "retryer for %s", target);
this.requestInterceptors =
checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
this.logger = checkNotNull(logger, "logger for %s", target);
this.logLevel = checkNotNull(logLevel, "logLevel for %s", target);
this.metadata = checkNotNull(metadata, "metadata for %s", target);
this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target);
this.options = checkNotNull(options, "options for %s", target);
this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target);
this.decoder = checkNotNull(decoder, "decoder for %s", target);
this.decode404 = decode404;
}
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
//执行请求并解析
return executeAndDecode(template);
} catch (RetryableException e) {
retryer.continueOrPropagate(e);
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
Object executeAndDecode(RequestTemplate template) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
boolean shouldClose = true;
try {
if (logLevel != Logger.Level.NONE) {
response =
logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
}
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404) {
return decode(response);
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
}
throw errorReading(request, response, e);
} finally {
if (shouldClose) {
ensureClosed(response.body());
}
}
}
long elapsedTime(long start) {
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
}
Request targetRequest(RequestTemplate template) {
for (RequestInterceptor interceptor : requestInterceptors) {
interceptor.apply(template);
}
return target.apply(new RequestTemplate(template));
}
Object decode(Response response) throws Throwable {
try {
return decoder.decode(response, metadata.returnType());
} catch (FeignException e) {
throw e;
} catch (RuntimeException e) {
throw new DecodeException(e.getMessage(), e);
}
}
再看一下带熔断功能的HystrixTargeter:
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
//Feign实例不是HystrixFeign.Builder的则忽略,调用原生的构建方法
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//以下方法则使用HystrixFeign Builder (自定义了构建实际代理对象HystrixInvocationHandler)构建一个执行处理器
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);
/* We take a sample fallback from the fallback factory to check if it returns a fallback
that is compatible with the annotated feign interface. */
Object exampleFallback = fallbackFactory.create(new RuntimeException());
Assert.notNull(exampleFallback,
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!",
feignClientName));
if (!target.type().isAssignableFrom(exampleFallback.getClass())) {
throw new IllegalStateException(
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'",
feignClientName, exampleFallback.getClass(), target.type()));
}
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);
}
最终HystrixTargeter创建出HystrixInvocationHandler,其源码为:
final class HystrixInvocationHandler implements InvocationHandler {
private final Target<?> target;
private final Map<Method, MethodHandler> dispatch;
private final FallbackFactory<?> fallbackFactory; // Nullable
private final Map<Method, Method> fallbackMethodMap;
private final Map<Method, Setter> setterMethodMap;
HystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
SetterFactory setterFactory, FallbackFactory<?> fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
this.setterMethodMap = toSetters(setterFactory, target, dispatch.keySet());
}
/**
* If the method param of InvocationHandler.invoke is not accessible, i.e in a package-private
* interface, the fallback call in hystrix command will fail cause of access restrictions. But
* methods in dispatch are copied methods. So setting access to dispatch method doesn't take
* effect to the method in InvocationHandler.invoke. Use map to store a copy of method to invoke
* the fallback to bypass this and reducing the count of reflection calls.
*
* @return cached methods map for fallback invoking
*/
static Map<Method, Method> toFallbackMethod(Map<Method, MethodHandler> dispatch) {
Map<Method, Method> result = new LinkedHashMap<Method, Method>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
/**
* Process all methods in the target so that appropriate setters are created.
*/
static Map<Method, Setter> toSetters(SetterFactory setterFactory, Target<?> target,
Set<Method> methods) {
Map<Method, Setter> result = new LinkedHashMap<Method, Setter>();
for (Method method : methods) {
method.setAccessible(true);
result.put(method, setterFactory.create(target, method));
}
return result;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
throws Throwable {
// early exit if the invoked method is from java.lang.Object
// code is the same as ReflectiveFeign.FeignInvocationHandler
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
//核心方法位置,在此,则实现把客户端请求封装为Hystrix命令的方式进行调用,实现了调用的熔断与降级
HystrixCommand<Object> hystrixCommand = new HystrixCommand<Object>(setterMethodMap.get(method)) {
@Override
protected Object run() throws Exception {
try {
//实际调用的请求方法已在上一个默认Handler的第二部分指出
return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
throw (Error) t;
}
}
@Override
protected Object getFallback() {
if (fallbackFactory == null) {
return super.getFallback();
}
try {
Object fallback = fallbackFactory.create(getExecutionException());
Object result = fallbackMethodMap.get(method).invoke(fallback, args);
if (isReturnsHystrixCommand(method)) {
return ((HystrixCommand) result).execute();
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return ((Observable) result).toBlocking().first();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return ((Single) result).toObservable().toBlocking().first();
} else if (isReturnsCompletable(method)) {
((Completable) result).await();
return null;
} else {
return result;
}
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
// Exceptions on fallback are tossed by Hystrix
throw new AssertionError(e.getCause());
}
}
};
if (isReturnsHystrixCommand(method)) {
return hystrixCommand;
} else if (isReturnsObservable(method)) {
// Create a cold Observable
return hystrixCommand.toObservable();
} else if (isReturnsSingle(method)) {
// Create a cold Observable as a Single
return hystrixCommand.toObservable().toSingle();
} else if (isReturnsCompletable(method)) {
return hystrixCommand.toObservable().toCompletable();
}
return hystrixCommand.execute();
}
至此,一个Feign代理Bean的创建过程和使用该bean发起远程调用逻辑已经讲述完毕。