Feign 是一个 Java 到 HTTP 客户端的粘合剂。Feign 的目标是以最少的开销和代码把 你的代码连接到 http api 上。通过定制的编解码器和错误处理,你可以请求任何基于文本的 http api 。
通过处理注解信息来生成模板化请求。在发出请求前,参数直接应用到这些模板上。这限制了 Feign 只支持基于文本的 api,这显著简化了系统的一些方面如重放请求。
依赖问题。目前项目组用的是 Hessian 做远程调用,由于 Hessian 存在对 jar 包的依赖,特别是一些项目升级到 JDK 1.8,采用 Maven 构建;而老的一些任然采用 1.6 ,是个简单的 Eclipse 工程,导致打包、部署非常麻烦。
依赖于接口,使用简洁。对于客户端,只依赖于接口类,通过 Spring 注入实现,迁移基本不需要很大的改动。
与 Spring Cloud 集成。Feign 本身是 Spring Cloud 的一个组件,可以通过 Ribbon 做路由,可以进一步提升服务化。
系统对性能的要求并不是那种很严苛的,基于文本的调用也方便调试。
首先要定义 Java 代码到 HTTP 请求模板的注解,如下,在接口的方法上进行。
public interface HttpApi { // 提交一些简单类型的参数,返回一个复合对象的列表 @RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); // 指定 http 头的一些字段。提交复合类型的参数 @RequestLine("PUT /repos/contributors/{id}") @Headers("Content-Type: application/json") void putContributor(@Param("id") String id, Contributor contributor); // 定制请求体的内容 @RequestLine("POST /login") @Headers("Content-Type: application/json") // json 的花括号必须进行转义 @Body("%7B/"name/": /"{name}/", /"password/": /"{password}/"%7D") void login(@Param("name") String name, @Param("password") String password); }
调用;
HttpApi httpApi = Feign.builder().decoder(new GsonDecoder()).encoder(new GsonEncoder()).target(HttpApi.class, "https://api.github.com"); List<Contributor> contributors = httpApi.contributors("OpenFeign", "feign");
如果参数或返回值有复合对象,Feign 内置的编解码器是不支持的,可以使用基于 Gson 实现的 feign-gson 库。
构建器的 target 方法接收一个接口类和目标 http api 的基路径(跟 @RequestLine
里指定的路径组合起来就是完整的请求路径)。
使用方请求 http api 时就像调用本地方法一样: httpApi.contributors("OpenFeign", "feign");
。
对于没有在 注解里指定的参数,即使用了 @Param
注解,都放进一个 Map 里,编码后作为请求体。
这会导致服务器端没法把参数映射到多个 POJO 上。
@RequestLine("PUT /repos/contributors/params/{id}") @Headers("Content-Type: application/json") void putContributorParam(@Param("id") String id, @Param("contributor1") String contributor1, @Param("contributor2") String contributor2);
以上面的方式提交的 Feign 会把 contributor1, contributor2
参数放进一个 Map 里,然后把这个 Map 序列化为 JSON 串作为请求体发送出去。 这也是默认的方式。
如果需要以 方式发送请求,需要手工对参数值进行 URLEncode 。
@RequestLine("PUT /repos/contributors/params/{id}") @Headers("Content-Type: application/x-www-form-urlencoded") @Body("contributor1={contributor1}&contributor2={contributor2}") void putContributorParam(@Param("id") String id, @Param("contributor1") String contributor1, @Param("contributor2") String contributor2);
调用: httpApi.putContributorParam("123654", "+-*/=中文&contributor2=123", "0123457896");
Content-Type: application/x-www-form-urlencoded Content-Length: 42 contributor1=+-*/=&contributor2=0123457896
服务端得到的参数的 contributor1 是 " -*/=中文"
,contributor2 是 "123,0123457896"
。