之前博文的案例中,我们是通过RestTemplate来调用服务,而Feign框架则在此基础上做了一层封装,比如,可以通过注解等方式来绑定参数,或者以声明的方式来指定请求返回类型是JSON。    这种“再次封装”能给我们带来的便利有两点,第一,开发者无需像使用RestTemplate那样过多地关注HTTP调用细节,第二,在大多数场景里,某种类型的调用请求会被在多个地方被多次使用,通过Feign能方便地实现这类“重用”。

1 通过案例快速上手Feign

在FeignDemo-Server项目里,搭建基于Eureka的服务器,该项目的端口号是8888,主机名是localhost,启动后,能通过http://localhost:8888/eureka/查看注册到Eureka服务器中的诸多服务提供者或调用者的信息。

在FeignDemo-ServiceProvider项目的控制器类里,我们提供了一个sayHello方法,本项目提供服务的端口号是1111,对外提供的application name(服务名)是sayHelloServiceProvider,是向FeignDemo-Server服务器(也是Eureka服务器)的http://localhost:8888/eureka/注册服务。而提供sayHello的方法如下所示,从中,我们能看到对应的RequestMapping值。

1          @RequestMapping(value = "/hello/{username}", method = RequestMethod.GET   )

2          public String sayHello(@PathVariable("username") String username){

3          return "hello " + username;

4      }

上述Eureka服务器和客户端的代码,是复用架构师入门:搭建基本的Eureka架构(从项目里抽取)这篇文章里的代码。

这里我们将在FeignDemo-ServiceCaller项目里,演示通过Feign调用服务的方式。

第一步,在pom.xml里,引入Eureka、Ribbon和Feign的相关包,关键代码如下。其中,是通过第1行到第9行的代码引入Eureka包,通过第10行到第13行的代码引入Ribbon包,通过第14行到第17行的代码引入Feign包。

  1. 1 <dependency>
  2. 2 <groupId>org.springframework.boot</groupId>
  3. 3 <artifactId>spring-boot-starter-web</artifactId>
  4. 4 <version>1.5.4.RELEASE</version>
  5. 5 </dependency>
  6. 6 <dependency>
  7. 7 <groupId>org.springframework.cloud</groupId>
  8. 8 <artifactId>spring-cloud-starter-eureka</artifactId>
  9. 9 </dependency>
  10. 10 <dependency>
  11. 11 <groupId>org.springframework.cloud</groupId>
  12. 12 <artifactId>spring-cloud-starter-ribbon</artifactId>
  13. 13 </dependency>
  14. 14 <dependency>
  15. 15 <groupId>org.springframework.cloud</groupId>
  16. 16 <artifactId>spring-cloud-starter-feign</artifactId>
  17. 17 </dependency>

第二步,在application.yml里,通过第3行的代码,定义本项目的名字叫callHelloByFeign,通过第5行的代码,指定本项目是工作在8080端口。同时通过第9行的代码,指定本项目是向http://localhost:8888/eureka/ (也就是FeignDemo-Server)这个Eureka服务器注册。

  1. 1 spring:
  2. 2 application:
  3. 3 name: callHelloByFeign
  4. 4 server:
  5. 5 port: 8080
  6. 6 eureka:
  7. 7 client:
  8. 8 serviceUrl:
  9. 9 defaultZone: http://localhost:8888/eureka/

第三步,在启动类里,通过第1行的代码,添加支持Feign的注释,关键代码如下。这样,在启动这个Eureka客户端时,就可以引入Feign支持。

  1. 1 @EnableFeignClients
  2. 2 @EnableDiscoveryClient
  3. 3 @SpringBootApplication
  4. 4 public class ServiceCallerApp
  5. 5 {
  6. 6 public static void main( String[] args )
  7. 7 { SpringApplication.run(ServiceCallerApp.class, args); }
  8. 8 }

第四步,通过Feign封装客户端调用的细节,外部模块是通过Feign来调用客户端的,这部分的代码是在Controller.java里。

  1. 1 省略必要的packageimport的代码
  2. 2 //通过注解指定待调用的服务名
  3. 3 @FeignClient("sayHelloServiceProvider")
  4. 4 //在这个接口里,通过Feign封装客户端的调用细节
  5. 5 interface FeignClientTool
  6. 6 {
  7. 7 @RequestMapping(method = RequestMethod.GET, value = "/hello/{name}")
  8. 8 String sayHelloInClient(@PathVariable("name") String name);
  9. 9 }
  10. 10 //Controller是控制器类
  11. 11 @RestController
  12. 12 public class Controller {
  13. 13 @Autowired
  14. 14 private FeignClientTool tool;
  15. 15 //在callHello方法是,是通过Feign来调用服务
  16. 16 @RequestMapping(value = "/callHello", method = RequestMethod.GET)
  17. 17 public String callHello(){
  18. 18 return tool.sayHelloInClient("Peter");
  19. 19 }
  20. 20 }

