目录
一、feign简介应用
1、feign的概念
2、feign的使用
二、feign原理分析
1、前言
2、@EnableFeignClients注解
2.1、@EnableFeignClients
2.2、FeignClientsRegistrar类
2.3、加载feign的配置信息
2.4、注册FeignClient
2.5、registerFeignClient
3、@FeignClient注解
3.1 FeignClient注解使用
3.2 @FeignClient的属性
3.3 @FeignClient源码分析
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。与当我们访问别的服务端口的时候 大部分使用httpclient等请求进行调用不同,在eureka注册的服务,我们可以使用Feign 声明接口的形式来进行相关服务的调用,并提供了失败回退(其实是Hystrix组件的使用)。Feign只是一个便利的rest框架,简化调用,最后还是通过ribbon在注册服务器中找到服务实例,然后对请求进行分配。
org.springframework.cloud
spring-cloud-starter-feign
@SpringBootApplication
//扫描项目下所有使用@FeignClient注解修饰的接口
@EnableFeignClients
@EnableEurekaClient
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
//使用feignclien注解和SpringMVC相关的常用注解 声明一个feign接口 调用微服务信息
@FeignClient(value = "service-ribbon",url = "https://api.github.com",decode404 = false,
fallback = MyFallback.class, fallbackFactory = MyFallBackFactory.class )
public interface SayHiFeign {
@RequestMapping("/hi", method = RequestMethod.GET)
String sayHi(@RequestParam(name="name",required = true) String name);
}
如上便完成了使用feign完成别的为服务调用,是不是很简单。点击下载demo源码 feign组件使用demo
通过如上的简单介绍我们大致了解了feign的概念以及如何在项目中进行使用,通过上面的观察以及使用,我们发现feign组件在springclound项目中使用是如此只简单:只需在项目的主启动类使用@EnableFeignClients注解,在服务接口类中使用@FeignClient修饰。下面我们就从这两个注解入手揭开feign的神秘面纱(貌似话说的有点大)。
为了方便理解,我们在这里先不给出结论,直接从源码中一步一步分析代码,最后再给出结论。
//运行时保留
@Retention(RetentionPolicy.RUNTIME)
//该注解用于修饰描述类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
//可以生产api doc
@Documented
//这个比较重要使用注解修饰的项目 需要先初始化FeignClientsRegistrar该类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
//basePackages的别名
String[] value() default {};
//指定需要扫描的包名列表
String[] basePackages() default {};
//指定需要扫描的class列表信息
Class>[] basePackageClasses() default {};
//feign调用的配置信息类 该对象非常重要,
//包含FeignClient需要的重试策略,超时策略,日志等配置,
//如果某个服务没有设置, 则读取默认的配置。
Class>[] defaultConfiguration() default {};
//直接指定FeignClient注解修饰的class
Class>[] clients() default {};
该注解 用来开启 Feign,所以其属性主要是指定要扫描的包名类名 FeignClient列表(value属性,basePackages属性,basePackageClasses属性,clients属性),以及相关的配置信息(defaultConfiguration),这些并非我们关注的重点 在该注解类上有个使用@Import注解引入FeignClientsRegistrar.class 的实体类 其实该类是实现feign调用的部分功能(前期准备)的实现,下面让我们来分析一下
FeignClientsRegistrar类
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
通过名字可以看出该类是一个注册器,该类实现了 ImportBeanDefinitionRegistrar 通过扫描某个特性的类,将bean注册到IOC中。他的生命周期方法是registerBeanDefinitions ,Spring 通过调用其 registerBeanDefinitions 方法来获取其提供的 bean definition。
//生命周期的方法
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注册feign 相关的特性 配置信息
registerDefaultConfiguration(metadata, registry);
//注册FeignClient 对象
registerFeignClients(metadata, registry);
}
上面开启feign主要分两大部分:
//注册配置信息
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//获取EnableFeignClients注解的元数据信息
Map defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//判断是否存在defaultConfiguration 属性信息
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
//注册defaultConfiguration 对应的属性信息类 ,如果为空则使用默认的配置
//FeignAutoConfiguration
//最终将配置类解析成一个BeanDefinition类注入到spring中
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
如上主要是从启动类的@EnableFeignClients 中获取defaultConfiguration属性中用户自定义的配置信息,如果没有则使用默认的配置信息类FeignAutoConfiguration,最终将其包装成BeanDefinition类注册到spring容器中(代码比较繁琐,个人只贴到这里)。配置中心主要提供了feign 的功能特性,该配置信息是适用于@FeignClient注解的全局配置,也可以在@FeignClient的configuration属性做单独的配置。该配置主要包含熔断器、失败重试、超时策略、日志配置等。
//注册FeigenClient
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//获取类扫描组件
ClassPathScanningCandidateComponentProvider scanner = getScanner();
//添加资源加载服务类 提供加载指定目录下的资源信息 便于后面的类扫描
scanner.setResourceLoader(this.resourceLoader);
Set basePackages;
//获取EnableFeignClients注解的属性信息 其中可能包含需要扫描的包名或者class信息
Map attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//创建扫描过滤规则 只扫描包含FeignClient注解修饰的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
//获取注解中包含client 属性 如果有则不进行包名扫描 直接遍历存在的client 进行注册
final Class>[] clients = attrs == null ? null
: (Class>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
//为扫描类设置上面创建的只扫描FeignClient注解的扫描规则
scanner.addIncludeFilter(annotationTypeFilter);
//获取扫描的包名
basePackages = getBasePackages(metadata);
}
else {
//有client 将client所在的包加入的扫描包列表中 添加新的一个过滤规则
final Set 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 FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
//遍历包扫描列表
for (String basePackage : basePackages) {
//通过报名获取对应的查找出来的FeignClient注解修饰的接口的ScanGeneric的 BeanDefinition描述
Set 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");
//从所有的元数据中获取到FeignClient注解相关的属性信息 url name fallBack fallbackFactory decode404等
Map attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
//attributes中局部配置信息注册和如上说的全局配置注册流程一致
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//根据元数据 将使用FeignClient修饰的类解析成BeanDefinition
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
registerFeignClients方法主要是通过扫描指定包下的所有使用@FeignClient修饰的接口类列表(如果@EnableFeignClients中配置了clients属性,则扫描出来的bean只有在clients中配置的那些),遍历这些类进行FeignClient的注册以及局部配置信息的注册。下面继续关注registerFeignClient()方法如何实现注册FeignClient
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map attributes) {
String className = annotationMetadata.getClassName();
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);
}
这个方面比较简单 只是获取FeignClient注解的元数据将其设置到BeanDefinitionBuilder 用于构造BeanDefinition类并注册到spring中。我们需要注册的是程序获取BeanDefinition的类是FeignClientFactoryBean(该类看名字可以得出是FeignClient工程类 i因为修饰的类是接口类 所有是创建FeignClient代理 这点我们在分析@FeignClient注解的时候讲解)
补充:BeanDefinition Spring容器启动的过程中,会将Bean解析成Spring内部的BeanDefinition结构
其不仅包含bean的香港信息 还包含其他关于描述该bean特性的信息 例如scode 懒加载等spring赋予的特性信息
里面类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等,其实就是将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建。’
总结:
到此开启Feign,就完成了我们总结一下
Feign的注册一共分为一下几步:
//使用feignclien注解和SpringMVC相关的常用注解 声明一个feign接口 调用微服务信息
@FeignClient(value = "service-ribbon",url = "https://api.github.com",decode404 = false,
fallback = MyFallback.class, fallbackFactory = MyFallBackFactory.class )
public interface SayHiFeign {
@RequestMapping("/hi", method = RequestMethod.GET)
String sayHi(@RequestParam(name="name",required = true) String name);
}
属性 |
相关描述 |
name/value | 服务名称(在注册中心注册的服务名称)(两者其中之一必填) |
url |
硬编码存在的url地址 如果该地址存在 则不通过服务名称查找服务 |
decode404 | 对于404的错误 是直接抛出异常(图1),还是将异常信息解码响应(图2) |
config | FeignClientsConfiguration feign客户端 提供encode编码器 decode解码器 contract连接器 |
fallback | 服务调用失败的时候会回调该类下同名的方法,用于失败的服务降级等操作 |
fallbackFactory | 失败回调工厂类生产fallback相关的工厂类作用与fallback一样 |
fallback 失败回调类
/**
*失败回调类 实现需要失败回调的接口 从而保证其有与实现类一样的方法
*/
@Component
public class MyFallback implements GitHubApiFeign {
Logger logger = LoggerFactory.getLogger(MyFallback.class);
@Override
public String searchRepositories(String queryStr) {
logger.info("失败回调操作");
return null;
}
@Override
public String searchRepositoriea(String queryStr) {
logger.info("失败回调操作");
return null;
}
}
/**
* 失败回调的工厂类 没有特殊之处 只是针对上述的失败类使用工厂模式创建而已
*/
@Component
public class MyFallBackFactory implements FallbackFactory {
private final MyFallback myFallback;
public MyFallBackFactory(MyFallback myFallback) {
this.myFallback = myFallback;
}
@Override
public GitHubApiFeign create(Throwable cause) {
//打印下异常
System.out.println("失败回调的工厂");
cause.printStackTrace();
return myFallback;
}
}
在 2.5 registerFeignClient中我们发现是将Feign的包装成BeanDefinition类注册到spring容器中,同时我们也可以明确的知道该包装成BeanDefinition的类为FeignClientFactoryBean类。在上面的篇幅中我们分析了Feign如何在Spring容器中进行注册的,下面我们就从FeignClientFactoryBean来分析一下Feign的调用过程(中间可能会简单涉及Ribbon和Hystrix的介绍)。
FeignClientFactoryBean实现了FactoryBean所以该类在spring中使用的时候会调用其getObject()方法返回指定的bean对象
@Override
public Object getObject() throws Exception {
//从应用中获取Feign上下文环境信息 FeignContext中默认使用FeignClientsConfiguration中的配置信息
//不如feign的Decoder解码 Encoder编码 Retryer重试次数 logger日志等 以及各种Feign HystrixFeign(带有熔断作用的Feign)等
FeignContext context = applicationContext.getBean(FeignContext.class);
//根据FeignContext 上下文环境以构建形式创建Feign 具体单独分析
Feign.Builder builder = feign(context);
//判断@FeignClient中的url属性是否有值 没有值则走Eureka注册中心
if (!StringUtils.hasText(this.url)) {
//如下是获取对应的请求url 如果与Eureka结合使用则是注册中心的服务名称类型的url
//这个做url的规范化处理
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
//使用负载均衡以及Hystrix 对请求进行相关的调用
return loadBalance(builder, context, new Target.HardCodedTarget<>(this.type,
this.name, url));
}
//有url属性值 则直接请求该url
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
//从Feign的上下文环境中获取Feign的client(主要用作请求的执行)
Client client = getOptional(context, Client.class);
//判断如果client的请求是存在且为LoadBalancerFeignClient(具备负载请求的客户端) 则将客户端转换
//并设置入Fiegn对象中
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);
}
//获取对应的Targeter类 该类的实现有两种一种是DefaultTargeter类(不做任何处理 仅仅是Feign target调用)
//另一种是HystrixTargeter类 使用给feign添加了Hystrix的熔断 失败回调的功能
Targeter targeter = get(context, Targeter.class);
//这个是我们的核心方法 用来创建Feign的具体代理对象
return targeter.target(this, builder, context, new Target.HardCodedTarget<>(
this.type, this.name, url));
}
将Feign环境中的配置信息(编解码对象,重试 ,client客户端,Targeter)获取到以构建的形式构造Feign对象
protected Feign.Builder feign(FeignContext context) {
//这个的get() getOptional()方法都是从Feign的上下文中根据获取对应的对象信息
//获取日志
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
//设置 日志 请求的编码对象 解码对象 Contract请求的协议信息
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
//可选操作
//设置日志等级
Logger.Level level = getOptional(context, Logger.Level.class);
if (level != null) {
builder.logLevel(level);
}
//重试
Retryer retryer = getOptional(context, Retryer.class);
if (retryer != null) {
builder.retryer(retryer);
}
//请求返回错误信息的解码对象
ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
if (errorDecoder != null) {
builder.errorDecoder(errorDecoder);
}
//连接超时,读取超市的配置信息
Request.Options options = getOptional(context, Request.Options.class);
if (options != null) {
builder.options(options);
}
//请求拦截器
Map requestInterceptors = context.getInstances(
this.name, RequestInterceptor.class);
if (requestInterceptors != null) {
builder.requestInterceptors(requestInterceptors.values());
}
//404请求不到情况下错误信息的编码处理
if (decode404) {
builder.decode404();
}
//构建成功返回
return builder;
}
使用@FeignClient注解修饰的Feign接口被包装成Target(目标对象),被Targeter(瞄准者)所调用
protected T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget target) {
// 1.获取Client的实现类,默认为LoadBalancerFeignClient类
// 实现在FeignRibbonClientAutoConfiguration中
Client client = getOptional(context, Client.class);
if (client != null) {
// 2.将LoadBalancerFeignClient包装到Feign.Builder
builder.client(client);
// 3.获取ApplicationContext中的Targeter实现
// 默认实现为HystrixTargeter,实现在FeignAutoConfiguration类中
Targeter targeter = get(context, Targeter.class);
// 4.重点在这里
// 我们来看下这个方法
return targeter.target(this, builder, context, target);
}
...
}
请求的处理 有url不走负载请求,没有url 则配合Eureka使用进行请求的负载均衡,但是无论@FeignClient的url是否有值 最终处理逻辑均相似 获取Targeter并调用其中的target()方法 唯一不同是配合Eureka targeter为HystrixTarger ,直接请求url的是使用DefaultTargerter对象。
public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget target) {
//判断如果该feign 不是HystrixFeign 则直接调用
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//是HystrixFeign 将其转换出来
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);
}
在上面的分析中我们看到了是@FiegnClient 注解的属性fallback或fallbackFactory对应的调用targetWithFallback或targetWithFallbackFactory,其实两者的逻辑处理相似 前者获取其中的回调实例,后者获取其中的回调工厂实例,但是最终还都是调用 1、builder.target(target, fallback); 2、builder.target(target, fallbackFactory);
如下Feign 和HystrixFeign提供众多的target方法
Hystrix 提供了四种target方法
public T target(Target target, T fallback) {
return this.build(fallback != null ? new feign.hystrix.FallbackFactory.Default(fallback) : null).newInstance(target);
}
public T target(Target target, FallbackFactory extends T> fallbackFactory) {
return this.build(fallbackFactory).newInstance(target);
}
public T target(Class apiType, String url, T fallback) {
return this.target(new HardCodedTarget(apiType, url), (Object)fallback);
}
public T target(Class apiType, String url, FallbackFactory extends T> fallbackFactory) {
return this.target(new HardCodedTarget(apiType, url), (FallbackFactory)fallbackFactory);
}
feign提供了五中target方法
public T target(Class apiType, String url) {
return target(new HardCodedTarget(apiType, url));
}
public T target(Target target) {
return build().newInstance(target);
}
如上这些方法都是调用其build().newInstance()方式,同时也是我们需要分析的下面我们来看一下build()方法
public Feign build() {
//获取SynchronousMethodHandler的工厂类 用来创建SynchronousMethodHandler对象
//而我们所有的Feign接口方法都被包装成该对象SynchronousMethodHandler
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder,
errorDecoder, synchronousMethodHandlerFactory);
//真正创建ReflectiveFeign 同时后面的newInstance会调用该对象的实现。
return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
}
如上创建ReflectiveFeign对象 我们需要看一下该类的newInstance()方法。
public T newInstance(Target target) {
//targetToHandlersByName 见名知义 从目标对象(使用@FeignClient修饰的接口类)中获取其中所有的接口方法 放到map中
//key 为方法的全限定名称,value 为MethodHandler的实现类 feign调用是SynchronousMethodHandler类
//在这里其实是将feign的所有方法包SynchronousMethodHandler 调用的时候会执行其invoke()方法
Map nameToHandler = targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
//遍历目标类的所有接口方法 并将其存放入methodToHandler方法处理器集合中
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)));
}
}
//调用SynchronousMethodHandlerFactory 的create方法 创建InvocationHandler 的实现类
//HystrixInvocationHandler(JDK动态代理)
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;
}
如上代码将为feign接口使用jdk动态代理创建了一个代理对象HystrixInvocationHandler。所以相关的对feign接口的调用触发HystrixInvocationHandler对象的invoke()方法,分析到这里我们对feign的调用逻辑大致清晰了,所有Feign接口类会使用创建Feign的代理对象,所有接口方法调用都 HystrixInvocationHandler的invoke()方法
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
//equals hashCode toString 单独设置
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();
}
//对于feign接口请求 结合Hystrix组件 使用新建线程来单独执行,防止服务之间调用时候某些服务不可用时候的服务雪崩
HystrixCommand
如上Feign接口调用是其实是根据方法名获取对应的MethodHandler类确定的说是SynchronousMethodHandler类的invoke()
同时该方法的调用是通过Hystrix组件来实现每个方法调用都创建线程来单独调用。
下面来看一下SynchronousMethodHandler的invoke()方法
public Object invoke(Object[] argv) throws Throwable {
//feign之间的调用是基于HTTP请求 所以在创建Resquest请求
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 {
//使用客户端执行请求 Feign默认集成了Ribbion 所以这里请求是带有负载均衡功能的
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 10
response.toBuilder().request(request).build();
//后面是将响应信息解码封装 然后返回
.....
}
如上invoke()调用其实是封装http请求 并发送请求,又因为这里feign和Ribbon默认集成,所以请求是带有负载功能的请求。到此feign组件分析结束。