在项目中我们有时候需要调用第三方的 API
,微服务架构中这种情况则更是无法避免——各个微服务之间通信。比如一般的项目中,有时候我们会使用 HTTP Client 发送 HTTP 请求来进行调用,而在微服务架构,Spring Cloud 全家桶中,Spring Cloud Feign 则是更常见的选择。那么,我如何只使用 Spring Cloud Feign 而不引入整个 Spring Cloud 呢?这就要从一个渣男语录 API
说起了……
免费的 API
特别多,github
上也有免费 API
地址汇总的 repo
,但这些都太正式了。有趣的事物总是会相互吸引的,无意间我发现了这么一个网站,“渣男:说话的艺术”(https://lovelive.tools/) ,每次请求都可以获取一句甜言蜜语(渣男语录),这不正是我这个钢铁直男需要的吗?而且特别良心的是,作者提供了 API
列表,给作者点赞!
首先,我们先快速构建一个 Spring Boot 的 web 项目,这里我就省略了。然后在我们 gradle
文件中添加 spring-cloud-starter-openfeign
依赖:
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '2.1.3.RELEASE'
然后,在启动类添加响应的注解 @EnableFeignClients
:
@SpringBootApplication
@EnableFeignClients
public class MyOpenFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MyOpenFeignApplication.class, args);
}
}
接着,我们便可以配置我们的 Client 了,我们先创建一个接口类,比如叫 BadGuyFeignClient
,并声明为 FeignClient
:
@FeignClient
public interface BadGuyFeignClient {
}
@FeignClient
有以下几个较常用属性:
name
,value
:指定FeignClient
的名称,如果项目使用了Ribbon
,name
属性会作为微服务的名称,用于服务发现;url
:url
一般用于调试,可以手动指定@FeignClient
调用的地址fallback
,fallbackFactory
:fallback
定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback
指定的类必须实现@FeignClient
标记的接口;fallbackFactory
工厂类,用于生成fallback
类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码;configuration
:Feign
配置类,可以自定义Feign
的Encoder
、Decoder
、LogLevel
、Contract
;path
:定义当前FeignClient
的统一前缀。
然后,我们便可以配置对应的属性,这里我们只是用来实现类似于 HTTP Client 的功能,所以只是简单配置了 url
和 path
这些属性:
@FeignClient(name = "badGuy", url = "${bab.guy.url}", path = "api")
public interface BadGuyFeignClient {
/**
* 随机获取一句甜言蜜语
*
* @return
*/
@GetMapping("SweetNothings")
String getSweetNothings();
/**
* 获取 count 条甜言蜜语
*
* @param count 获取甜言蜜语条数
* @return Json 格式的结果
*/
@GetMapping("SweetNothings/{count}/Serialization/Json")
QuotationResult<String> getSweetNothingsJsonByCount(@PathVariable("count") Integer count);
}
声明为 FeignClient
之后,我们便可以在代码中使用 @Resource
或者 @Autowire
进行注入使用了:
@Component
public class BadServiceImpl implements BadGuyService {
@Autowired
private BadGuyFeignClient badGuyFeignClient;
@Override
public List<String> getQuotations(Integer count) {
if (count == null || count <= 0) {
String singleQuotation = badGuyFeignClient.getSweetNothings();
return new ArrayList<String>() {{
add(singleQuotation);
}};
}
return badGuyFeignClient.getSweetNothingsJsonByCount(count).getReturnObj();
}
}
然后 Controller
中是这么写的:
@GetMapping({"quotations", "quotations/{count}"})
public ResultWrapper<List<String>> getBadGuyQuotations(
@PathVariable(value = "count", required = false) Integer count
) {
try {
List<String> resultStrings = badGuyService.getQuotations(count);
return new ResultWrapper<>(resultStrings);
} catch (Exception e) {
log.error("Failed to get bad guy quotations.", e);
return new ResultWrapper<List<String>>() {{
setCode("1000002");
setMessage("error");
setData(null);
}};
}
}
启动项目之后,我们可以访问 http://localhost:8088/api/badGuy/quotations 或者 http://localhost:8088/api/badGuy/quotations/10 后面跟数字,即可得到对应条目数的结果。(完整项目地址在文末)
HttpClient
与之同样实现的还有 Okhttp
、Httpurlconnection
、RestTemplate
等等,其 URL 参数是以编程方式构造的,数据被发送到其他服务。在更复杂的情况下,我们将不得不RestTemplate
深入到更低级别的 API
提供的甚至是 API
的细节。
FeignClient
则更像是在基于 REST 的服务调用上提供更高级别的抽象,在客户端编写声明式 REST 服务接口,并使用这些接口来编写客户端程序。开发人员不用担心这个接口的实现。这将在运行时由 Spring 动态配置。通过这种声明性的方法,开发人员不需要深入了解由 HTTP 提供的 HTTP 级别 API
的细节的RestTemplate
。
总的来讲,FeignClient
更具抽象性,也更简单、灵活。
本文简单介绍了如何使用 Spring Cloud Feign
组件来替代 HttpClient
来实现调用第三方服务的方法,除了集成 Feign
组件,我们也可以在项目中加入 Ribbon
用于服务发现,加入 Hystrix
用于服务熔断等等,这样就会完整地构建出一个基本服务了。