推荐文章
深入理解Feign的Method has too many Body parameters异常
Feign 实现多文件上传
spring cloud feign (包含上传文件和下载文件)
Feign实现多文件上传,Open Feign多文件上传解决方案
前面,我们已经把Feign的2个注解:@FeignClient、@EnableFeignClients,以及Feign客户端接口扫描注册过程,包括Feign创建代理的过程、代理执行请求的流程都分析了一遍。其实,还有2部分没有提到:OpenFeign是如何完成自动配置的
、OpenFeign的负载均衡实现
。
在我们引入的spring-cloud-openfeign-core
的jar包中,里面存在spring.factories文件,配置了如下内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
我们不妨先看下FeignAutoConfiguration
@Configuration
@ConditionalOnClass(Feign.class) // 必须存在Fegin这个类,该自动配置类才生效
@EnableConfigurationProperties({FeignClientProperties.class, // 前缀为: feign.client.xxx配置
FeignHttpClientProperties.class}) // 前缀为: feign.httpclient.xxx配置
public class FeignAutoConfiguration {
// 这里会默认注入容器中所有配置的 FeignClientSpecification 的bean
// 那么,在哪里有配置这个bean呢?
// 1. @EnableFeignClients会默认配置
// beanName-> default.@EnableFeignClients所标注的类的类名+FeignClientSpecification
// beanClass-> FeignClientSpecification(并且我们指定了2个参数,一个是: default, 另一个参数是从@EnableFeignClients注解上获取的配置类)
// 2. 在@EnableFeignClients扫描包下的@FeignClient标注的客户端接口, 会配置
// beanName->客户端名字(contextId->value->name->serviceId) .FeignClientSpecification
// beanClass-> FeignClientSpecification(并且我们指定了2个参数,一个是: name(contextId->value->name->serviceId) ,
// 另外一个参数是从@FeignClient注解上获取的配置类)
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
// springcloud提供了featuresEndpoint,可以方便我们查看系统启动的一些features,进而了解系统特征
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
// 这个就比较关键了
// FeignContext继承自NamedContextFactory
// 它持有了全部注入的FeignClientSpecification, 这些配置将会被用来创建spring子容器(每个子容器都有自己的名字)
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
// 当存在feign.hystrix.HystrixFeign这个类时,
// 并且我们没有自定义Targeter的bean时,将会使用HystrixTargeter
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
// 当不存在feign.hystrix.HystrixFeign这个类时,使用 DefaultTargeter
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
// 以下2个内部配置类, 都是当不存在ILoadBalancer负载均衡器时, 并且引入对应的依赖才会生效
/* 需要引入feign-httpclient的依赖
io.github.openfeign
feign-httpclient
9.4.0
*/
@Configuration
@ConditionalOnClass(ApacheHttpClient.class) // 必须导入ApacheHttpClient
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer") // 没有引入ILoadBalancer负载均衡器依赖
@ConditionalOnMissingBean(CloseableHttpClient.class) // 允许用户自定义CloseableHttpClient
@ConditionalOnProperty(value = "feign.httpclient.enabled", // feign.httpclient.enabled为true, 或未配置
matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
// 配置 HttpClientConnectionManager
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) // 可以通过feign.httpclient.xxx配置
{
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
// 配置 CloseableHttpClient
@Bean
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
this.httpClient = httpClientFactory.createBuilder().
setConnectionManager(httpClientConnectionManager).
setDefaultRequestConfig(defaultRequestConfig).build();
return this.httpClient;
}
// 将apache的httpClient适配成Feign需要Client接口实例
@Bean
@ConditionalOnMissingBean(Client.class) // 当已经配置了Client的bean时,就不会配置这个了
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
/*
引入依赖
io.github.openfeign
feign-okhttp
11.8
*/
@Configuration
@ConditionalOnClass(OkHttpClient.class) // 必须导入okhttp的依赖
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer") // 没有引入ILoadBalancer负载均衡器依赖
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class) // 允许用户自定义OkHttpClient实例的bean
@ConditionalOnProperty(value = "feign.okhttp.enabled") // 必须用户主动开启okhttp才会生效
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
// 连接池的 配置
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
// okhttp3的配置
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
// 将okhttp3适配成feign的Client接口实例
@Bean
@ConditionalOnMissingBean(Client.class) // 当已经配置了Client的bean时,就不会配置这个了
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
}
在FeignAutoConfiguration中,配置FeignContext
这个bean,而从上一节分析中,我们发现在FeignClientFactoryBean中在拿组件的时候,几乎都会从FeignContext中去拿的,所以,有必要看下FeignContext。
可以把FeignContext理解为一个spring子容器的集合,每一个spring子容器都会有自己的名字
,同时创建一个spring(子)容器,需要指定一个或多个配置类
(就好比我们之前的spring.xml配置文件),而当我们需要组件的时候,首先,要根据名字,从FeignContext中取出对应的spring子容器
,然后根据类型,从spring子容器中取出对应的bean
,这样就可以使用不同名字就可以拿到不同的组件,也就是配置隔离
。
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, // 默认使用的配置类
"feign", // 属性源的名字
"feign.client.name" // 属性名
);
}
}
FeignContext 继承自NamedContextFactory
,见名知义,带名字的容器工厂。
// NamedContextFactory的泛型是 内部的 Specification接口
// 也就是说: NamedContextFactory的实现类, 必须指定一个泛型,
// 并且这个泛型要实现NamedContextFactory.Specification接口,
// 那么,指定这个泛型有什么用呢 ?
// 就相当于说, 子类可以自定义自己的Specification实现,
// 比如: FeignContext有自己的Specification实现(FeignClientSpecification)
// Ribbon也有自己的Specification实现(RibbonClientSpecification)
// 但是,不管它们怎么实现,每个Specification实现都会由一个name和一个配置类数组组成
// 并且,不同的Specification实现,不能混在一起,由子类自己去定义实现,就不会搞混了,
// 你注入你的Specification实现(比如feign),我注入我的Specification实现(比如ribbon)
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
implements DisposableBean,
ApplicationContextAware { // 外部spring父容器回调设置
// 一个Specification由一个name和一个配置类数组组成(由配置类, 我们就可以根据配置类创建spring容器了呀)
public interface Specification {
String getName();
Class<?>[] getConfiguration();
}
// 名字 -> spring子容器
private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();
// 名字 -> NamedContext.Spcification接口实例
private Map<String, C> configurations = new ConcurrentHashMap<>();
// 外部spring父容器
private ApplicationContext parent;
// 默认配置类
private Class<?> defaultConfigType;
// 属性源名字(比如: 子类FeignContext是:"feign")
private final String propertySourceName;
// 属性名(比如: 子类FeignContext是:"feing.client.name")
private final String propertyName;
// 唯一构造方法,传入默认配置类、属性源名字、属性名
public NamedContextFactory(Class<?> defaultConfigType,
String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;
}
// 回调设置外部spring父容器
@Override
public void setApplicationContext(ApplicationContext parent) throws BeansException {
this.parent = parent;
}
// 由子类设置 Specification接口实例 集合, 放入configurations中
// (比如: FeignAutoConfiguration中就把自动注入的List集合设置进来了)
public void setConfigurations(List<C> configurations) {
for (C client : configurations) {
// 名字 -> NamedContext.Specification接口实例
this.configurations.put(client.getName(), client);
}
}
// 获取所有子容器的名字
public Set<String> getContextNames() {
return new HashSet<>(contexts.keySet());
}
// 销毁时, 关闭所有子容器
@Override
public void destroy() {
Collection<AnnotationConfigApplicationContext> values = this.contexts.values();
for (AnnotationConfigApplicationContext context : values) {
// This can fail, but it never throws an exception (you see stack traces
// logged as WARN).
context.close();
}
this.contexts.clear();
}
// 根据名字,从NamedContext中,找出名字对应的spring子容器(如果此时还没有,则会去创建该容器)
protected AnnotationConfigApplicationContext getContext(String name) {
// 先从contexts属性中查找
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
// 没有,则走创建的逻辑
this.contexts.put(name, createContext(name));
}
}
}
// 如果有, 则直接取出
return this.contexts.get(name);
}
// 创建对应名字的spring子容器
protected AnnotationConfigApplicationContext createContext(String name) {
// 直接new(只要来拿, 我就new一个出来, 不管有没有)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 前面设置进来的NamedContext.Specification实例的getName()方法返回值就是name
// 也就是: 这里先查看是否设置了对应名字的NamedContext.Specification实例
if (this.configurations.containsKey(name)) {
// 如果有设置, 则取出NamedContext.Specification实例的所有配置类,注册到上面创建的容器中
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
context.register(configuration);
}
}
// 看完了有没有指定name的Specification,
// 然后继续遍历configurations,找出其中以"default."开头的Specification实例
// (比如: 在上一篇中, 在feign客户端的扫描过程中,
// 是有添加default.@EnableFeignClients所标注的类的类名+FeignClientSpecification的FeignClientSpecification实例的,
// 并且该实例的配置类, 就是@EnableFeignClients注解的defaultConfiguration属性)
// 同样,也把它们作为配置类注册到容器中,
// 这样就相当于用户主动设置的保底配置(注意注册的顺序就是解析的顺序)
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
// 同时注册2个类: PropertyPlaceholderAutoConfiguration 和 默认配置类
// (比如: FeignContext默认的配置类是FeignClientsConfiguration, 它是框架指定的兜底配置, 并且里面生效条件基本都以用户设置的优先)
context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);
// 注册了一个属性源
// (比如: FeignContext设置的是 feign:feign.client.name->{name})
context.getEnvironment().getPropertySources()
.addFirst(new MapPropertySource(this.propertySourceName,
Collections.singletonMap(this.propertyName, name)));
// 将外部spring容器设置为当前子容器的父容器(所以子容器自然能去父容器里去拿组件了)
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
}
// 设置displayName为 “NamedContextFactory-{name}”
context.setDisplayName(generateDisplayName(name));
// 所有的配置类、属性源、父容器设置好后,刷新该springg子容器(解析配置类,注册配置的组件)
context.refresh();
return context;
}
// 从对应名字的容器中获取对应类型的bean(包括父容器, 优先子容器)
public <T> T getInstance(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type).length > 0) {
return context.getBean(type);
}
return null;
}
// 延迟获取(ClientFactoryObjectProvider)
public <T> ObjectProvider<T> getLazyProvider(String name, Class<T> type) {
return new ClientFactoryObjectProvider<>(this, name, type);
}
// 延迟获取(BeanObjectProvider)
public <T> ObjectProvider<T> getProvider(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
return context.getBeanProvider(type);
}
// 从对应名字的子容器中,根据类和泛型获取组件
public <T> T getInstance(String name, Class<?> clazz, Class<?>... generics) {
ResolvableType type = ResolvableType.forClassWithGenerics(clazz, generics);
return getInstance(name, type);
}
// 从spring子容器中(包括父容器, 优先子容器), 获取对应类型(及泛型)的组件
public <T> T getInstance(String name, ResolvableType type) {
AnnotationConfigApplicationContext context = getContext(name);
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type);
if (beanNames.length > 0) {
for (String beanName : beanNames) {
if (context.isTypeMatch(beanName, type)) {
return (T) context.getBean(beanName);
}
}
}
return null;
}
// 从spring子容器中(包括父容器, 优先子容器), 获取“所有”对应类型的组件
public <T> Map<String, T> getInstances(String name, Class<T> type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,type).length > 0) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}
return null;
}
}
FeignClientsConfiguration是在FeignContext构造方法中指定的默认配置类
,同时从FeignContext的父类NamedContext中,我们知道FeignClientsConfiguration是最后注册到spring子容器中的配置类
,它是框架提供给我们的默认兜底的配置类,我们可以覆盖其中配置的组件,达到配置feign客户端
的目的。
@Configuration
public class FeignClientsConfiguration {
// 注入消息转化器(包含许多的消息转换器, 见: HttpMessageConvertersAutoConfiguration)
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
// 注入注解参数处理器
// PathVariableParameterProcessor、
// QueryMapParameterProcessor、
// RequestHeader、
// RequestParamParameterProcessor
@Autowired(required = false)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
// 注入Feign格式化注册器
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
// 注入feign的logger
@Autowired(required = false)
private Logger logger;
// 定义feign的解码器
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
// 定义feign的编码器
@Bean
@ConditionalOnMissingBean
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
}
// 定义feign的契约
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
// 格式化类型转换器
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
// 如果引入了feign-hystrix, 则定义HystrixFeign.Builder(它实现了Feign.Builder)
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
// feign的重试器,默认不重试,遇到异常,直接将异常抛给调用者
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
// 上面静态内部类中也定义了一个Feign.Builder, 那么谁生效呢?
// 在解析一个配置类时, 先解析里面的成员类 (ConfigurationClassParser#doProcessConfigurationClass)
// 所以,上面的定义先生效,如果上面没满足生效条件,这个才生效
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
// Feign的日志工厂
// (如果logger不为null,则直接使用该logger;如果为null,则使用new Slf4jLogger(Class>))
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(logger);
}
}
在分析feign的负载均衡的过程前,先回顾下FeignClientFactoryBean#getTarget()
,这个方法会返回Feign客户端接口的代理对象。当我们的Feign客户端没有设置url时,就会从FeignContext中获取Client实例
,然后Feign会使用这个Client实例发送请求。(而当我们设置了url时,并且也没有配置Client实例时,则会使用默认的Client.Default
(见:Feign.Builder#client属性))
<T> T getTarget() {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
// 如果没有设置url,将会使用负载均衡
return (T) loadBalance(builder,
context,
new HardCodedTarget<>(this.type,
this.name,
url
)
);
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this,
builder,
context,
new HardCodedTarget<>(this.type,
this.name,
url)
);
}
protected <T> T loadBalance(Feign.Builder builder,
FeignContext context,
HardCodedTarget<T> target) {
// 从FeignContext中获取Client(这一步,就是负载均衡的扩展点入口)
Client client = getOptional(context, Client.class);
if (client != null) {
// 将获取到的Client,设置给了Feign.Builder
builder.client(client);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
// 负载均衡需要引入spring-cloud-starter-netflix-ribbon
throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
从上面,我们得出了结论:要实现负载均衡,容器必须提供Feign的Client实现,这个Client实现将会用于负载均衡,这个类就是LoadBalancerFeignClient
,我们先看它是怎么配置到容器中的。
// 必须存在com.netflix.ribbon:ribbon-loadbalancer的负载均衡依赖 和 Feign的依赖
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
// 在Feign的自动配置类之前执行
@AutoConfigureBefore(FeignAutoConfiguration.class)
// 开启feign.httpclient的属性配置
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class, // 使用ApacheClient的实现,包装在LoadBalancerFeignClient中
OkHttpFeignLoadBalancedConfiguration.class, // 使用OkHttpClient的实现,包装在LoadBalancerFeignClient中
DefaultFeignLoadBalancedConfiguration.class // 使用Feign自己默认的Client.Default的实现,包装在LoadBalancerFeignClient中
})
public class FeignRibbonClientAutoConfiguration {
// SpringClientFactory也继承自:NamedContextFactory
// 只不过它提供的NamedContextFactory.Specification是RibbonClientSpecification
// 所以说Feign的负载均衡就是借用了ribbon的实现
// 当没有引入spring-retry的依赖时, 这个配置生效(可被用户覆盖)
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(SpringClientFactory factory) {
// 可缓存Feign客户端的对应的FeignLoadBalancer(同一个clientName,只创建一个FeignLoadBalancer)
return new CachingSpringLoadBalancerFactory(factory);
}
// 当引入了spring-retry的依赖时, 这个配置生效(可被用户覆盖)
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(SpringClientFactory factory,
LoadBalancedRetryFactory retryFactory) {
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}
// Request.Options可被覆盖
@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
}
}
public class CachingSpringLoadBalancerFactory {
// ribbon的NamedContextFactory
protected final SpringClientFactory factory;
// 负载均衡重试工厂
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
this.factory = factory;
}
// 根据客户端名称 获取 FeignLoadBalaner
public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
this.factory = factory;
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
}
public FeignLoadBalancer create(String clientName) {
// 先从缓存中拿,缓存中有,则直接返回;没有,则须创建
FeignLoadBalancer client = this.cache.get(clientName);
if(client != null) {
return client;
}
// 从ribbon的NamedContextFactory,根据客户端名字获取IClientConfig (客户端配置隔离的体现)
IClientConfig config = this.factory.getClientConfig(clientName);
// 从ribbon的NamedContextFactory,根据客户端名字获取ILoadBalancer(客户端配置隔离的体现)
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
// 从ribbon的ServerIntrospector ,根据客户端名字获取ILoadBalancer(客户端配置隔离的体现)
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
// 创建FeignLoadBlancer
client = loadBalancedRetryFactory != null ?
new RetryableFeignLoadBalancer(lb, config, serverIntrospector,loadBalancedRetryFactory)
: new FeignLoadBalancer(lb, config, serverIntrospector);
// 放入缓存
this.cache.put(clientName, client);
return client;
}
}
@Configuration
@ConditionalOnClass(ApacheHttpClient.class) // 必须引入feign-httpclient的依赖
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
class HttpClientFeignLoadBalancedConfiguration {
@Configuration
@ConditionalOnMissingBean(CloseableHttpClient.class)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
private CloseableHttpClient httpClient;
@Autowired(required = false)
private RegistryBuilder registryBuilder;
@Bean
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
public HttpClientConnectionManager connectionManager(
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
FeignHttpClientProperties httpClientProperties) {
final HttpClientConnectionManager connectionManager = connectionManagerFactory
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
httpClientProperties.getMaxConnectionsPerRoute(),
httpClientProperties.getTimeToLive(),
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@Bean
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
return this.httpClient;
}
@Bean
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties);
return this.httpClient;
}
private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager,
FeignHttpClientProperties httpClientProperties) {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.build();
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).
setConnectionManager(httpClientConnectionManager).build();
return httpClient;
}
@PreDestroy
public void destroy() throws Exception {
connectionManagerTimer.cancel();
if(httpClient != null) {
httpClient.close();
}
}
}
// 直接使用LoadBalancerFeignClient包装了apache请求客户端
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled")
class OkHttpFeignLoadBalancedConfiguration {
@Configuration
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
protected static class OkHttpFeignConfiguration {
private okhttp3.OkHttpClient okHttpClient;
@Bean
@ConditionalOnMissingBean(ConnectionPool.class)
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
Long timeToLive = httpClientProperties.getTimeToLive();
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
}
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
followRedirects(followRedirects).
connectionPool(connectionPool).build();
return this.okHttpClient;
}
@PreDestroy
public void destroy() {
if(okHttpClient != null) {
okHttpClient.dispatcher().executorService().shutdown();
okHttpClient.connectionPool().evictAll();
}
}
}
// 直接使用LoadBalancerFeignClient包装了okhttp请求客户端
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
@Configuration
class DefaultFeignLoadBalancedConfiguration {
// 直接使用LoadBalancerFeignClient包装了Feign原始的请求客户端
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory,
clientFactory);
}
}