一、简介
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.hreadpool.default.coreSize = 10
hystrix.hreadpool.default.maxQueueSize = 50
hystrix.hreadpool.default.queueSizeRejectionThreshold = 30
#切换http-client,okhttp组件
feign.httpclient.enabled = false
feign.okhttp.enabled = true