在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,所以在调用远程服务时就必须使用HTTP客户端。咱们可使用JDK原生的URLConnection
、Apache的Http Client
、Netty的异步HTTP Client, Spring的RestTemplate
。可是,用起来最方便、最优雅的仍是要属Feign了。css
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 咱们能够作到使用HTTP请求远程服务时能与调用本地方法同样的编码体验,开发者彻底感知不到这是远程方法,更感知不到这是个HTTP请求。好比:java
@Autowired private AdvertGropRemoteService service; // 远程服务 public AdvertGroupVO foo(Integer groupId) { return service.findByGroupId(groupId); // 经过HTTP调用远程服务 }
开发者经过service.findByGroupId()
就能完成发送HTTP请求和解码HTTP返回结果并封装成对象的过程。apache
为了让Feign知道在调用方法时应该向哪一个地址发请求以及请求须要带哪些参数,咱们须要定义一个接口:json
@FeignClient(name = "ea") // [A] public interface AdvertGroupRemoteService { @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) // [B] AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId) // [C] @RequestMapping(value = "/group/{groupId}", method = RequestMethod.PUT) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName)
A: @FeignClient
用于通知Feign组件对该接口进行代理(不须要编写接口实现),使用者可直接经过@Autowired
注入。markdown
B: @RequestMapping
表示在调用该方法时须要向/group/{groupId}
发送GET
请求。app
C: @PathVariable
与SpringMVC
中对应注解含义相同。异步
Spring Cloud应用在启动时,Feign会扫描标有@FeignClient
注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每一个接口方法建立一个RequetTemplate
对象,该对象封装了HTTP请求须要的所有信息,请求参数名、请求方法等信息都是在这个过程当中肯定的,Feign的模板化就体如今这里。微服务
在本例中,咱们将Feign与Eureka和Ribbon组合使用,@FeignClient(name = "ea")
意为通知Feign在调用该接口方法时要向Eureka中查询名为ea
的服务,从而获得服务URL。性能
Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为java对象是由解码器(Decoder)完成的。编码
默认状况下,Feign会将标有@RequestParam
注解的参数转换成字符串添加到URL中,将没有注解的参数经过Jackson转换成json放到请求体中。注意,若是在@RequetMapping
中的method
将请求方式指定为POST
,那么全部未标注解的参数将会被忽略,例如:
@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET) void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);
此时由于声明的是GET请求没有请求体,因此obj
参数就会被忽略。
在Spring Cloud环境下,Feign的Encoder*只会用来编码没有添加注解的参数*。若是你自定义了Encoder, 那么只有在编码obj
参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter
类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的做用是能够根据HTTP响应信息返回一个异常,该异常能够在调用Feign接口的地方被捕获到。咱们目前就经过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。
Feign在默认状况下使用的是JDK原生的URLConnection
发送HTTP请求,没有链接池,可是对每一个地址会保持一个长链接,即利用HTTP的persistence connection
。咱们能够用Apache的HTTP Client替换Feign原始的http client, 从而获取链接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5
版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient
依赖:
<!-- 使用Apache HttpClient替换Feign原生httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>${feign-httpclient}</version> </dependency>
而后在application.properties
中添加:
feign.httpclient.enabled=true
经过Feign, 咱们能把HTTP远程调用对开发者彻底透明,获得与调用本地方法一致的编码体验。这一点与阿里Dubbo中暴露远程服务的方式相似,区别在于Dubbo是基于私有二进制协议,而Feign本质上仍是个HTTP客户端。若是是在用Spring Cloud Netflix搭建微服务,那么Feign无疑是最佳选择。