什么是Feign

Feign 是由 Netflix 团队开发的一款基于 Java 实现的 HTTP client,借鉴了 Retrofi、 JAXRS-2.0、WebSocket 等类库。通过 Feign,我们可以像调用方法一样非常简单地访问 HTTP API。这篇博客将介绍如何使用原生的 Feign,注意,是原生的,不是经过 Spring 层层封装的 Feign。

补充一下,在 maven 仓库中搜索 feign,我们会看到两种 Feign: OpenFeign Feign 和 Netflix Feign。它们有什么区别呢?简单地说,OpenFeign Feign 的前身就是 Netflix Feign,因为 Netflix Feign 从 2016 年开始就不维护了,所以建议还是使用 OpenFeign Feign。

为什么使用Feign

为什么要使用HTTP client

首先,因为 Feign 本身是一款 HTTP client,所以,这里先回答:为什么使用 HTTP client?

假设不用 HTTP client,我们访问 HTTP API 的过程大致如下。是不是相当复杂呢?直接操作 socket 已经非常麻烦了,我们还必须在熟知 HTTP 协议的前提下自行完成报文的组装和解析,代码的复杂程度可想而知。

那么,这个过程是不是可以更简单一些呢?

我们可以发现,在上面的图中,红框的部分是相对通用的,是不是可以把这些逻辑封装起来?基于这样的思考,于是就有了 HTTP client(根据类库的不同,封装的层次会有差异)。

所以,为什么要使用 HTTP client 呢?简单地说,就是为了让我们更方便地访问 HTTP API。

为什么要使用Feign

HTTP client 的类库还有很多,例如 Retrofi、JDK 自带的 HttpURLConnection、Apache HttpClient、OkHttp、Spring 的 RestTemplate,等等。我很少推荐说要使用哪种具体的类库,如果真的要推荐 Feign 的话,主要是由于它优秀的扩展性(不是一般的优秀,后面的使用例子就可以看到)。

如何使用Feign

关于如何使用 Feign,官方给出了非常详细的文档,在我看过的第三方类库中,算是比较少见的。

本文用到的例子也是参考了官方文档。

项目环境说明

os:win 10

jdk:1.8.0_231

maven:3.6.3

IDE:Spring Tool Suite 4.6.1.RELEASE

引入依赖

这里引入 gson,是因为入门例子需要有一个 json 解码器。

    <properties>
<feign.version>11.2</feign.version>
</properties> <dependencies>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>${feign.version}</version>
</dependency>
</dependencies>

入门例子

入门例子中使用 Feign 来访问 github 的接口获取 Feign 这个仓库的所有贡献者。

通过下面的代码可以发现,Feign 本质上是使用了动态代理来生成访问 HTTP API 的代码,定义 HTTP API 的过程有点像在定义 advice。

// 定义HTTP API
interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors")
// @RequestLine(value = "GET /repos/{owner}/{repo}/contributors", decodeSlash = false)// 测试转义"/"、"+"
// @RequestLine("GET /repos/{owner:[a-zA-Z]*}/{repo}/contributors")// 测试正则校验
// @Headers("Accept: application/json") // 测试添加header
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
} public static class Contributor {
String login;
int contributions;
} public class MyApp { public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new GsonDecoder()) // 返回内容为json格式,所以需要用到json解码器
// .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true)) // 配置超时参数等
.target(GitHub.class, "https://api.github.com"); // 像调用方法一样访问HTTP API
github.contributors("OpenFeign", "feign").stream()
.map(contributor -> contributor.login + " (" + contributor.contributions + ")")
.forEach(System.out::println);
}
}

个性化配置

除了简单方便之外,Feign 还有一个很大的亮点,就是有相当优秀的扩展性,几乎什么都可以自定义。下面是官方给的一张图,基本涵盖了 Feign 可以扩展的内容。每个扩展支持都有一个对应的适配包,例如,更换解码器为 jackson 时,需要引入io.github.openfeign:feign-jackson的适配包。

更换为Spring的注解

在入门例子中,我们使用 Feign 自带的注解来定义 HTTP API。但是,对于习惯了 Spring 注解的许多人来说,无疑需要增加学习成本。我们自然会问,Feign 能不能支持 Spring 注解呢?答案是肯定的。Feign 不但能支持 Spring 注解,还可以支持 JAX-RS、SOAP 等等。

