一、简介
Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka、Ribbon组合使用以支持负载均衡;可以与hystrix结合实现服务的限流、熔断、降级。
二、@EnableFeignClients
我们通常使用@EnableFeignClients注解开启feign的功能,那么就先从这个注解入手。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {};
//可以指定具体的包路径
String[] basePackages() default {};
//指定具体的class
Class<?>[] basePackageClasses() default {};
//指定配置
Class<?>[] defaultConfiguration() default {};
//指定feign client 为null通过扫描得到
Class<?>[] clients() default {};
}
导入了FeignClientsRegistrar,@Import注解的用法这里就不多说了,不懂的自行学习。
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册配置类
registerDefaultConfiguration(metadata, registry);
// 注册feign clients
registerFeignClients(metadata, registry);
}
}
注册配置类
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 {
name = "default." + metadata.getClassName();
}
//注册到容器中
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
//注册了FeignClientSpecification的beandefinition到容器中,后续后实例化
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
//添加构造方法入参
builder.addConstructorArgValue(name);
//添加构造方法入参
builder.addConstructorArgValue(configuration);
//通过构造方法,把配置类填充到FeignClientSpecification属性中去
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
注册feign clients
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 获取扫描classpath下component组件的扫描器
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
// 获取注解@EnableFeignClients的信息
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");
// @EnableFeignClients的属性clients为空
if (clients == null || clients.length == 0) {
// 扫描器增加过滤器
scanner.addIncludeFilter(annotationTypeFilter);
// 获取配置的扫描包的路径,如果没配置,默认为启动类的包路径
basePackages = getBasePackages(metadata);
}
else {
final Set<String> clientClasses = new HashSet<>();
basePackages = new HashSet<>();
//把指定了client所在的包放入basePackages
for (Class<?> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
//通过过滤器筛选需要注册的feign client
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)));
}
for (String basePackage : basePackages) {
//
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 判断注解的类是否是一个接口
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
//获取feign client的属性
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取client的名称 顺序为 contextId,name,serviceId
String name = getClientName(attributes);
//注册每个client的配置信息
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册feign client 就是往注册器中添加了一个bd 后续会实例化成一个bean
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
注册bd
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
//生成FeignClientFactoryBean的bd,是一个FactoryBean
//这也是为什么@FeignClient作用在一个接口上,却还能被其他bean用@Autowired注解引用
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);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
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 = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
//原始类的className
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
//注册bd
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
实例化feign client
feign client的实例化就是通过FeignClientFactoryBean的getObject方法实现的,下面来解析一下
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
@Override
public Object getObject() throws Exception {
return getTarget();
}
<T> T getTarget() {
// 这个FeignContext在FeignAutoConfiguration配置中已经声明了,所以可以直接用applicationContext获取bean
FeignContext context = applicationContext.getBean(FeignContext.class);
//配置feign 的decoder、encoder、retryer、contract、RequestInterceptor等
//这些有默认配置,在FeignAutoConfiguration及FeignClientsConfiguration中有默认配置
Feign.Builder builder = feign(context);
//没有指定url属性
if (!StringUtils.hasText(url)) {
//自动拼接缺省的http请求前缀
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
//直接返回负载均衡client,发送请求时会先经过负载均衡器,再调用实际发送请求的client
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();
// feign默认集成了ribbon,所以拿到的是LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// 获取负载均衡中 实际发送发送请求的client
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// 获取负载均衡器中 实际发送请求的client
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
// 不走负载均衡
builder.client(client);
}
//默认DefaultTargeter,如果使用了Hystrix 此处返回的就是HystrixTargeter
Targeter targeter = get(context, Targeter.class);
//创建实例 根据Targeter的类型选择创建逻辑
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
}
DefaultTargeter走的逻辑就是HystrixTargeter的简化逻辑,所以我们直接分析HystrixTargeter就好了
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
//没有开启hystrix
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
//如果开启了hystrix
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
//获取实例名称
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
//注解中的fallback
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(name, context, target, builder, fallback);
}
//注解中的FallbackFactory
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
//开启了hystrix,但是没有写fallback或fallbackFactory方法
return feign.target(target);
}
这里分成了几种情况,但是逻辑也差不太多,我们以fallback为例分析
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
//获取fallback类的实例
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
//创建feign client实例
return builder.target(target, fallbackInstance);
}
public <T> T target(Target<T> target, T fallback) {
// fallback实例被封装成了FallbackFactory
return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
.newInstance(target);
}
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
//添加了一个处理器工厂,后续通过工厂的create方法创建handler
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
//如果是没有使用fallback的,nullableFallbackFactory是null
return new HystrixInvocationHandler(target, dispatch, setterFactory,
nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
//调用父类的创建方法
return super.build();
}
//创建实例
public <T> T newInstance(Target<T> target) {
//获取所有的方法,并为方法创建相应的处理器SynchronousMethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
//遍历放入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)));
}
}
//通过工厂创建处理器
InvocationHandler handler = factory.create(target, methodToHandler);
//jdk动态代理创建feign client实例
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
调用逻辑
上面讲到了feign client的创建,在请求调用的时候,主要的逻辑就是通过InvocationHandler的invoke方法处理请求的。InvocationHandler是根据工厂类创建的,如果你的项目启用了Hystrix那么创建的就是HystrixInvocationHandler,如果没有启用创建的就是FeignInvocationHandler。HystrixInvocationHandler跟FeignInvocationHandler的区别就是在http调用前实现了Hystrix的限流,熔断等逻辑,最后都会调用到SynchronousMethodHandler的invoke方法。我们就以简单的FeignInvocationHandler为例分析一下远程调用过程吧。
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();
}
//根据方法获取对应的处理器SynchronousMethodHandler
return dispatch.get(method).invoke(args);
}
SynchronousMethodHandler
public Object invoke(Object[] argv) throws Throwable {
// 根据方法参数构造出请求模板对象
RequestTemplate template = buildTemplateFromArgs.create(argv);
// 可以在方法参数上面指定请求配置对象Options
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;
}
}
}
请求发送
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 根据请求模板对象得到请求request对象
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 通过具体的client组件去进行网络请求
// 如果没有指定url,会使用ribbon的LoadBalancerFeignClient
response = client.execute(request, options);
// 封装响应对象
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;
}
}
三、自动装配
自动装配原理spring.factories中写了一些配置类,这个类中实例化了一些组件的必要实例对象,我们看一下
1、FeignAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
//FeignClient配置文件
@EnableConfigurationProperties({ FeignClientProperties.class,
FeignHttpClientProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public HasFeatures feignFeature() {
return HasFeatures.namedFeature("Feign", Feign.class);
}
//feign上下文,可对外提供类似spring上下文的功能
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
//实例化HystrixTargeter
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
//httpClient组件
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
//feign.httpclient.enabled为true的时候生效
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
private final Timer connectionManagerTimer = new Timer(
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
@Autowired(required = false)
private RegistryBuilder registryBuilder;
private CloseableHttpClient httpClient;
@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(),
this.registryBuilder);
this.connectionManagerTimer.schedule(new TimerTask() {
@Override
public void run() {
connectionManager.closeExpiredConnections();
}
}, 30000, httpClientProperties.getConnectionTimerRepeat());
return connectionManager;
}
@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;
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
@PreDestroy
public void destroy() throws Exception {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
//OkHttpClient组件
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
//当不存在ILoadBalancer类的时候,因为feign默认集成了ribbon,所以想要生效,必须手动排除掉ribbon的jar包
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
//feign.okhttp.enabled为true的时候生效
@ConditionalOnProperty("feign.okhttp.enabled")
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();
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 (this.okHttpClient != null) {
this.okHttpClient.dispatcher().executorService().shutdown();
this.okHttpClient.connectionPool().evictAll();
}
}
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
}
通过调用的源码我们可以知道client的远程调用是通过jdk自带的HttpURLConnection进行的,我们可以切换为HttpClient或者OkHttpClient。
切换为HttpClient
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.10.1</version>
</dependency>
配置文件
feign.httpclient.enabled = true
切换为OkHttpClient
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>10.10.1</version>
</dependency>
配置文件
feign.httpclient.enabled = false
feign.okhttp.enabled = true
超时配置
@Bean
@ConditionalOnMissingBean
public Request.Options feignRequestOptions() {
return new Request.Options(connectTime, TimeUnit.MILLISECONDS, readTime, TimeUnit.MILLISECONDS, true);
}
2、FeignLoadBalancerAutoConfiguration
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientFeignLoadBalancerConfiguration.class,
OkHttpFeignLoadBalancerConfiguration.class,
DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {
}
这里又导入了三个配置类,对应了默认的Client,HttpClient和OkHttpClient,上面的切换的原理正常情况下在这地方生效的。以其中一个HttpClient为例
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
HttpClient httpClient) {
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
}
}
导入了HttpClientFeignConfiguration配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public 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(),
//最大连接数,默认200
httpClientProperties.getMaxConnections(),
//每个管道的最大连接数,默认50
httpClientProperties.getMaxConnectionsPerRoute(),
//每条连接的最大存活时间,默认900
httpClientProperties.getTimeToLive(),
//时间单位,默认 秒
httpClientProperties.getTimeToLiveUnit(), this.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;
}
//创建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 {
this.connectionManagerTimer.cancel();
if (this.httpClient != null) {
this.httpClient.close();
}
}
}
四、常用配置
#ribbon组件开启,默认也是true
ribbon.eureka.enabled = true
#请求连接的超时时间
ribbon.ConnectTimeout = 5000
#请求处理的超时时间
ribbon.ReadTimeout = 25000
#同一实例最大重试次数,不包括首次调用。默认值为0
ribbon.MaxAutoRetries = 0
#同一个服务其他实例的最大重试次数,不包括第一次调用的实例。默认值为1
ribbon.MaxAutoRetriesNextServer = 0
#是否所有操作都允许重试。默认值为false
ribbon.OkToRetryOnAllOperations = false
#hystrix组件的配置
feign.hystrix.enabled = true
#是否开启超时熔断
hystrix.command.default.execution.timeout.enabled = true
#断路器超时设置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 30000
#熔断线程数限制
hystrix.threadpool.default.coreSize = 10
hystrix.threadpool.default.maxQueueSize = 50
hystrix.threadpool.default.queueSizeRejectionThreshold = 30
#切换http-client,okhttp组件
feign.httpclient.enabled = false
feign.okhttp.enabled = true