Feign如何对静态服务发起Http请求?
Feign单独使用时,可以对配置的静态服务发起Http调用;当Feign结合Ribbon时,对配置的静态服务可以根据负载均衡策略进行调用;再结合Eureka,可以配合注册中心以及负载均衡策略动态发起调用。本篇分析Feign对配置的静态服务是如何发起Http调用的
一、 Maven依赖
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
二 、自动配置类
Feign中的自动配置类是FeignAutoConfiguration,例举出其中重要的两个Bean
FeignContext
Targeter
FeignContext内部还有内置配置类FeignClientsConfiguration,这是创建服务提供方IOC容器时会被解析的配置类,例举出几个重要的BeanDecoder
Encoder
Feign.Builder
FeignLoggerFactory
Contract
三、 Bean释义
FeignContext
托管给容器的一个Bean,在以服务提供方为维度初始化子容器时会被使用到
@Bean public FeignContext feignContext() { FeignContext context = new FeignContext(); context.setConfigurations(this.configurations); return context; }
Targeter
用以生成Fiegn代理类
@Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new HystrixTargeter(); }
服务提供方维度的Bean
Decoder
编码器,用以对请求的参数做编码
@Bean 5b4 @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); }
Encoder
解码器,用以对服务端的响应做解码
@Bean @ConditionalOnMissingBean @ConditionalOnMissingClass("org.springframework.data.domain.Pageable") public Encoder feignEncoder() { return new SpringEncoder(this.messageConverters); }
Retryer
重试策略,默认不重试
@Bean @ConditionalOnMissingBean public Retryer feignRetryer() { return Retryer.NEVER_RETRY; }
Builder
用以封装FeignClient上所有配置信息
@Bean @Scope("prototype") @ConditionalOnMissingBean public Feign.Builder feignBuilder(Retryer retryer) { return Feign.builder().retryer(retryer); }
Contract
以类似于SpringMvc约定的参数格式来解析FeignClient接口的参数
@Bean @ConditionalOnMissingBean public Contract feignContract(ConversionService feignConversionService) { return new SpringMvcContract(this.parameterProcessors, feignConversionService); }
关于Feign的使用在官网有介绍:需要建立合适的FeignClient,配置好静态服务的节点信息,最终使用注解开启Feign功能。然后便可以用方法调用的形式来代替传统编写Http请求的步骤,极大简化了开发过程。而关于整个Feign可以分为开启与使用两步,开启:对配置的服务提供方资源进行收集并创建代理类;使用:当调用方法是,交由实际创建的代理类来请求服务提供方的资源
四 、FeignClient类收集
Feign的开启需要使用注解@EnableFeignClients,这也是整个Feign的入口
在能被容器扫描的类上配置注解@EnableFeignClients后,启动服务
Spring处理该类时,会从类上注解中收集导入的ImportBeanDefinitionRegistrar,随后调用它们的registerBeanDefinitions方法
@EnableFeignClients上导入的类是FeignClientsRegistrar
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented @Import(FeignClientsRegistrar.class)public @interface EnableFeignClients {
日常使用中,一个FeignClient类对应于一个服务,类中不同的方法对应与服务中不同的接口。这样配置之后,对不同类不同方法的调用,可以达到对不同服务不同接口的发起Http调用。
在FeignClientsRegistrar的registerBeanDefinitions方法中,分别为注册默认配置类和注册FeignClient类
在registerFeignClients中注册FeignClient类
使用容器的Scanner,配合注解类型过滤器,从配置的或者默认的路径中,扫描出带有注解@FeignClient的类形成BeanDefinition
在registerFeignClient中尝试将这些BeanDefinition注册到容器中
在构造BeanDefinitionBuilder时,属性contextId实际是@FeignClient注解上配置的value或者name属性,用以区分服务提供方,BeanDefinitionBuilder持有的BeanDefinition的BeanClass已经被设置为FeignClientFactoryBean(这是一个FactoryBean,在Bean需要往容器托管时,其getObject方法会被调用),最后构造完成后注册到容器中。
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); } public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); Set<String> basePackages; Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName()); AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { final Set<String> clientClasses = new HashSet<>(); basePackages = new HashSet<>(); for (Class<?> clazz : clients) { basePackages.add(ClassUtils.getPackageName(clazz)); clientClasses.add(clazz.getCanonicalName()); } 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) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; AnnotationMetadata annotationMetadata = beanDefinition.getMetadata(); Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface"); Map<String, Object> attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes); } } } } private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); 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(); boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be // null beanDefinition.setPrimary(primary); String qualifier = getQualifier(attributes); if (StringUtils.hasText(qualifier)) { alias = qualifier; } BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias }); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); }
五 创建服务提供方IOC容器
FeignClientFactoryBean与FeignClient具有对应关系,一个FeignClient对应一个FeignClientFactoryBean(FeignClient上配置的name或value唯一),FeignClientFactoryBean有实现接口ApplicationContextAware,所以在回调方法中设置了属性applicationContext
在FeignClientFactoryBean的getObject方法中,实际调用的是getTarget
方法getTarget中首先从当前容器中获取FeignContext,也就是在自动配置类中托管的BeanFeignContext
方法feign中获取到Builder对象
以FeignClient上是否有配置url来决定启动负载均衡与否
对于配置了url的FeignClient(静态服务),在获取到Client与Targeter之后构造出代理对象。默认未配置Client,使用默认实现
@Override public Object getObject() throws Exception { return getTarget(); } <T> T getTarget() { FeignContext context = this.applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) { if (!this.name.startsWith("http")) { this.url = "http://" + this.name; } else { this.url = this.name; } this.url += cleanPath(); return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.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) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap 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)); }
上述获取Builder 、Client 和Targeter并不是像获取FeignContext一样从当前容器中获取,而是从以服务提供方为维度的子IOC容器中获取,这点是和获取Ribbon配置的组件一样的流程
获取Builder调用了方法get
获取Client调用了方法getOptional
获取Targeter调用了方法get
但最终都是调用了context的getInstance方法
protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off Feign.Builder builder = get(context, Feign.Builder.class) // required values .logger(logger) .encoder(get(context, Encoder.class)) .decoder(get(context, Decoder.class)) .contract(get(context, Contract.class)); // @formatter:on configureFeign(context, builder); return builder; } protected <T> T get(FeignContext context, Class<T> type) { T instance = context.getInstance(this.contextId, type); if (instance == null) { throw new IllegalStateException( "No bean found of type " + type + " for " + this.contextId); } return instance; } protected <T> T getOptional(FeignContext context, Class<T> type) { return context.getInstance(this.contextId, type); } protected <T> T get(FeignContext context, Class<T> type) { T instance = context.getInstance(this.contextId, type); if (instance == null) { throw new IllegalStateException( "No bean found of type " + type + " for " + this.contextId); } return instance; }
获取实例的方法由FeignContext继承自父类NamedContextFactory
先尝试获取服务提供方的上下文
如果还未创建服务提供方上线文,尝试创建子IOC容器
容器类型为AnnotationConfigApplicationContext,并将FeignContext内置的配置类FeignClientsConfiguration以注册Bean的形式注册到容器中
在随后的refresh动作中,配置类上的配置的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; } protected AnnotationConfigApplicationContext getContext(String name) { if (!this.contexts.containsKey(name)) { synchronized (this.contexts) { if (!this.contexts.containsKey(name)) { this.contexts.put(name, createContext(name)); } } } return this.contexts.get(name); } protected AnnotationConfigApplicationContext createContext(String name) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); if (this.configurations.containsKey(name)) { for (Class<?> configuration : this.configurations.get(name) .getConfiguration()) { context.register(configuration); } } for (Map.Entry<String, C> entry : this.configurations.entrySet()) { if (entry.getKey().startsWith("default.")) { for (Class<?> configuration : entry.getValue().getConfiguration()) { context.register(configuration); } } } context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType); context.getEnvironment().getPropertySources().addFirst(new MapPropertySource( this.propertySourceName, Collections.<String, Object>singletonMap(this.propertyName, name))); if (this.parent != null) { // Uses Environment from parent as well as beans context.setParent(this.parent); // jdk11 issue // https://github.com/spring-cloud/spring-cloud-netflix/issues/3101 context.setClassLoader(this.parent.getClassLoader()); } context.setDisplayName(generateDisplayName(name)); context.refresh(); return context; }
六 、为FeignClient创建代理类
待服务提供方上下文创建完成,FeignClientFactoryBean从上下文中获取到Builder、Client和Targeter后,开始创建代理类
targeter是配置的BeanHystrixTargeter,封装了HardCodedTarget对象作为参数后,调用到Feign的内部类Builder的target方法
用调用方配置封装了Factory对象,这是SynchronousMethodHandler的内部类
用FeignClient的配置封装了ParseHandlersByName
最终再封装为ReflectiveFeign返回,封装时的invocationHandlerFactory是默认的InvocationHandlerFactory.Default
// FeignClientFactoryBean.getTarget return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)); // HystrixTargeter @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class); if (setterFactory != null) { builder.setterFactory(setterFactory); } Class<?> fallback = factory.getFallback(); if (fallback != void.class) { return targetWithFallback(factory.getName(), context, target, builder, fallback); } Class<?> fallbackFactory = factory.getFallbackFactory(); if (fallbackFactory != void.class) { return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory); } return feign.target(target); } public <T> T target(Target<T> target) { return build().newInstance(target); } public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
创建代理类的过程是在ReflectiveFeign的newInstance中完成的
在封装的ParseHandlersByName中解析FeignClient类
使用配置的SpringMvc参数格式解析类来从FeignClient类中提取元数据,实际是由父类BaseContract完成
使用封装的SynchronousMethodHandler.Factory来创建FeignClient类中的方法对应的方法处理器MethodHandler(SynchronousMethodHandler)
将方法名与方法处理器的对应关系map返回
回到ReflectiveFeign中key看到将方法与方法处理器的映射关系保存在methodToHandler中
使用InvocationHandlerFactory.Default来创建代理类
Default中最终创建的代理类是FeignInvocationHandler
@Override public <T> T newInstance(Target<T> target) { Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); 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); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; } // SpringMvcContract的父类BaseContract @Override public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) { checkState(targetType.getTypeParameters().length == 0, "Parameterized types unsupported: %s", targetType.getSimpleName()); checkState(targetType.getInterfaces().length <= 1, "Only single inheritance supported: %s", targetType.getSimpleName()); if (targetType.getInterfaces().length == 1) { checkState(targetType.getInterfaces()[0].getInterfaces().length == 0, "Only single-level inheritance supported: %s", targetType.getSimpleName()); } Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>(); for (Method method : targetType.getMethods()) { if (method.getDeclaringClass() == Object.class || (method.getModifiers() & Modifier.STATIC) != 0 || Util.isDefault(method)) { continue; } MethodMetadata metadata = parseAndValidateMetadata(targetType, method); checkState(!result.conta 45a6 insKey(metadata.configKey()), "Overrides unsupported: %s", metadata.configKey()); result.put(metadata.configKey(), metadata); } return new ArrayList<>(result.values()); } // ReflectiveFeign内部类ParseHandlersByName public Map<String, MethodHandler> apply(Target key) { List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type()); Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>(); for (MethodMetadata md : metadata) { BuildTemplateByResolvingArgs buildTemplate; if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) { buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else if (md.bodyIndex() != null) { buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else { buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder); } result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); } return result; } // SynchronousMethodHandler public MethodHandler create(Target<?> target, MethodMetadata md, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) { return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, decode404, closeAfterDecode, propagationPolicy); } // InvocationHandlerFactory内部实现类Default @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { return new ReflectiveFeign.FeignInvocationHandler(target, dispatch); } }
七、FeignClient调用
服务启动,FeignClient类被收集,对应的代理类被创建。当调用FeignClient类的方法时,实际是对代理类的调用,以完成对静态服务资源的请求
方法被调用,代理类FeignInvocationHandler的invoke方法被调用
dispatch维护了方法与方法处理器的对应关系,获取当前调用的方法对应的方法处理器并执行invoke方法
方法处理器的类型为SynchronousMethodHandler
在其invoke方法中,根据方法参数和配置的SpringMvc参数转化器,构造RequestTemplate,调用executeAndDecode
在方法executeAndDecode中,交由配置的BeanClient来执行请求
默认的Client是Client的内部实现类Default,这是Feign的内部类Builder中的默认属性
内部实现类Default中,使用JDK原生的HttpURLConnection完成了http请求
// ReflectiveFeign内部类FeignInvocationHandler @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(); } return dispatch.get(method).invoke(args); } // SynchronousMethodHandler类 @Override public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); Retryer retryer = this.retryer.clone(); while (true) { try { return executeAndDecode(template); } 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) throws Throwable { Request request = targetRequest(template); if (logLevel != Logger.Level.NONE) { logger.logRequest(metadata.configKey(), logLevel, request); } Response response; long start = System.nanoTime(); try { response = client.execute(request, options); } 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); boolean shouldClose = true; try { if (logLevel != Logger.Level.NONE) { response = logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime); } if (Response.class == metadata.returnType()) { if (response.body() == null) { return response; } if (response.body().length() == null || response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { shouldClose = false; return response; } // Ensure the response body is disconnected byte[] bodyData = Util.toByteArray(response.body().asInputStream()); return response.toBuilder().body(bodyData).build(); } if (response.status() >= 200 && response.status() < 300) { if (void.class == metadata.returnType()) { return null; } else { Object result = decode(response); shouldClose = closeAfterDecode; return result; } } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) { Object result = decode(response); shouldClose = closeAfterDecode; return result; } else { throw errorDecoder.decode(metadata.configKey(), response); } } catch (IOException e) { if (logLevel != Logger.Level.NONE) { logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime); } throw errorReading(request, response, e); } finally { if (shouldClose) { ensureClosed(response.body()); } } } // Client内部实现类Default @Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection, request); } HttpURLConnection convertAndSend(Request request, Options options) throws IOException { final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection(); if (connection instanceof HttpsURLConnection) { HttpsURLConnection sslCon = (HttpsURLConnection) connection; if (sslContextFactory != null) { sslCon.setSSLSocketFactory(sslContextFactory); } if (hostnameVerifier != null) { sslCon.setHostnameVerifier(hostnameVerifier); } } connection.setConnectTimeout(options.connectTimeoutMillis()); connection.setReadTimeout(options.readTimeoutMillis()); connection.setAllowUserInteraction(false); connection.setInstanceFollowRedirects(options.isFollowRedirects()); connection.setRequestMethod(request.httpMethod().name()); Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING); boolean gzipEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP); boolean deflateEncodedRequest = contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE); boolean hasAcceptHeader = false; Integer contentLength = null; for (String field : request.headers().keySet()) { if (field.equalsIgnoreCase("Accept")) { hasAcceptHeader = true; } for (String value : request.headers().get(field)) { if (field.equals(CONTENT_LENGTH)) { if (!gzipEncodedRequest && !deflateEncodedRequest) { contentLength = Integer.valueOf(value); connection.addRequestProperty(field, value); } } else { connection.addRequestProperty(field, value); } } } // Some servers choke on the default accept string. if (!hasAcceptHeader) { connection.addRequestProperty("Accept", "*/*"); } if (request.requestBody().asBytes() != null) { if (contentLength != null) { connection.setFixedLengthStreamingMode(contentLength); } else { connection.setChunkedStreamingMode(8196); } connection.setDoOutput(true); OutputStream out = connection.getOutputStream(); if (gzipEncodedRequest) { out = new GZIPOutputStream(out); } else if (deflateEncodedRequest) { out = new DeflaterOutputStream(out); } try { out.write(request.requestBody().asBytes()); } finally { try { out.close(); } catch (IOException suppressed) { // NOPMD } } } return connection; }
八、替换Http请求组件
只使用Feign时,默认的Client是内部类Default,可以通过配置依赖选择使用ApacheHttpClient或OkHttpClient
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-httpclient --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>11.0</version> </dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>11.0</version> </dependency>
最后
感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。
也欢迎大家关注我的公众号:程序员麦冬,麦冬每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!
- 如何使用事务码SMICM分析ABAP代码发起的HTTP请求的错误ICM_HTTP_SSL_PEER_CERT_UNTRUSTED
- 通过java,如何对代码里发起的http请求进行抓包?
- SpringCloud Feign服务的消费者,如何相互调用
- 前后端完全分离后前端如何启动静态服务保证开发人员的访问?
- nginx 已有80端口服务如何在开启一个非80端口的静态资源指向
- AngularJS如何跨域发起Http请求(Access-Control-Allow-Origin)
- 如何使用Google Volley网络库发起带Header的HTTP请求?
- 如何使用HttpURLConnection发起这http请求
- spring cloud各个微服务之间如何相互调用(Feign、Feign带token访问服务接口)
- 如何让aspnet服务加载静态资源html(我的动态网页静态化) 转
- 如何解决使用Feign远程调用服务时返回Null的情况
- Spring boot2X Consul如何使用Feign实现服务调用
- 如何在发起HTTP请求时附带Cookie
- 了解如何设计和开发基于Http请求的数据接口服务系统
- 我的Spark学习之路(二)geotrellis初探:如何发起WEB服务显示地图
- 了解如何设计和开发基于Http请求的数据接口服务系统
- 如何使用feign调用服务
- 类中的静态方法如何调用?-------Bosent中同一类下面的两个服务怎么调用
- Spring Cloud源码分析之Eureka篇第四章:服务注册是如何发起的
- webservice开发---------如何使用cxf发布soap方式的web服务