下面就是使用 Sping 注解定义 HTTP API 的例子。注意,pom 文件中要引入 io.github.openfeign:feign-spring4 的依赖

// 定义HTTP API
interface GitHub { @GetMapping("/repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@RequestParam("owner") String owner, @RequestParam("repo") String repo);
} public class MyApp { public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.contract(new SpringContract())// 自定义contract
.target(GitHub.class, "https://api.github.com");
}
}

自定义解码器和编码器

在入门例子中,我们使用 gson 来解析 json。那么,如果我想把它换成 jackson 行不行?Feign 照样提供了支持。

注意,pom 文件中要引入 io.github.openfeign:feign-jackson 的依赖

public class MyApp {

    public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new JacksonDecoder()) // 自定义解码器
.encoder(new JacksonEncoder()) // 自定义编码器
.target(GitHub.class, "https://api.github.com");
}
}

自定义内置的HTTP client

接下来的这个自定义就更厉害了。Feign 本身作为一款 HTTP client,竟然还可以支持其他 HTTP client。

这里用 OkHttp 作例子。注意,pom 文件中要引入 io.github.openfeign:feign-okhttp 的依赖

public class MyApp {

    public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.client(new OkHttpClient())// 自定义client
.target(GitHub.class, "https://api.github.com");
}
}

自定义拦截器

我们访问外部接口时,有时需要带上一些特定的 header,例如,应用标识、token,我们可以通过两种方式实现:一是使用注解定义 HTTP API,二是使用拦截器(更常用)。下面的例子中,使用拦截器给请求添加 token 请求头。

public class MyInterceptor implements RequestInterceptor {

    @Override
public void apply(RequestTemplate template) {
template.header("token", LoginUtils.getCurrentToken());
}
}
public class MyApp { public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.requestInterceptor(new MyInterceptor())
.target(GitHub.class, "https://api.github.com");
}
}

自定义重试器

默认情况下,Feign 访问 HTTP API 时,如果抛出IOException,它会认为是短暂的网络异常而发起重试,这时,Feign 会使用默认的重试器feign.Retryer.Default(最多重试 5 次),如果不想启用重试,则可以选择另一个重试器feign.Retryer.NEVER_RETRY。当然,我们也可以自定义。

奇怪的是,Feign 通过重试器的 continueOrPropagate(RetryableException e)方法是否抛出RetryableException来判断是否执行重试,为什么不使用 true 或 false 来判断呢?

注意,重试器是用来判断是否执行重试,自身不包含重试的逻辑。

public class MyRetryer implements Retryer {

    int attempt = 0;

    @Override
public void continueOrPropagate(RetryableException e) {
// 如果把RetryableException抛出,则不会继续重试
// 否则继续重试
if(attempt++ >= 3) {// 重试三次
throw e;
}
} @Override
public Retryer clone() {
return this;
}
}
public class MyApp { public static void main(String... args) {
// 获取用来访问HTTP API的代理类
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.retryer(new MyRetryer())
//.retryer(Retryer.NEVER_RETRY) // 不重试
.exceptionPropagationPolicy(ExceptionPropagationPolicy.UNWRAP)
.target(GitHub.class, "https://api.github.com");
}
}

结语

以上,基本讲完 Feign 的使用方法,其实 Feign 还有其他可以扩展的东西,例如,断路器、监控等等。感兴趣的话,可以自行分析。

最后,感谢阅读。

参考资料

Feign github

相关源码请移步:https://github.com/ZhangZiSheng001/feign-demo

本文为原创文章,转载请附上原文出处链接:https://www.cnblogs.com/ZhangZiSheng001/p/14989165.html

