无关微服务中,服务与服务如何通信,我曾经给大家介绍了Ribbon近程调用的相干常识,不晓得大家有没有发现Ribbon的问题呢?

Ribbon的问题

在Ribbon中,如果咱们想要发动一个调用,是这样的:

@Resourceprivate RestTemplate restTemplateString result = restTemplate.getForObject("http://my-goods/goods/get", String.class);Goods goods = JSONObject.parseObject(result, Goods.class);

这就像一个一般的http申请一样,须要对入参和出参进行手动解决。

打一眼看上去如同没什么问题,但认真一想就不对劲了:这个被调用的接口都是咱们本人写的,入参和出参都是确定的,甚至写被调用的接口的人都是同一个...有没有一种更好的形式,比方像调用本地办法一样间接调用它呢?

比方这样:

Goods goods = goodsServer.get();

这个术语叫:近程办法调用

明天的配角Feign就为咱们实现了这样的性能,上面有请~

什么是Feign

官网是这样介绍的:Feign 是受Retrofit、JAXRS-2.0和WebSocket启发的 Java 到 HTTP 客户端binder(我也不晓得这里怎么翻译)。

为什么说是binder?在看Feign所实现的性能及出发点来说,Feign自身并未实现HTPP客户端,而且在其余组件的客户端的下层进行了加强,咱们能够先来感受一下Feign所带给咱们的性能

客户端

  • java.net.URL
  • Apache HTTP
  • OK Http
  • Ribbon
  • Java 11 http2
  • ....

标准

  • Feign
  • JAX-RS
  • JAX-RS 2
  • SOAP
  • Spring 4
  • ....

编解码

  • GSON
  • JAXB
  • Jackson
  • ....

其余

  • Hystrix
  • SLF4J
  • Mock
更多内容查看github: https://github.com/OpenFeign/...

根本应用

1. 在商品服务中编写crud

小伙伴能够本人轻易找个我的项目写,次要就是搞几个接口来调用

@RestController@RequestMapping("/goods")public class GoodsController {    @GetMapping("/get-goods")    public Goods getGoods(){        return new Goods().setName("苹果")                .setPrice(1.1)                .setNumber(2);    }    @GetMapping("/list")    public List<Goods> list(){        ArrayList<Goods> goodsList = new ArrayList<>();        Goods apple = new Goods().setName("苹果")                .setPrice(1.1)                .setNumber(2);        goodsList.add(apple);        Goods lemon = new Goods().setName("柠檬")                .setPrice(5.1)                .setNumber(3);        goodsList.add(lemon);        return goodsList;    }    @PostMapping("save")    public void save(@RequestBody Goods goods){        System.out.println(goods);    }    @DeleteMapping    public void delete(String id){        System.out.println(id);    }}

2. 建设一个demo,引入依赖

<dependencies>  <dependency>    <groupId>io.github.openfeign</groupId>    <artifactId>feign-core</artifactId>  </dependency>  <!-- 用于编解码 -->  <dependency>    <groupId>io.github.openfeign</groupId>    <artifactId>feign-jackson</artifactId>  </dependency></dependencies>

3.编写feign标准的接口

public interface GoodsApi {    @RequestLine("GET /goods/get-goods")    Goods getGoods();    @RequestLine("GET /goods/list")    List<Goods> list();    @RequestLine("POST /goods/save")    @Headers("Content-Type: application/json")    void save(Goods goods);    @RequestLine("DELETE /goods?id={id}")    @Headers("Content-Type: application/json")    void delete(@Param("id") String id);}
Feign标准是什么置信小伙伴一看例子就懂了

4.测试

