经过前文讲解, 我们已使用 Eureka 实现服务发现; 使用 Ribbon 实现了负载均衡这种听起来很高端的东西. 我们的架构已经初具雏形, 但依然存在很多问题, 下面不妨来分析下前文的代码 --
- @GetMapping("/users/{id}")
- public User findById(@PathVariable Long id) {
- // 这里用到了 RestTemplate 的占位符能力
- User user = this.restTemplate.getForObject(
- "http://microservice-provider-user/users/{id}",
- User.class,
- id
- );
- // ... 电影微服务的业务...
- return user;
- }
这里, this.restTemplate.getForObject("http://microservice-provider-user/users/{id}"... 这行代码是比较糟糕的, 存在诸多问题 --
如果系统业务非常复杂, 而你是一个新人, 当你看到这行代码, 恐怕很难一眼看出其用途是什么! 此时, 你很可能需要寻求老同事的帮助 (往往是这行代码的作者, 哈哈哈, 可万一离职了呢?), 或者查阅该目标地址对应的文档 (文档常常还和代码不匹配, 哈哈哈), 才能清晰了解这行代码背后的含义! 否则, 你只能陷入蛋疼的境地!
这个例子构造的 URL 非常简单, 但如果你需要构造类似如下这么丑陋的 URL 时 (原谅我老是拿百度开涮, 其实我没有恶意):
https://www.baidu.com/s?wd=asf&rsv_spt=1&rsv_iqid=0xa25bbeba000047fd&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=3&rsv_sug1=2&rsv_sug7=100&rsv_sug2=0&inputT=328&rsv_sug4=328
, 恐怕就有心无力了! 尽管 RestTemplate 支持使用占位符, 从而让我们避免字符串拼接的尴尬境地, 但构造这么复杂的 URL 依然是很麻烦的. 更可怕的是, 互联网时代需求变化非常之快, 你的参数可能会从 10 个变成 12 个, 15 个, 再后来又精简成 13 个...... 维护这个 URL 真的是想想都累...... 总而言之, 复杂 URL 的构造会让你处于一种不性福的状态!
铺垫了这么多, 如何解决以上问题? 用 Feign!
TIPS
Feign 本质上来说是个山寨, 其设计思想基本都来源于 Retrofit(使用方式更是如出一辙).
Retrofit 的 GitHub:<https://github.com/square/retrofit> https://github.com/square/retrofit> ; , 如果你知道 Square 公司, 那么你很厉害! 是的, Retrofit 也是开源 OKHttp 的那家公司开源的 -- 所以, 笔者喜欢将 Square 公司称为''HTTP 客户端小王子", 但其实人家是做移动支付的.
Spring Cloud 对 Retrofit 也有支持: , 目前正在孵化中, 有兴趣的可以去体验一下. 学会 Feign 后, Retrofit 上手也就是 5 分钟的事情.
简介
Feign 是 Netflix 开发的声明式, 模板化的 HTTP 客户端, 其灵感来自 Retrofit,JAXRS-2.0 以及 webSocket.Feign 可帮助我们更加便捷, 优雅地调用 HTTP API.
在 Spring Cloud 中, 使用 Feign 非常简单 -- 只需创建接口, 并在接口上添加注解即可.
Feign 支持多种注解, 例如 Feign 自带的注解或者 JAX-RS 注解等. Spring Cloud 对 Feign 进行了增强, 使其支持 Spring MVC 注解, 另外还整合了 Ribbon 和 Eureka, 从而使得 Feign 的使用更加方便.
TIPS
Feign 的 GitHub: https://github.com/OpenFeign/feign
Quick Start
下面来将前面的例子用 Feign 改写, 让其达到与 Ribbon + RestTemplate 相同的效果.
复制项目 microservice-consumer-movie, 将 ArtifactId 修改为 microservice-consumer-movie-feign ;
加依赖:
- <dependency>
- <groupId>
- org.springframework.cloud
- </groupId>
- <artifactId>
- spring-cloud-starter-openfeign
- </artifactId>
- </dependency>
加注解: 启动类上添加 @EnableFeignClients ;
编写 Feign Client:
- @FeignClient(name = "microservice-provider-user")
- public interface UserFeignClient {
- @GetMapping("/users/{id}")
- User findById(@PathVariable("id") Long id);
- }
这样一个 Feign Client 就写完啦! 其中,@FeignClient 注解中的 microservice-provider-user 是想要请求服务的名称, 这是用来创建 Ribbon Client 的 (Feign 整合了 Ribbon). 在本例中, 由于使用了 Eureka, 所以 Ribbon 会把 microservice-provider-user 解析成 Eureka Server 中的服务.
除此之外, 还可使用 url 属性指定请求的 URL(URL 可以是完整的 URL 或主机名), 例如 @FeignClient(name = "abcde", url = "http://localhost:8000/") . 此时, name 可以是任意值, 但不可省略, 否则应用将无法启动!
- Controller:
- @RequestMapping("/movies")
- @RestController
- public class MovieController {
- @Autowired
- private UserFeignClient userFeignClient;
- @GetMapping("/users/{id}")
- public User findById(@PathVariable Long id) {
- return this.userFeignClient.findById(id);
- }
- }
只需使用 @Autowire 注解, 即可注入上面编写的 Feign Client.
RestTemplate 与 Feign 对比
相信通过本文的例子, 聪明的你对如何使用 Feign 已经了然于心了. 文章的最后, 对比一下 RestTemplate + Ribbon 与 Feign.
角度 | RestTemplate + Ribbon | Feign(自带 Ribbon) |
---|---|---|
可读性、可维护性 | 欠佳(无法从 URL 直观了解这个远程调用是干什么的) | 极佳(能在接口上写注释,方法名称也是可读的,能一眼看出这个远程调用是干什么的) |
开发体验 | 欠佳( 拼凑 URL 不性福 ) | 极佳(写出漂亮的代码, 女朋友更爱你了 ) |
风格一致性 | 欠佳(本地 API 调用和 RestTemplate 调用的代码风格截然不同) | 极佳(完全一致,不点开 Feign 的接口,根本不会察觉这是一个远程调用而非本地 API 调用) |
性能 | 较好 | 中等(性能是 RestTemplate 的 50% 左右;如果为 Feign 配置连接池,性能可提升 15% 左右) |
灵活性 | 极佳 | 中等(内置功能能满足大多数项目的需求) |
那么如何选择呢? 相信这才是大家最关注的问题!
笔者认为 --
一般来说, 建议使用 Feign, 并杜绝使用 RestTmplate. 为什么用 Feign 相信不必啰嗦; 可为什么要杜绝 RestTemplate, 那是因为在一个项目里, 保持统一的编码风格乃至体验, 是非常重要的. 我个人的架构原则是尽量减少开发人员的选择, 如果 A 能解决问题, 就杜绝使用 B-- 最佳实践永远只有一个! 并且, 共存带来的往往不是相得益彰, 反而是歧义, 错乱以及额外的学习成本, 理解成本 (笔者当年参与过一个同时使用 Struts1 + Struts2 + Servlet 的项目, 可以想象一下学习成本有多高; 笔者还参与一个一个使用 Jackson + FastJson + JSON-lib + Gson 的项目, 可想而知操作 JSON 的代码有多混乱......80% 的开发在骂娘中度过时光, 并抨击别人使用他不熟悉的 JSON 操作库, 后来被笔者统一成 Jackson 后, 大家都安心干活了)!
有人可能会对 Feign 的性能存在顾虑, 笔者认为, Feign 的性能虽然不那么优秀, 但大部分场景下都是 OK 的 -- 项目的性能瓶颈一般都不出在 HTTP 客户端上, 而在于自身业务的处理!
求同存异 -- 上文虽说要杜绝 RestTemplate, 但事无绝对, 你得根据具体情况具体分析 -- 对于某些变态需求, 在使用 Feign 很难实现或无法实现时, 可考虑使用 RestTemplate + Feign 共存的方式......Spring Cloud 官方也承认, 无论 Fegin 怎么改进, 其灵活性也无法比得上 RestTemplate! 但是, 这么做之前请务必慎重, 记住, 共存带来的往往不是相得益彰, 反而是歧义, 错乱以及额外的学习成本, 理解成本.
配套代码
GitHub:
Gitee:
本文首发
http://www.itmuch.com/spring-cloud/finchley-9/
干货分享
来源: http://www.bubuko.com/infodetail-2916074.html