简介
在项目中我们有时候需要调用第三方的 API
,微服务架构中这种情况则更是无法避免——各个微服务之间通信。比如一般的项目中,有时候我们会使用 HTTP Client 发送 HTTP 请求来进行调用,而在微服务架构,Spring Cloud 全家桶中,Spring Cloud Feign 则是更常见的选择。那么,我如何只使用 Spring Cloud Feign 而不引入整个 Spring Cloud 呢?
什么是Feign?
Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息。
而Feign则会完全代理HTTP请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Feign整合了Ribbon和Hystrix,可以让我们不再需要显式地使用这两个组件。
总起来说,Feign具有如下特性:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解;
- 支持可插拔的HTTP编码器和解码器;
- 支持Hystrix和它的Fallback;
- 支持Ribbon的负载均衡;
- 支持HTTP请求和响应的压缩。
这看起来有点像我们springmvc模式的Controller层的RequestMapping映射。这种模式是我们非常喜欢的。Feign是用@FeignClient来映射服务的。
首先找一个AIP
免费的 API
特别多,github
上也有免费 API
地址汇总的 repo
,但这些都太正式了。有趣的事物总是会相互吸引的,无意间我发现了这么一个网站,“渣男:说话的艺术”(lovelive.tools) ,每次请求都可以获取一句甜言蜜语(渣男语录),特别良心的是,作者提供了 API
列表,给作者点赞!
如何调用第三方服务?
首先,我们先快速构建一个 Spring Boot 的 web 项目,这里我就省略了。然后在pom中添加feign的相关依赖
1 | <dependency> |
然后,在启动类添加响应的注解 @EnableFeignClients
:
1 |
|
接着,我们便可以配置我们的 Client 了,我们先创建一个接口类,比如叫 BadGuyFeignClient
,并声明为 FeignClient
:
1 |
|
@FeignClient
有以下几个较常用属性:
属性名 | 默认值 | 作用 | 备注 |
---|---|---|---|
value | 空字符串 | 调用服务名称,和name属性相同,如果项目使用了 Ribbon ,name 属性会作为微服务的名称,用于服务发现; |
|
serviceId | 空字符串 | 服务id,作用和name属性相同 | 已过期 |
name | 空字符串 | 调用服务名称,和value属性相同,如果项目使用了 Ribbon ,name 属性会作为微服务的名称,用于服务发现; |
|
url | 空字符串 | url一般用于调试,可以手动指定@FeignClient调用的地址 | |
decode404 | false | 配置响应状态码为404时是否应该抛出FeignExceptions | |
configuration | {} | Feign配置类,可以自定义 Feign的 Encoder、Decoder、LogLevel、Contract; | 参考FeignClientsConfiguration |
fallback | void.class | 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口 | 底层依赖hystrix,启动类要加上@EnableHystrix |
fallbackFactory | void.class | 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码 | |
path | 空字符串 | 自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping |
然后,我们便可以配置对应的属性,这里我们只是用来实现类似于 HTTP Client 的功能,所以只是简单配置了 url
和 path
这些属性:
1 | "badGuy", url = "${bab.guy.url}", path = "api") (name = |
声明为 FeignClient
之后,我们便可以在代码中使用 @Resource
或者 @Autowire
进行注入使用了:
1 |
|
然后 Controller
中是这么写的:
1 |
|
启动项目之后,我们可以访问
http://localhost:8080/api/badGuy/quotations
或者
http://localhost:8080/api/badGuy/quotations/10
后面跟数字,即可得到对应条目数的结果
1 | { |
Feign client 如何设置请求头信息?
在调用http接口时,一般都需要在请求头里面添加相应鉴权参数,类似于ak、sk之类的密钥或者某个动态参数等,那么在使用Feign client调用时,该如何添加请求头信息呢?
准备接口
首先,准备一个第三方服务接口,我这里直接在rap上定义了一个查询用户信息接口,调用该接口必须在请求头传一个token参数和请求体中传userId,postman请求示例如下图:
如何添加请求有呢,这里提供两种方式。
在请求方法上添加请求头参数
示例代码如下:
1 | "apiClient", url = "${test.url}", path = "api") (name = |
注意queryUser
方法中多了一个@RequestHeader
参数token
,这个就相当于往请求头添加了一个token参数,这种情况适用于该token是一个在业务中经常动态变化的参数,需要在接口调用方动态获取。
利用@FeignClient的configuration属性
新建一个配置类如下
1 | public class ClientConfiguration { |
修改请求类如下:
1 | "apiClientTwo", url = "${test.url}", path = "api", configuration = ClientConfiguration.class) (name = |
注意:这里使用了@FeignClient的configuration属性,并在该配置类中往请求头添加了token入参,这种方式适用于往请求头中存放的参数是固定的,类似于ak、sk或者用于授权的应用ID密钥之类的。
完整项目源码请参考:springboot-middleware-feign
FeignClient
与 HttpClient
的区别是什么?
HttpClient
与之同样实现的还有 Okhttp
、Httpurlconnection
、RestTemplate
等等,其 URL 参数是以编程方式构造的,数据被发送到其他服务。在更复杂的情况下,我们将不得不RestTemplate
深入到更低级别的 API
提供的甚至是 API
的细节。
FeignClient
则更像是在基于 REST 的服务调用上提供更高级别的抽象,在客户端编写声明式REST 服务接口,并使用这些接口来编写客户端程序。开发人员不用担心这个接口的实现。这将在运行时由 Spring 动态配置。通过这种声明性的方法,开发人员不需要深入了解由 HTTP 提供的 HTTP 级别 API
的细节的RestTemplate
。
总的来讲,FeignClient
更具抽象性,也更简单、灵活。
总结
本文简单介绍了如何使用 Spring Cloud Feign
组件来替代 HttpClient
来实现简单调用第三方服务的方法,除了集成 Feign
组件,我们也可以在项目中加入 Ribbon
用于服务发现,加入 Hystrix
用于服务熔断等等,这样就会完整地构建出一个基本服务了。