如何使用原生的Feign的更多相关文章

  1. Spring Cloud中Feign如何统一设置验证token

    代码地址:https://github.com/hbbliyong/springcloud.git 原理是通过每个微服务请求之前都从认证服务获取认证之后的token,然后将token放入到请求头中带过 ...

  2. Spring Cloud下使用Feign Form实现微服务之间的文件上传

    背景 ​ Spring Cloud现在已经被越来越多的公司采用了,微服务架构比传统意义上的单服务架构从复杂度上多了很多,出现了很多复杂的场景.比如,我们的产品是个app,支持第三方登录功能,在手机端调 ...

  3. ②SpringCloud 实战:引入Feign组件,完善服务间调用

    这是SpringCloud实战系列中第二篇文章,了解前面第一篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 简介 Feign 是一个声明式的 RE ...

  4. 如何使用原生的Ribbon

    什么是Ribbon 之前分析了如何使用原生的Feign,今天我们来研究 Netflix 团队开发的另外一个类库--Ribbon. Ribbon 和 Feign 有很多相似的地方,首先,它们本质上都是 ...

  5. 分享一个 SpringCloud Feign 中所埋藏的坑

    背景 前段时间同事碰到一个问题,需要在 SpringCloud 的 Feign 调用中使用自定义的 URL:通常情况下是没有这个需求的:毕竟都用了 SpringCloud 的了,那服务之间的调用都是走 ...

  6. Java面试题精选,大型网站系统架构你不得不懂的10个问题

    作者:JavaGuide(公众号) 下面这些问题都是一线大厂的真实面试问题,不论是对你面试还是说拓宽知识面都很有帮助.之前发过一篇8 张图读懂大型网站技术架构 可以作为不太了解大型网站系统技术架构朋友 ...

  7. Nepxion Discovery【探索】微服务企业级解决方案

    Nepxion Discovery[探索]微服务企业级解决方案] Nepxion Discovery[探索]使用指南,基于Spring Cloud Greenwich版.Finchley版和Hoxto ...

  8. 那天晚上和@FeignClient注解的深度交流

    废话篇 那晚,我和@FeignClient注解的深度交流了一次,爽! 主要还是在技术群里看到有同学在问相关问题,比如: contextId是干嘛的?name相同的多个Client会报错? 然后觉得有必 ...

  9. 不使用SpringBoot如何将原生Feign集成到Spring中来简化http调用

    在微服务架构中,如果使用得是SpringCloud,那么只需要集成SpringFeign就可以了,SpringFeign可以很友好的帮我们进行服务请求,对象解析等工作. 然而SpingCloud是依赖 ...

随机推荐

  1. 【转载】基于Linux命令行KVM虚拟机的安装配置与基本使用

    基于Linux命令行KVM虚拟机的安装配置与基本使用 https://alex0227.github.io/2018/06/06/%E5%9F%BA%E4%BA%8ELinux%E5%91%BD%E4 ...

  2. Linux_搭建Samba服务(匿名访问)

    [RHEL8]-SMBserver:[RHEL7]-SMBclient !!!测试环境我们首关闭防火墙和selinux(SMBserver和SMBclient都需要) [root@localhost ...

  3. IT菜鸟之BIOS和VT

    一.虚拟化:VT(Virtualization Technology) 二.BIOS (basic input output system基本输入输出系统) 主板优先启动--bios启动--bios开 ...

  4. ELK学习实验014:Nginx日志JSON格式收集

    1 Kibana的显示配置 https://demo.elastic.co/app/kibana#/dashboard/welcome_dashboard 环境先处理干净 安装nginx和httpd- ...

  5. 电路调试检测维修总结积累 20181015 板子:3060-A

    今天在检修一块3060-A电路板时 发现   3.3V烫 但是3.3V路上的电容并没有短路 于是拆单片机   拆RS232EN  拆  FM24V 最后发现  原来是  1117-3.3  处的33U ...

  6. 7.2-5 usermod

    7.2 usermod:修改用户信息 usermod 命令用于修改系统已经存在的用户的账号信息.   -c comment         修改用户password文件中用户的说明栏,同useradd ...

  7. 第七章 DevOps工具链

    DevOps工具生态圈 协同开发工具 敏捷开发 可视化 加强团队沟通协作 数据分析 协同开发 持续集成工具 Jenkins 自动化编译 自动化测试 自动化部署 丰富的插件库 版本管理工具 Git 简介 ...

  8. spark_shuffle方式的演进过程

    spark shuffle有四种方式,分别是 hashshuffle 优化后的hashshuffle sortshuffle bypass 一.hashshuffle与优化 一开始spark的shuf ...

  9. SQL SERVER 实现相同记录为空显示(多列去除重复值,相同的只显示一条数据)

    sql server语句查询中碰到结果集有重复数据,需要把这个重复数据汇总成一条显示.其余则正常显示. 使用SQL内置函数 ROW_NUMBER() 加 PARTITION 完成 ROW_NUMBER ...

  10. 调试动态加载的js

    用浏览器无法调试异步加载页面里包含的js文件.简单的说就是在调试工具里面看不到异步加载页面里包含的js文件   最近在一个新的web项目中开发功能.这个项目的管理界面有一个特点,框架是固定的,不会刷新 ...