问题描述

最近在使用Spring Cloud改造现有服务的工作中,在内部服务的调用方式上选择了Feign组件,由于服务与服务之间有权限控制,发现通过Feign来进行调用时如果发生了401、407错误时,调用方不能够取回被调用方返回的错误信息。

产生原因

Feign默认使用java.net.HttpURLConnection进行通信,通过查看其子类sun.net.www.protocol.http.HttpURLConnection源码发现代码中在进行通信时单独对错误码为401\407的错误请求做了处理,当请求的错误码为401\407时,会关闭请求流,由于此时还并没有将返回的错误信息写入响应流中,所以接收的返回信息中仅仅能获取到response.status(),而response.body()null

HttpURLConnection相关信息的源码链接:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/net/www/protocol/http/HttpURLConnection.java#1079

解决思路

关于此问题产生的原因已经很明显了,就是feign.Client实现通信的方式选用了我们不想使用的HttpURLConnection。想到通常在Spring的代码中OCP都是运用得很好的,所以基本上有解决此问题的信心了,最不济就是自己扩展Feign,实现一个自己想要的feign.Client,当然这种事情Spring Cloud基本都会自己搞定,这也是Spring Cloud强大完善的一个地方。

通过这个思路查看源码,果然看到了Spring Cloud在使用Feign提前内置了三种通信方式(feign.Client.Defaultfeign.httpclient.ApacheHttpClientfeign.okhttp.OkHttpClient),其中缺省的情况使用的就是feign.Client.Default,这个就是使用HttpURLConnection通信的方式。

源码解析

Spring Cloud项目中使用了Ribbon的组件,其会帮助我们管理使用Feign,查看org.springframework.cloud.netflix.feign.ribbon.FeignRibbonClientAutoConfiguration源码

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignRibbonClientAutoConfiguration { @Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory);
} @Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignLoadBalancedConfiguration { @Autowired(required = false)
private HttpClient httpClient; @Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
ApacheHttpClient delegate;
if (this.httpClient != null) {
delegate = new ApacheHttpClient(this.httpClient);
}
else {
delegate = new ApacheHttpClient();
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
} @Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "feign.okhttp.enabled", matchIfMissing = true)
protected static class OkHttpFeignLoadBalancedConfiguration { @Autowired(required = false)
private okhttp3.OkHttpClient okHttpClient; @Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
OkHttpClient delegate;
if (this.okHttpClient != null) {
delegate = new OkHttpClient(this.okHttpClient);
}
else {
delegate = new OkHttpClient();
}
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
}
  • feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) 方法结合其上注解我们可以很清楚的知道,当没有feign.ClientBean的时候会默认生成feign.Client.Default来进行通信,这就是之前说的缺省通信方式
  • HttpClientFeignLoadBalancedConfigurationOkHttpFeignLoadBalancedConfiguration,我们可以看到其生效的条件,当classpath中有feign.httpclient.ApacheHttpClient并且配置feign.httpclient.enabled=true(缺省为true)、feign.okhttp.OkHttpClient并且配置feign.okhttp.enabled=true(缺省为true)
  • 当使用ApacheHttpClient或者OkHttpClient进行通信时就不会导致发生401\407错误时,取不到返回的错误信息了

解决方法

通过其上的分析,解决方法已经显而易见了,

  1. pom.xml文件中新增
      <dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>8.18.0</version>
</dependency>

或者

      <dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
  1. application.properties文件中增加(可缺省,如有切换调用方式需求可添加配置进行控制)

    feign.okhttp.enabled=true

    或者

    feign.httpclient.enabled=true

总结

  • 由于新增的依赖没有被starter管理,并且缺省不会导致程序启动异常,并且返回响应为null与此依赖没有直接关系,因此不方便定位到问题,特此记录下来,希望能帮助到遇到同样问题的人,如对文章有不同的看法,望给予指正。
  • 本文建立在已经搭建完成Feign的调用基础之上,没有讲述Feign的使用,因为此类文章很多,在此就不重复了。

