我们在微服务架构上开发的时候,通讯框架基本上都用过dubbo/feign之类的。对于我们java生态来说grpc/thrift可能很少在用,如果我们使用的是SpringCloud alibaba 可以选用openfeign 和 dubbo,如果我们使用的是netflix可以选用openfeign作为RPC通讯框架。这里我只推荐使用SpringCloud alibaba,因为netflix已经撂挑子了,听说最近已经捡起来继续维护SpringCloud netflix了,但和SpringCloud alibaba相比已经落后很多的版本了。
RPC基础
谈到RPC 这里必须提一嘴RPC的概念和实现的原理:
概念
什么是RPC?
百度解释:RPC(Remote Procedure Call)— 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。简而言之,RPC就是向调用本地方法一样去调用远程服务的方法。
什么情况下使用 RPC ?
例如开发电商系统,需要拆分出用户服务、商品服务、优惠券服务、支付服务、订单服务、物流服务、售后服务等等,这些服务之间都相互调用,这时内部调用最好使用 RPC ,同时每个服务都可以独立部署,独立上线。 也就说当我们的项目太大,需要解耦服务,扩展性强、部署灵活,这时就要用到 RPC ,主要解决了分布式系统中,服务与服务之间的调用问题。
优点:加快开发人员微服务之间对接开发的速度,强化微服务调用规范。
缺点:隐藏了内部调用细节,开发人员不容易理解RPC的原理和配置,可能会导致RPC调用的性能不高。比如openfegin 可以使用OkHttpClient/Apache HttpClient/HttpUrlConnection,如果没有引入OkHttp或其他的高性能组件,默认会使用HttpUrlConnection 这样HTTP连接不会被池化,每次请求都会去创建连接。这个分析源码的时候会进行解释。
RPC 调用中的所有角色
- provider 服务提供方
- consumer 调用方
- 注册中心 保存所有服务的列表以及检测服务是否可用
RPC的调用过程
RPC 调用一般会像下图一样进行信息传递:
① 在RPC 传输过程中可能还会有加密认证
② 序列化是RPC协议很重要的步骤,序列化如果处理不好会影响到整体的调用性能。后面会介绍openfegin使用的序列化方式。
③ 在B 调用A的时候可能A有多个服务,这个时候B服务要去注册中心获取服务列表,使用负载均衡算法进行选择最合适的A服务进行发送请求。
④ A服务和B服务都会和注册中心建立心跳机制,检查服务的可用性,假如A1服务挂掉Eureka在推送给B服务的注册列表会剔除A1服务,B服务在调用A服务的时候就会负载调用不到A1。
⑤ dubbo 提供的负载方式非常多,但Openfegin如果默认使用Loaderbalaner的话只有轮询和随机两种负载方式可用。
负载均衡算法: 轮询、iphash、随机、权重、自定义。
RPC 如何做到像调用本地方法一样调用?
其实能想本地方法一样调用远程服务方法就是RPC框架给你做了一层动态代理,你在Spring中注入Provider Bean其实是Spring容器在初始化的时候将所有的Provider 提供的api包的interface解析成了代理对象,你实际使用的是代理对象。当你调用代理对象的方法时,会被对应的handler进行代理处理,比如封装http header、contract、body、执行http请求/TCP请求。请求完成之后将结果数据反序列化解析出来给到你,对你来说就像调用本地方法一样,实际上框架底层为你做了很多的操作。下面源码解析部分会讲openfeign具体做了哪些事情。
openfegin简单使用
Maven 引用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
配置
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker // 启用hystrix
@EnableFeignClients(basePackages = "com.allens.biz.fegin") // 扫描feign clients
public class BizServer {
@LoadBalanced // restTemplate使用LoadBalanced做负载,使用ribbon则不需要配置此注解
@Bean
RestTemplate restTemplate () {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(BizServer.class, args);
}
}
feign client
@FeignClient("TEST-SERVICE")
public interface TestServiceFegin {
@GetMapping("/hello/test")
String testHello ();
}
业务代码
@Autowired
TestServiceFegin testServiceFegin;
@GetMapping("/feginconsumer")
public void feginConsumer () {
System.out.println(testServiceFegin.testHello());
}
可以看到feign的使用非常的简单,想调用本地代码一样调用远程服务。下面我们会重点讲openfeign的源码实现。
源码解析
openfeign 的autoconfig
我们看下使用的教程配置发现我们在使用openfeign的时候要加一个注解@EnableFeignClients
并指定了一个包的路径。在开发生产过程中如果细心就会发现很多组件都会使用注解去启用整个组件,配置很简单,在有@Configuration
的配置类上加上注解@EnableXXX
即可。Springboot在扫描带有@Configuration的类时会去解析@EnableXXX注解,如果注解中标注有@Import(xxx.class),就会去将xxx.class注入到Springboot容器中。如果xxx.class 实现了ImportBeanDefinitionRegistrar,Springboot容器会将AnnotationMetadata
和BeanDefinitionRegistry
传递给xxx.class的重写方法registerBeanDefinitions
。我们就可以借助registerBeanDefinitions
传过来的registry来修改beandefinition以及添加注册我们需要的beandefinition。
class:ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
// 如果实现者没有重写则默认会走这个默认接口方法处理
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
// 提供给实现者重写的接口方法
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
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 {};
/**
* 这个配置后回扫描包下的 feign client (标有@FeignClient注解的interface)
* Base packages to scan for annotated components.
* <p>
* {@link #value()} is an alias for (and mutually exclusive with) this attribute.
* <p>
* 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.
* <p>
* 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 <code>@Configuration</code> for all feign clients. Can contain override
* <code>@Bean</code> 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 {};
/**
* 指定具体的FeignClient 进行解析注入到容器生成动态代理实现类
* List of classes annotated with @FeignClient. If not empty, disables classpath
* scanning.
* @return list of FeignClient classes
*/
Class<?>[] clients() default {};
}
@Import(FeignClientsRegistrar.class)
我们看到EnableFeignClients
有这个配置注解,这个会自动选择配置feign,我们们点击进去看下这个类。
FeignClientsRegistrar
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
可以看到这个类做了两件事情
① 注册feign的默认配置类
② 注册所有的feign client,feign client 就是被注解@FeignClients
修饰的接口类
注册配置
method:
registerDefaultConfiguration
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取我们EnableFeignClients中配置的属性
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
// 给我们的EnableFeignClients所在的配置类加上default.前缀
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
defaultAttrs.get("defaultConfiguration")
获取EnableFeignClients中配置的defaultConfiguration
,我们看下官方注释:
A custom @Configuration for all feign clients. Can contain override @Bean definition for the pieces that make up the client, for instance feign.codec.Decoder, feign.codec.Encoder, feign.Contract.
为所有的feign clients提供自定义的config,可以包含组成客户端的组件的覆盖@Bean定义,例如feign.codec。解码器,feign.codec。编码器,feign.Contract。
method:
registerClientConfiguration
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
// 我们都知道Spring在注册Bean之前会生成BeanDefinition,
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
// 注册我们带有EnableFeignClients的配置类
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
Spring在注册Bean之前会生成BeanDefinition,大致bean的生命周期是 ResourceLoader 或者 AnnotationClassLoader 会去从xml或者java config中获取所有bean的定义,然后将所有的bean的定义转换为bean definition,然后将所有的bean definition 解析成bean注入到容器中。
注册feign client (重点)
我们来看下重点的部分,往容器中注册所有的feign clients
method:registerFeignClients
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 存放feign client的bean definition
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 获取EnableFeignClients注解配置的属性
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
// 获取EnableFeignClients配置的clients
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
// 如果feign clients classes不为空则扫描找到这个feign client
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
// 如果feign clients classes为空则直接创建一个AnnotatedGenericBeanDefinition
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// 遍历所有的feign client definition
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
// 验证feign client 是否为interface,如果@FeignClient注解卸载impl class上就会报错,feign client 只能是接口
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
// 获取FeignClient注解属性配置
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
// 获取Feign Client name 也就是服务id,The service id with optional protocol prefix. Synonym for
String name = getClientName(attributes);
// 注册feign client的config 和上面一样
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 注册feign client (*)
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
method:
registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
// 获取FeignClient注解修饰的类名
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
// 获取bean工厂(容器)
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
// 创建feign client 创建的工厂bean
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
// 生成beandefinition 为后续注入Spring容器做准备
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(clazz, () -> {
// ---------------------这些属性都来自FeignClient注解配置---------------------
// 设置url
factoryBean.setUrl(getUrl(beanFactory, attributes));
// 设置路径
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean
.parseBoolean(String.valueOf(attributes.get("decode404"))));
// 是否配置的有fallback 回调
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class
? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class
? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(),
null));
}
// 获取最终的BeanDefinition
return factoryBean.getObject();
});
// 设置可注入类型
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
// 是否懒加载bean
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
// 设置工厂bean到beanDefinition
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
// 设置成主要的bean,Spring中如果类型有多个bean则回去@Primary修饰的做默认的注入
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
// 创建bean holder ,Spring 的容器注入不在本文的讨论范围,有兴趣可查资料,或看我后续教程
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
qualifiers);
// 注册BeanDefinition到Spring容器中,准备生成Bean
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
上述代码的重点是:factoryBean.getObject()
工厂Bean会返回一个BeanDefinition的构造对象。我们看下工厂bean做了哪些操作:
FeignClientFactoryBean
method:getObject
@Override
public Object getObject() {
return getTarget();
}
method:getTarget
<T> T getTarget() {
// 获取fegin的上下文
FeignContext context = beanFactory != null
? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
// 创建者模式,BeanDefinition创建是全权交给这个构造器来处理的后续会频繁出现
Feign.Builder builder = feign(context);
// 上面的registerFeignClient已经给次FactoryBean赋值过url等属性,可以参照上述代码
if (!StringUtils.hasText(url)) { // 如果URL为空
if (url != null && LOG.isWarnEnabled()) {
LOG.warn(
"The provided URL is empty. Will try picking an instance via load-balancing.");
}
else if (LOG.isDebugEnabled()) {
LOG.debug("URL not provided. Will use LoadBalancer.");
}
// 如果服务名称不以HTTP开头
if (!name.startsWith("http")) {
url = "http://" + name; // 例如:http://TEST-SERVICE/
}
else {
// URL直接取服务id
url = name;
}
// 拼接路由
url += cleanPath();
// 获取beandefinition
return (T) loadBalance(builder, context,
new HardCodedTarget<>(type, name, url));
}
// 下面这部分不难,其实和上面的处理方式差不多,不做过多介绍了
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((RetryableFeignBlockingLoadBalancerClient) client)
.getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
HardCodedTarget
硬编码,会根据这个对象生成代理对象的硬编码,hashcode等等。
method:loadBalance
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 获取http client
Client client = getOptional(context, Client.class);
if (client != null) {
// 设置客户端
builder.client(client);
// 获取到Target
Targeter targeter = get(context, Targeter.class);
// targeter配置完成
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 or spring-cloud-starter-loadbalancer?");
}
从容器中获取到注册的HTTP客户端,组件默认注册的是LoadBalancerFeignClient 所以这里会读到这个bean。
可以参考这个类DefaultFeignLoadBalancedConfiguration
,默认注册了LoadBalancerFeignClient。细心的同学会发现LoadBalancerFeignClient
使用了Client.Default
做默认的客户端,这个我们后续再讲。
最终会调用到这个Feign.target(Target target)创建
BeanDefinition
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
Feign
method:build 开始创建Feign client beandefinition
public Feign build() {
// Capability.enrich 内部就是一个reduce,如果capabilities不为空就会创建一个装饰者模式去调用capability中的enrich方法来装饰对象,有兴趣可以自己看下源码,这里capabilities是空的,所以直接会返回原始对象。
Client client = Capability.enrich(this.client, capabilities);
// 重试器
Retryer retryer = Capability.enrich(this.retryer, capabilities);
// 请求拦截器,feign 在请求的时候会经过这些拦截器,我们如果想拦截feign的话可以实现RequestInterceptor注入导入期即可,这里会获取到拦截器进行拦截处理
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
// 日志工具
Logger logger = Capability.enrich(this.logger, capabilities);
// http相关
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
// * 序列化器
Encoder encoder = Capability.enrich(this.encoder, capabilities);
// * 反序列化器
Decoder decoder = Capability.enrich(this.decoder, capabilities);
// InvocationHandlerFactory 这个很重要
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
// 这个很重要,后续会使用这个累进行创建真正做事的拦截器
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
// 相当于是一个map config会在后续频繁调用这个对象
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
// 生成了ReflectiveFeign
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
ReflectiveFeign
method:
build().newInstance(target)
我们看下这段代码做了哪些操作
public <T> T newInstance(Target<T> target) {
// 其实就是获取所有代理method的handler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
// 处理method的代理handler字典
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
// 不太明白这个干嘛的,断点都是null,有兴趣自己看下好了
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) { // 如果是纯对象则直接不做任何处理
continue;
} else if (Util.isDefault(method)) { // 如果是默认的方法生成method handler
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 创建动态代理的method handler, InvocationHandlerFactory.create
InvocationHandler handler = factory.create(target, methodToHandler);
// 这里不用我多说,这里生成了动态代理对象实现了我们FeignClient注解修饰的interface
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
// 返回代理对象
return proxy;
}
只要我们调用proxy对象就会触发调用FeignInvocationHandler.invoke
然后调用SynchronousMethodHandler.invoke
来做具体的HTTP请求。
method:InvocationHandlerFactory.create 最终会创建一个FeignInvocationHandler
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");
// 会将methodToHandler注入到dispatch属性中
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();
}
// 会从dispatch中取出对应method的handler进行调用,这里的handler就是下面的SynchronousMethodHandler.Factory(主要是这个)创建的handler或默认方法创建的handler
return dispatch.get(method).invoke(args); // 这里调用后回调用SynchronousMethodHandler
}
@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();
}
}
method:
ParseHandlersByName.apply
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
// 获取FeignClient注解修饰的interface 所有方法的元信息
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate =
new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
if (md.isIgnored()) {
result.put(md.configKey(), args -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
// 创建代理的handler
result.put(md.configKey(),
// SynchronousMethodHandler.Factory 这里会创建一个handler
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
return result;
}
method:SynchronousMethodHandler:create`
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
// 创建SynchronousMethodHandler
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
}
SynchronousMethodHandler
这个handler是继承MethodHandler,动态代理的话会默认调用这个累的invoke方法:
method:SynchronousMethodHandler.invoke
@Override
public Object invoke(Object[] argv) throws Throwable {
// 创建请求模板
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
// 重试器
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 执行请求并解码
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
// 过异常了就会重试
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
// 记录日志
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
method:
SynchronousMethodHandler.executeAndDecode
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 执行请求,我们的客户端是loadbalanerFeignClient
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.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);
// 解码
if (decoder != null)
return decoder.decode(response, metadata.returnType());
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(),
elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
loadbalanerFeignClient 会调用delegate属性中的httpclient 可以是okhttp、httpclient、httpurlconnection,我们这里没有引入任何的其他组件,默认是httpurlconnection。
openfeign 使用的httpclient、Okhttp
我们只需要引用okhttp和httpclient的包,在FeignAutoConfiguration
中就会帮我们autoconfig
OkHttpFeignLoadBalancedConfiguration
中会autoconfig帮我配置好LoadBalancerFeignClient的代理http客户端,当然我这里引用了ribbon的包,会使用ribbon的负载均衡器。
总结
① 一定要使用apache httpclient/okhttp对http连接池化管理提升RPC性能
② 负载均衡可以使用ribbon、loadbalancer其中一个即可,ribbon和loadbalancer各有缺点,ribbon netflix撂挑子了,loadbalancer提供的方案少,自行斟酌使用。
③ 动态代理是Spring的灵魂,很多组件都少不了它,openfeign也不例外。
④ 序列化我先不讲了,留在下一讲
⑤ 一定要结合代码一起看,不然很容易乱
⑥ 如果觉得有用,请点赞收藏