SpringCloudRPC远程调用核心原理:feign.Client客户端容器实例

feign.Client客户端容器实例

前面介绍了常用的Feign客户端实现类,大致如下:

(1)Client.Default类:默认的实现类,使用JDK的HttpURLConnnection类提交HTTP请求。

(2)ApacheHttpClient类:该客户端类在内部使用ApacheHttpClient开源组件提交HTTP请求。

(3)OkHttpClient类:该客户端类在内部使用OkHttp3开源组件提交HTTP请求。

(4)LoadBalancerFeignClient类:内部使用Ribbon负载均衡技术完成HTTP请求处理。

Feign在启动时有两个与feign.Client客户端实例相关的自动配置类,根据多种条件组合装配不同类型的feign.Client客户端实例到Spring IOC容器,这两个自动配置类为
FeignRibbonClientAutoConfiguration和FeignAutoConfiguration。

SpringCloudRPC远程调用核心原理:feign.Client客户端容器实例_第1张图片

SpringCloudRPC远程调用核心原理:feign.Client客户端容器实例_第2张图片

装配LoadBalancerFeignClient负载均衡容器实例

详细来看,Feign涉及的与Client相关的两个自动配置类具体如下:

(1)
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration:此自动配置类能够配置具有负载均衡能力的FeignClient容器实例。

(2)
org.springframework.cloud.openfeign.FeignAutoConfiguration:此自动配置类只能配置原始的FeignClient客户端容器实例。

事实上,第一个自动配置类
FeignRibbonClientAutoConfiguration在容器的装配次序上优先于第二个自动配置类FeignAutoConfiguration。

为了达到高可用,Spring Cloud中一个微服务提供者至少应该部署两个以上节点,从这个角度来说,LoadBalancerFeignClient容器实例已经成为事实上的标配。

具体可以参见
FeignRibbonClientAutoConfiguration源码,节选如下:

import com.netflix.loadbalancer.ILoadBalancer;
...
@ConditionalOnClass({ILoadBalancer.class, Feign.class})
@Configuration
@AutoConfigureBefore({FeignAutoConfiguration.class}) //本配置类具备优先权
@EnableConfigurationProperties({FeignHttpClientProperties.class})
@Import({
HttpClientFeignLoadBalancedConfiguration.class,//配置:包装ApacheHttpClient实例的负载均衡客户端
OkHttpFeignLoadBalancedConfiguration.class, //配置:包装OkHttpClient实例的负载均衡客户端
DefaultFeignLoadBalancedConfiguration.class //配置:包装Client.Default实例的负载均衡客户端
})
public class FeignRibbonClientAutoConfiguration {
 //空的构造器
 public FeignRibbonClientAutoConfiguration() {
 }
...
}

从源码中可以看到,
FeignRibbonClientAutoConfiguration的自动配置有两个前提条件:

(1)当前的类路径中存在ILoadBalancer.class接口。

(2)当前的类路径中存在Feign.class接口。

在这里重点讲一下ILoadBalancer.class接口,它处于ribbon的JAR包中。如果需要在类路径中导入该JAR包,就需要在Maven的pom.xml文件中增加ribbon的相关依赖,具体如下:

 
 
 org.springframework.cloud
 spring-cloud-starter-netflix-ribbon
 

为了加深大家对客户端负载均衡的理解,这里将ILoadBalancer.class接口的两个重要的抽象方法列出来,具体如下:

package com.netflix.loadbalancer;
import java.util.List;
public interface ILoadBalancer {
 //通过负载均衡算法计算server服务器
Server chooseServer(Object var1);
//取得全部的服务器
List getAllServers();
...
}


FeignRibbonClientAutoConfiguration自动配置类并没有直接配置LoadBalancerFeignClient容器实例,而是使用@Import注解。通过导入其他配置类的方式完成LoadBalancerFeignClient客户端容器实例的配置。

分别导入了以下3个自动配置类:

(1)
HttpClientFeignLoadBalancedConfiguration.class:该配置类

负责配置一个包装ApacheHttpClient实例的LoadBalancerFeignClient负载均衡客户端容器实例。

(2)
OkHttpFeignLoadBalancedConfiguration.class:该配置类负责配置一个包装OkHttpClient实例的LoadBalancerFeignClient负载均衡客户端容器实例。

(3)
DefaultFeignLoadBalancedConfiguration.class:该配置类负责配置一个包装Client.Default实例的LoadBalancerFeignClient负载均衡客户端容器实例。

SpringCloudRPC远程调用核心原理:feign.Client客户端容器实例_第3张图片

装配ApacheHttpClient负载均衡容器实例

首先来看如何配置一个包装ApacheHttpClient实例的负载均衡客户端容器实例。这个IOC实例的配置由
HttpClientFeignLoadBalancedConfiguration自动配置类完成,其源码节选如下:

@Configuration
@ConditionalOnClass({ApacheHttpClient.class})
@ConditionalOnProperty(
 value = {"feign.httpclient.enabled"},
 matchIfMissing = true
)
class HttpClientFeignLoadBalancedConfiguration {
 //空的构造器
 HttpClientFeignLoadBalancedConfiguration() {
 }
 @Bean
 @ConditionalOnMissingBean({Client.class})
public Client feignClient(
CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient)
{
 ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
 return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); //进行包装
 }
...

首先来看源码中的feignClient()方法,分为两步:

(1)创建一个ApacheHttpClient类型的feign.Client客户端实例,该实例的内部使用Apache httpclient开源组件完成HTTP请求处理。