在Controller.java这个文件,其实定义了一个接口和一个类。在第5行的FeignClientTool接口里,我们封装了Feign的调用业务,具体来说,是通过第3行的FeignClient注解,指定了该接口会调用“sayHelloServiceProvider“服务提供者的服务,而通过第8行的,则指定了调用该服务提供者中sayHelloInClient的方法。

而在第12行的Controller类里,先是在第14行里,通过Autowired注解,引入了FeignClientTool类型的tool类,随后在第17行的callHello方法里,是通过tool类的sayHelloInClient方法,调用了服务提供者的相关方法。

也就是说,在callHello方法里,我们并没有再通过RestTemplate,以输入地址和服务名的方式调用服务,而是通过封装在FeignClientTool(Feign接口)里的方法调用服务。

完成上述代码后,我们可以通过如下的步骤查看运行效果。

第一步,启动FeignDemo-Server项目,随后输入http://localhost:8888/,能看到注册到Eureka服务器里的诸多服务。

第二步,启动FeignDemo-ServiceProvider项目,随后输入http://localhost:1111/hello/Peter,能调用其中的服务,此时,能在浏览里看到“hello Peter”的输出。

第三步,启动FeignDemo-ServiceCaller项目,随后输入http://localhost:8080/callHello,同样能在浏览里看到“hello Peter”的输出。请注意,这里的调用是通过Feign完成的。

2 通过比较其它调用方式,了解Feign的封装性

在之前的代码里,我们是通过如下形式,通过RestTemplate对象来调用服务。

1      RestTemplate template = getRestTemplate();

2             String retVal = template.getForEntity("http://sayHello/hello/Eureka", String.class).getBody();

在第2行的调用中,我们需要指定url以及返回类型等信息。

之前我们还见过基于RestClient对象的调用方式,关键代码如下。

1      RestClient client = (RestClient)ClientFactory.getNamedClient("RibbonDemo");

2       HttpRequest request = HttpRequest.newBuilder().uri(new URI("/hello")).build();

3      HttpResponse response = client.executeWithLoadBalancer(request);

其中是在第1行指定发起调用的RestClient类型的对象,在第2行里指定待调用的目标地址,随后在第3行发起调用。

这两种调用方式有着如下的共同点:调用时,需要详细地知道各种调用参数,比如服务提供者的url,如果有需要通过Ribbon实现负载均衡等机制,也需要在调用时一并指定。

但事实上,这些调用方式的底层细节,应该向服务使用者屏蔽,比如在调用时,无需关注目标url等信息。这就好比某位老板要秘书去订飞机票,作为服务使用者的老板只应当关心调用的结果,比如买到的飞机票是几点开的,该去哪个航站楼登机,至于调用服务的底层细节,比如该到哪个订票网站去买,服务使用者无需知道。

说得更专业些,这叫“解耦合”,即降低服务调动者和服务提供者之间的耦合度,这样的好处是,一旦服务提供者改变了实现细节(没改变服务调用接口),那么服务调用者部分的代码无需改动。

我们再来回顾下通过Feign调用服务的方式。

1      private FeignClientTool tool; //定义Feign类

2      tool.sayHelloInClient("Peter"); //直接调用

第2行是调用服务,但其中,我们看不到任何服务提供者的细节,因为这些都在第1行引用的FeignClientTool类里封装掉了。也就是说,通过基于Feign的调用方式,开发者能真正地做到“面向业务”,而无需过多地关注发起调用的细节。

3 通过注解输出调用日志

在开发和调试阶段,我们希望能看到日志,从而能定位和排查问题。这里,我们将讲述在Feign里输出日志的方法,以便让大家能在通过Feign调用服务时,看到具体的服务信息。

这里我们将改写FeignDemo-ServiceCaller项目。

改动点1:在application.yml文件里,增加如下的代码,以开启Feign客户端的DEBUG日志模式,请注意,这里需要指定完成的路径,就像第3行那样。