Spring Cloud项目中通过Feign进行内部服务调用发生401\407错误无返回信息的问题的更多相关文章

  1. 【spring】在spring cloud项目中使用@ControllerAdvice做自定义异常拦截,无效 解决原因

    之前在spring boot服务中使用@ControllerAdvice做自定义异常拦截,完全没有问题!!! GitHub源码地址: 但是现在在spring cloud中使用@ControllerAd ...

  2. 【spring】【spring mvc】【spring boot】获取spring cloud项目中所有spring mvc的请求资源

    实现的方法: 1.在父级项目中 或者 每个微服务都引用的项目中添加实体类Resource 2.在父级项目中 或者 每个为服务都引用的项目中写一个工具类,作用是用来获取请求资源 3.在每一个微服务的启动 ...

  3. Spring Cloud(十二)声名式服务调用:Feign 的使用(下)

    前言 本文是对上一篇博文的扩充,很多平时用不到的特性就开始简略一写,Spring Cloud各版本之间的差距很大的,用不到的可能下一个版本就被kill掉了.由于笔者写本文开始的时候误解了Feign的继 ...

  4. Spring Cloud Alibaba(四)实现Dubbo服务消费

    本项目演示如何使用 Spring Cloud Alibaba 完成 Dubbo 的RPC调用. Spring Cloud与Dubbo Spring Cloud是一套完整的微服务架构方案 Dubbo是国 ...

  5. Spring cloud项目实践(一)

    链接地址:http://sail-y.github.io/2016/03/21/Spring-cloud%E9%A1%B9%E7%9B%AE%E5%AE%9E%E8%B7%B5/ 什么是Spring ...

  6. Spring Cloud Feign 声明式服务调用

    目录 一.Feign是什么? 二.Feign的快速搭建 三.Feign的几种姿态 参数绑定 继承特性 四.其他配置 Ribbon 配置 Hystrix 配置 一.Feign是什么? ​ 通过对前面Sp ...

  7. 控制反转和spring在项目中可以带来的好处

    Spring实例化Bean的三种方式分别是: 1,xml配置使用bean的类构造器 <bean id="personService" class="cn.servi ...

  8. 如何使用windows版Docker并在IntelliJ IDEA使用Docker运行Spring Cloud项目

    如何使用windows版Docker并在IntelliJ IDEA使用Docker运行Spring Cloud项目 #1:前提准备 1.1 首先请确认你的电脑是windows10专业版或企业版,只有这 ...

  9. 你真的理解 Spring Boot 项目中的 parent 吗?

    前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...

随机推荐

  1. API创建/更新员工联系电话

    DECLARE ln_phone_id PER_PHONES.PHONE_ID%TYPE; ln_object_version_number PER_PHONES.OBJECT_VERSION_NUM ...

  2. 关于tomcat中Servlet对象池

    Servlet在不实现SingleThreadModel的情况下运行时是以单个实例模式,如下图,这种情况下,Wrapper容器只会通过反射实例化一个Servlet对象,对应此Servlet的所有客户端 ...

  3. (一〇八)iPad开发之横竖屏适配

    在iPad开发中,横竖屏的视图常常是不同的,例如侧边栏Dock,在横屏时用于屏幕较宽,可以展示足够多的内容,每个按钮都可以展示出标题:而竖屏时Dock应该比较窄,只显示图标不现实按钮标题. iPad比 ...

  4. UNIX网络编程——带外数据小结

    TCP没有真正的带外数据,不过提供紧急模式和紧急指针.一旦发送端进入紧急模式,紧急指针就出现在发送到对端的分节中的TCP首部中.连接的对端收取该指针是在告知接收进程发送端已经进入紧急模式,而且该指针指 ...

  5. iOS中 自定义cell升级版 (高级)

    接上次分享的自定义cell进行了优化:http://blog.csdn.net/qq_31810357/article/details/49611255 指定根视图: self.window.root ...

  6. TCP的定时器系列 — SYNACK定时器

    主要内容:SYNACK定时器的实现,TCP_DEFER_ACCPET选项的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 在上一篇博客中,已经连带 ...

  7. Scipy教程 - 距离计算库scipy.spatial.distance

    http://blog.csdn.net/pipisorry/article/details/48814183 在scipy.spatial中最重要的模块应该就是距离计算模块distance了. fr ...

  8. 小强的HTML5移动开发之路(10)——在线画板

    来自:http://blog.csdn.net/dawanganban/article/details/18094557 在这一篇文章中我们用html5来实现一个在线的画板,玩过你画我猜的朋友今天也可 ...

  9. android studio2.0出现的gradle问题,instant Run即时运行不了.

    android studio 2.0出现的gradle问题: instant Run即时运行不了.经历了几乎9个preView版本的AS2.0,终于迎来了正式版,然而晴天我的霹雳,好不容易装好的2.0 ...

  10. MinerQueue.java 访问队列

    MinerQueue.java 访问队列 package com.iteye.injavawetrust.miner; import java.util.HashSet; import java.ut ...