(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将ApacheHttpClient实例包装起来,然后返回该包装实例,作为feign.Client类型的Spring IOC容器实例。

接下来介绍
HttpClientFeignLoadBalancedConfiguration类上的两个重要注解:@ConditionalOnClass(ApacheHttpClient.class)和@ConditionalOnProperty(value="feign.httpclient.enabled",matchIfMissing=true)。

这两个条件的含义为:

(1)必须满足ApacheHttpClient.class在当前的类路径中存在。

(2)必须满足工程配置文件中feign.httpclient.enabled配置项的值为true。

如果以上两个条件同时满足,
HttpClientFeignLoadBalancedConfiguration自动配置工作就会启动。

具体如何验证呢?首先在应用配置文件中将配置项feign.httpclient.enabled的值设置为false,然后在
HttpClientFeignLoadBalancedConfiguration的feignClient()方法内的某行设置断点,重新启动项目,注意观察,会发现整个启动过程中断点没有被命中。

接下来,将配置项feign.httpclient.enabled的值设置为true,再一次启动项目,发现断点被命中。由此可见,验证
HttpClientFeignLoadBalancedConfiguration自动配置类被启动。

为了满足@ConditionalOnClass(ApacheHttpClient.class)的条件要求,需要为pom文件加上feign-httpclient和httpclient组件相关的Maven依赖,具体如下:

 
 io.github.openfeign
 feign-httpclient
 9.5.1
 
 

 org.apache.httpcomponents
 httpclient
 ${httpclient.version}
 

对于feign.httpclient.enabled配置项来说,@ConditionalOnProperty的matchIfMissing属性值默认为true,也就是说,这个属性在默认情况下就为true。

装配OkHttpClient负载均衡容器实例

接下来看如何配置一个包装OkHttpClient实例的负载均衡客户端容器实例。这个IOC实例的配置,由
OkHttpFeignLoadBalancedConfiguration自动配置类负责完成,其源码节选如下:

@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnProperty("feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
 //空的构造器
 OkHttpFeignLoadBalancedConfiguration () {
 }
 @Bean
 @ConditionalOnMissingBean({Client.class})
public Client feignClient(
CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient)
{
 OkHttpClient delegate = new OkHttpClient (httpClient);
 return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); //进行包装
 }
...
}

首先来看源码中的feignClient()方法,分为两步:

(1)创建一个OkHttpClient类型的客户端实例,该实例的内部使用OkHttp3开源组件来完成HTTP请求处理。

(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将OkHttpClient实例包装起来,然后返回LoadBalancerFeignClient客户端实例作为feign.Client客户端IOC容器实例。

接下来介绍
OkHttpFeignLoadBalancedConfiguration类上的两个重要的注解:@ConditionalOnClass(OkHttpClient.class)和@ConditionalOnProperty("feign.okhttp.enabled")。

这两个条件的含义为:

(1)必须满足OkHttpClient.class在当前类路径中存在。

(2)必须满足工程配置文件中feign.okhttp.enabled配置项的值为true。

如果以上两个条件同时满足,
OkHttpFeignLoadBalancedConfiguration自动配置工作就会启动。

为了满足@ConditionalOnClass(OkHttpClient.class)的条件要求,由于OkHttpClient.class类的位置处于feign-okhttp相关的JAR包中,因此需要在pom文件加上feign-okhttp和okhttp3相关的Maven依赖,具体如下:



 com.squareup.okhttp3
 okhttp



 io.github.openfeign
 feign-okhttp

对于feign.okhttp.enabled配置项的设置,在默认情况下就为false。也就是说,如果需要使用feign-okhttp,就一定需要进行特别的配置,工程配置文件的配置项大致如下:

feign.httpclient.enabled=false
feign.okhttp.enabled=true

装配Client.Default负载均衡容器实例

最后来看如何配置一个包装Client.Default客户端实例的负载均衡容器实例。这个IOC实例的配置由
DefaultFeignLoadBalancedConfiguration自动配置类完成。该配置类也就是FeignRibbonClientAutoConfiguration配置类通过@import注解导入的第3个配置类。


DefaultFeignLoadBalancedConfiguration的源码节选如下:

package org.springframework.cloud.openfeign.ribbon;
//省略import
@Configuration
class DefaultFeignLoadBalancedConfiguration {
 DefaultFeignLoadBalancedConfiguration() {
 }
 @Bean
 @ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
 SpringClientFactory clientFactory)
 {
 return new LoadBalancerFeignClient( new Default((SSLSocketFactory)null, (HostnameVerifier)null), cachingFactory, client
 }
}

通过源码可以看出,如果前面两个客户端自动配置类的条件没有满足,IOC容器中没有feign.Client客户端容器实例,就创建一个默认的客户端实例:

(1)创建一个Client.Default默认客户端实例,该实例将使用HttpURLConnnection完成请求处理。

(2)创建一个LoadBalancerFeignClient负载均衡客户端实例,将Client.Default实例包装起来,然后返回LoadBalancerFeignClient客户端实例,作为feign.Client类型的Spring IOC容器实例。

最后小结一下本章的内容。本章通过对Spring Cloud中Feign核心原理和实现机制的解读,帮助大家深入彻底地了解Spring Cloud的底层原理。

本章层层递进,抽丝剥茧,着重介绍了远程接口的JDK Proxy代理实例的创建和Feign远程接口调用的执行两大流程。

本章虽然借助了Spring Cloud的源码,但并没有在源码中迷失,更加注重原理的分析和阐述。最终让大家既学习了Spring Cloud的原理,又阅读了Spring Cloud的源码,并且可以通过源码的学习领悟一些Java高手编程所用到的设计模式和代码组织方式。

你可能感兴趣的:(java,spring,cloud,rpc)