1      logging:

2        level:

3          com.controller.FeignClientTool: DEBUG

改动点2:在这个项目的启动类ServiceCallerApp.java里,增加定义日志级别的代码,在第7行的feignLoggerLevel方法里,我们通过第8行的代码,指定了Feign日志级别是FULL。

  1. 1 //省略必要的pacakge和import代码
  2. 2 @EnableFeignClients
  3. 3 @EnableDiscoveryClient
  4. 4 @SpringBootApplication
  5. 5 public class ServiceCallerApp{
  6. 6 @Bean //定义日志级别是FULL
  7. 7 Level feignLoggerLevel() {
  8. 8 return Level.FULL;
  9. 9 }
  10. 10 //启动类
  11. 11 public static void main( String[] args ) {
  12. 12 SpringApplication.run(ServiceCallerApp.class, args);
  13. 13 }
  14. 14 }

完成后,依次运行Eureka服务器、服务提供者和服务调用者的启动类,随后在浏览器里输入http://localhost:8080/callHello,即能在控制台里看到DEBUG级别的日志,下面给出了部分输出。

1      2018-06-17 12:18:27.296 DEBUG 208 --- [rviceProvider-2] com.controller.FeignClientTool           : [FeignClientTool#sayHelloInClient] ---> GET http://sayHelloServiceProvider/hello/Peter?name=Peter HTTP/1.1

2      2018-06-17 12:18:27.296 DEBUG 208 --- [rviceProvider-2] com.controller.FeignClientTool           : [FeignClientTool#sayHelloInClient] ---> END HTTP (0-byte body)

从第1行的输出里,我们能看到以GET的方式向FeignClientTool类的sayHelloInClient方法发起调用,从第2行的输出里,能看到调用结束。

在上文里,我们用的是FULL级别的日志,此外,还有NONE、BASIC和HEADERS这三种,在下表里,我们将详细讲述各级别日志的输出情况。

日志输出级别

描述

NONE

不输出任何日志

BASIC

只输出请求的方法,请求的URL和相应的状态码,以及执行的时间

HEADERS

除了会输出BASIC级别的日志外,还会记录请求和响应的头信息

FULL

输出所有的和请求和响应相关的日志信息

一般情况下,在调试阶段,可以把日志级别设置成FULL,等上线后,可以把级别调整为BASIC,因为在生产环境上,过多的日志反而会降低排查和定位问题的效率。

4 压缩请求和返回,以提升访问效率

在网络传输过程中,如果我们能降低传输流量,那么即可提升处理请求的效率。尤其地,在一些日常访问量比较高的网络应用中,如果能降低处理请求(Request)和发送返回信息(Response)的时间,那么就能提升本站的吞吐量。

在Feign里,我们一般能通过如下的配置,来压缩请求和响应。

第一,可以通过在application.yml里增加如下的配置,从而压缩请求和返回信息。

1      feign:

2        compression:

3          request:

4            enabled: true

5      feign:

6         compression:

7           response:

8            enabled: true

其中,前4行是压缩请求,而后4行是压缩返回。

第二,可以通过如下的代码,设置哪类请求(或返回)将被压缩,这里我们在第4行里,指定了两类格式的请求将被压缩。

1      feign:

2        compression:

3          request:

4            mime-types: text/xml,application/xml

第三,可以通过如下的代码,指定待压缩请求的最小值,这里是2048,也就是说,超过这个值的request才会被压缩。

1      feign:

2        compression:

3          request:

4            min-request-size:2048

    本文谢绝转载。其它和Spring Cloud相关的博文如下:    

 

Spring Cloud微服务系列文,服务调用框架Feign的更多相关文章

  1. 2018年分享的Spring Cloud 2.x系列文章

    还有几个小时2018年就要过去了,盘点一下小编从做做公众号以来发送了273篇文章,其中包含原创文章90篇,虽然原创的有点少,但是2019年小编将一如既往给大家分享跟多的干货,分享工作中的经验,让大家在 ...

  2. 【多线程】java多线程Completablefuture 详解【在spring cloud微服务之间调用,防止接口超时的应用】【未完成】

    参考地址:https://www.jianshu.com/p/6f3ee90ab7d3 示例: public static void main(String[] args) throws Interr ...

  3. Spring Cloud 微服务六:调用链跟踪Spring cloud sleuth +zipkin

    前言:随着微服务系统的增加,服务之间的调用关系变得会非常复杂,这给运维以及排查问题带来了很大的麻烦,这时服务调用监控就显得非常重要了.spring cloud sleuth实现了对分布式服务的监控解决 ...

  4. Dubbo和Spring Cloud微服务架构比较

    Dubbo 出生于阿里系,是阿里巴巴服务化治理的核心框架,并被广泛应用于中国各互联网公司:只需要通过 Spring 配置的方式即可完成服务化,对于应用无入侵,设计的目的还是服务于自身的业务为主. 微服 ...

  5. 在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用

    本文为阿里云容器服务Spring Cloud应用开发系列文章的第一篇. 一.在阿里云容器服务上开发Spring Cloud微服务应用(本文) 二.部署Spring Cloud应用示例 三.服务发现 四 ...

  6. Dubbo 和 Spring Cloud微服务架构 比较及相关差异

    你真的了解微服务架构吗?听听八年阿里架构师怎样讲述Dubbo和Spring Cloud微服务架构. 微服务架构是互联网很热门的话题,是互联网技术发展的必然结果.它提倡将单一应用程序划分成一组小的服务, ...

  7. 【spring colud】spring cloud微服务项目搭建【spring boot2.0】

    spring cloud微服务项目搭建 =================================== 示例版本: 1.spring boot 2.0版本 2.开发工具 IntellJ IDE ...

  8. 7个点说清楚spring cloud微服务架构

    前言 spring cloud作为当下主流的微服务框架,让我们实现微服务架构简单快捷,spring cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A ...

  9. spring cloud微服务实践七

    在spring cloud 2.x以后,由于zuul一直停滞在1.x版本,所以spring官方就自己开发了一个项目 Spring Cloud Gateway.作为spring cloud微服务的网关组 ...

随机推荐

  1. election.go

    package)) ].GetResponseRange().Kvs[] ) ].Value),]         }         wch := client.Watch(cctx, string ...

  2. grpc.go

    package,,), etcd.WithPrefix(), etcd.WithPrevKV()}     gw.wch = gw.c.Watch(gw.ctx, gw.target, opts... ...

  3. 【源码分析】Canal之Binlog的寻找过程

    binlog的寻找过程可能的场景如下: instance第一次启动 发生数据库主备切换 canal server HA情况下的切换 所以这个过程是能够保证binlog不丢失的关键点. 本文从源码的角度 ...

  4. 使用ASIFormDataRequest完成用户的登录操作

    ASIFormDataRequest是用于向表单post数据或get数据,可以用于用户向服务器端发送请求完成登录注册,上传下载数据的操作. 之前写过一篇文章是介绍使用ios内置的功能完成登录操作(NS ...

  5. Spring mvc 上传进度条实现

    以下的仅仅是学习而已,记录以下笔记 1 springmvc 进度条,要实现ProgressListener接口,实现方法update(long readLength, long contextLeng ...

  6. Python + Appium 【已解决】driver(session)在多个class之间复用,执行完一个类的用例,再次执行下个类的用例时不需要初始化

    实现效果:打开App进行自动化测试,只需打开APP一次,按先后顺序执行n个py文件中的相应操作,实现自动化测试. 示例:如截图示例,一个App,根据此APP内不同的模块,写成了不同的py文件, 预期结 ...

  7. MIP 脚本域名地址变更公告

    尊敬的 MIP 开发者: MIP 团队为了解决 MIP-Cache 页面下 cookie 相互覆盖问题,增强站点品牌露出,在 2017 年 8 月将 MIP 的脚本域名和 MIP-Cache 页面域名 ...

  8. 434个H5游戏源码

    各种类型HTML5游戏,界面和JS均可供项目参考 下面是下载地址

  9. linux服务器 jboss-7安装

    jBoss简介 JBoss是一个运行EJB的J2EE应用服务器.它是开放源代码的项目,遵循最新的J2EE规范.从JBoss项目开始至今,它已经从一个EJB容器发展成为一个基于的J2EE的一个web 操 ...

  10. ASP.NET Core 实战:构建带有版本控制的 API 接口

    一.前言 在上一篇的文章中,主要是搭建了我们的开发环境,同时创建了我们的项目模板框架.在整个前后端分离的项目中,后端的 API 接口至关重要,它是前端与后端之间进行沟通的媒介,如何构建一个 “好用” ...