public class FeignDemo {    public static void main(String[] args) {        // 构建feign接口        GoodsApi goodsApi = Feign.builder()                .encoder(new JacksonEncoder())                .decoder(new JacksonDecoder())                .target(GoodsApi.class, "http://localhost:8082");        // 调用测试        System.out.println(goodsApi.getGoods());        System.out.println(goodsApi.list());        goodsApi.save(new Goods().setName("banana"));        goodsApi.delete("1");    }}

灵光一闪

看到这里,不晓得大家有没有灵光一闪呢?

我要是把构建的代码写这样

@Beanpublic GoodsApi goodsApi(){  return Feign.builder()                .encoder(new JacksonEncoder())                .decoder(new JacksonDecoder())                .target(GoodsApi.class, "http://localhost:8082");}

而后应用时间接注入,woc,这不是爽翻天?

比拟罕用的应用形式

回顾什么是Feign章节,除了下面根本应用之外,feign还反对Spring 4的标准,以及各种http客户端(如okHttp),重试超时,日志等,我给大家介绍一个比拟罕用的形式

减少依赖

<!-- spring4标准 --><dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-spring4</artifactId>  <version>10.10.1</version></dependency><!-- ribbon客户端 --><dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-ribbon</artifactId></dependency><!-- okhttp客户端 --><dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-okhttp</artifactId></dependency><!-- 日志 --><dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-slf4j</artifactId></dependency><dependency>  <groupId>ch.qos.logback</groupId>  <artifactId>logback-classic</artifactId></dependency>

编写接口

public interface Spring4GoodsApi {    @GetMapping("/goods/get-goods")    Goods getGoods();    @GetMapping("/goods/list")    List<Goods> list();    @PostMapping(value = "/goods/save", consumes = MediaType.APPLICATION_JSON_VALUE)    void save(Goods goods);    @DeleteMapping("/goods")    void delete(@RequestParam(value = "id") String id);}

测试

public class Spring4FeignDemo {    public static void main(String[] args) {        Spring4GoodsApi goodsApi = Feign.builder()                // 应用spring4标准                .contract(new SpringContract())                // 应用jackson编解码                .encoder(new JacksonEncoder())                .decoder(new JacksonDecoder())                // okhttp客户端                .client(new OkHttpClient())                // 申请失败重试,默认最大5次                .retryer(new Retryer.Default())                // 申请超时配置                .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))                // 日志配置,将在申请前后打印日志                .logger(new Slf4jLogger())                // 日志等级配置,BASIC:只打印申请门路和响应状态码根本信息                .logLevel(Logger.Level.BASIC)                .target(Spring4GoodsApi.class, "http://localhost:8082");        System.out.println(goodsApi.getGoods());        System.out.println(goodsApi.list());        goodsApi.save(new Goods().setName("banana"));        goodsApi.delete("1");    }}

拦截器

是个http客户端就会有拦截器机制,用于在申请前对立做一些操作:比方增加申请头

feign的拦截器应用形式如下:

public class AuthFeignInterceptor implements RequestInterceptor {    @Override    public void apply(RequestTemplate template) {        System.out.println("进入拦截器");           HttpServletRequest request =                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();        template.header("token", request.getHeader("token"));    }}
实现RequestInterceptor即可

在build时退出

Feign.builder()  .requestInterceptor(new AuthFeignInterceptor())  .target(Spring4GoodsApi.class, "http://localhost:8082");

以上就是Feign的罕用形式了,学会之后,整合到Spring Cloud也是手到擒来。

整合Spring Cloud

官网文档:https://docs.spring.io/spring...

该整合样例为订单服务调用商品服务

间接三板斧走起

1. 退出依赖

在order-server中引入依赖

<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

2. 减少注解

在Application类中加上注解EnableFeignClients

@EnableFeignClients@SpringBootApplicationpublic class OrderApplication {    public static void main(String[] args) {        SpringApplication.run(GoodsApplication.class, args);    }}

任何一个配置类都能够,然而举荐在启动类上加,因为注解默认扫描该注解类门路下的所有包,而后启动类又是在最顶端的,所以这样就能够扫描到所有的包了。

当然,你也能够间接扫描某个包,毕竟个别feign接口都放在一起

3. 编写配置

见配置章节

三板斧完结,开始编写样例

4. 编写样例

@FeignClient(name = "my-goods", path = "/goods", contextId = "goods")public interface GoodsApi {    @GetMapping("/get-goods")    Goods getGoods();    /**     * get 形式传加入上需@SpringQueryMap注解     */    @GetMapping("/goods")    Goods getGoods(@SpringQueryMap Goods goods);    @GetMapping("/list")    List<Goods> list();    @PostMapping(value = "/save")    void save(Goods goods);    @DeleteMapping    void delete(String id);}

FeignClient:

name: 调用的服务名称

path: 门路前缀,该类下的所有接口都会继承该门路

contextId: 用于辨别不同的feign接口,因为一般来说一个服务不止一个feign接口,比方还有个GoodsDetailApi(商品详情), 然而他们的name属性是雷同的,都是商品服务,所有须要一个contextId来辨别不同的业务场景

其余的与罕用形式雷同

5. 测试

@RestController@RequestMapping("/order")public class OrderController {    @Resource    private GoodsApi goodsApi;    @GetMapping("/get-goods")    public Goods getGoods(){        return goodsApi.getGoods();    }}

配置

在罕用形式中,咱们构建一个feign接口的各种属性,是通过硬编码实现的,整合到spring之后,能够通过配置的形式实现了,更加的灵便。

日志

feign:  client:    config:      # 全局配置, 配置类外面的属性名叫defaultConfig, 值却是default, 留神不要搞错      default:        loggerLevel: FULL      # 独自服务配置 对应的是contextId 优先级更高      goods:        loggerLevel: BASIC
全局配置这里特地坑,不看源码基本不晓得怎么配,小伙伴肯定要留神

客户端

feign默认应用的HttpURLConnection作为客户端,小伙伴也能够替换成其余的客户端

前言:所有的客户端都为Client接口的实现类,想要晓得是否替换胜利只需在对应的实现类中打个断点

应用httpclient

引入依赖

<dependency>  <groupId>org.apache.httpcomponents</groupId>  <artifactId>httpclient</artifactId></dependency><dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-httpclient</artifactId>  <version>10.10.1</version></dependency>

这样就能够了,不必批改任何配置,这是因为当服务中蕴含ApacheHttpClient的class时,httpClient的feign主动配置类就会失效,并且比默认的HttpURLConnection主动配置类优先级更高。此时服务就将注入HttpClient作为客户端。

源码如下:

@Import 导入的程序是HttpClient, OkHttp, Default(HttpURLConnection)

该配置类的失效条件就是存在ApacheHttpClient类,而feign.httpclient.enabled配置默认不配也是失效。

应用OkHttp

引入依赖

<dependency>  <groupId>io.github.openfeign</groupId>  <artifactId>feign-okhttp</artifactId></dependency>

因为咱们在上一步引入了httpclient依赖,而httpclient的优先级比okhttp高,并且是默认失效,所以想要okhttp失效有两种形式:

  • 删除掉httpclient的依赖
  • 显示敞开httpclient

这里我应用第二种形式

feign:  # 将httpclient敞开  httpclient:    enabled: false  # 将okhttp开启  okhttp:    enabled: true

GZIP压缩

有时当申请数据过大时,进行压缩数据能够无效的进步申请性能,feign也提供这样的配置形式

留神:压缩只在非okhttp客户端时失效

feign:  httpclient:    enabled: true  # 配置压缩  compression:    request:      enabled: true      # 压缩的类型,默认就是这些      mime-types: text/xml, application/xml, application/json      # 压缩的字节最小阈值, 超过该大小才进行压缩,默认1024      min-request-size: 10
这里我应用httpclient,不写也能够,我只是为了让大家晓得我没用okhttp

容错重试

互联网利用无奈解决的问题之一:网络分区。当被服务提供者呈现这种状况,始终无奈响应状况,咱们也不可能让服务消费者始终傻傻的等着,所以咱们能够给服务配置一个超时工夫,超过肯定的工夫被调用的服务未响应,就把申请掐断。

feign的配置:

feign:  client:    config:      # 全局配置      default:        # 连贯超时工夫 单位毫秒  默认10秒        connectTimeout: 1000        # 申请超时工夫 单位毫秒  默认60秒        readTimeout: 5000

互联网利用无奈解决的问题之二:网络抖动。当经验这种状况时,咱们只能让服务进行重试。

feign配置:

feign:  client:    config:      # 全局配置      default:        # 重试 默认重试5次        retryer: feign.Retryer.Default

拦截器

拦截器与罕用形式雷同,实现RequestInterceptor接口即可

@Componentpublic class AuthFeignInterceptor implements RequestInterceptor {    @Override    public void apply(RequestTemplate template) {        System.out.println("进入拦截器");           HttpServletRequest request =                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();        template.header("token", request.getHeader("token"));    }}
加上@Component注解放到spring容器中,服务启动时会主动退出到feign的拦截器链

也能够应用配置的形式

feign:  client:    config:      # 全局配置      default:        requestInterceptors:          - com.my.micro.service.order.interceptor.AuthFeignInterceptor

小结

本篇具体介绍了一种近程办法调用形式:Feign,当初,带大家来简略的回顾一下。

什么是Feign?

一种近程办法调用客户端,整合了ribbon,httpclient, okhttp, 反对各种各样的标准,如Spring4

Feign的根本应用形式?

编辑接口,加上Feign反对的标准注解,应用Feign.Builder构建出代理类,发动调用。

如何整合SpringCloud?

引入依赖,加上@EnableFeignClients注解,依据需要减少配置

本篇内容根本涵盖了Feign的罕用形式,心愿大家有所播种,咱们下期再见~

gittee: https://gitee.com/lzj960515/m...

集体博客空间:https://zijiancode.cn/archive...

看完之后想必有所播种吧~ 想要理解更多精彩内容,欢送关注公众号:程序员阿鉴,阿鉴在公众号欢送你的到来~