之前博文的案例中,我们是通过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    <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-web</artifactId>
4 <version>1.5.4.RELEASE</version>
5 </dependency>
6 <dependency>
7 <groupId>org.springframework.cloud</groupId>
8 <artifactId>spring-cloud-starter-eureka</artifactId>
9 </dependency>
10 <dependency>
11 <groupId>org.springframework.cloud</groupId>
12 <artifactId>spring-cloud-starter-ribbon</artifactId>
13 </dependency>
14 <dependency>
15 <groupId>org.springframework.cloud</groupId>
16 <artifactId>spring-cloud-starter-feign</artifactId>
17 </dependency>

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

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

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

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

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

1    省略必要的package和import的代码
2 //通过注解指定待调用的服务名
3 @FeignClient("sayHelloServiceProvider")
4 //在这个接口里,通过Feign封装客户端的调用细节
5 interface FeignClientTool
6 {
7 @RequestMapping(method = RequestMethod.GET, value = "/hello/{name}")
8 String sayHelloInClient(@PathVariable("name") String name);
9 }
10 //Controller是控制器类
11 @RestController
12 public class Controller {
13 @Autowired
14 private FeignClientTool tool;
15 //在callHello方法是,是通过Feign来调用服务
16 @RequestMapping(value = "/callHello", method = RequestMethod.GET)
17 public String callHello(){
18 return tool.sayHelloInClient("Peter");
19 }
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    //省略必要的pacakge和import代码
2 @EnableFeignClients
3 @EnableDiscoveryClient
4 @SpringBootApplication
5 public class ServiceCallerApp{
6 @Bean //定义日志级别是FULL
7 Level feignLoggerLevel() {
8 return Level.FULL;
9 }
10 //启动类
11 public static void main( String[] args ) {
12 SpringApplication.run(ServiceCallerApp.class, args);
13 }
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. spring的依赖注入是什么意思

    最近学习spring框架,对依赖注入有些模糊,遂上网翻阅资料,做了下列总结,原博客为CSDN 南夏的 spring的依赖注入是什么意思,侵删! Spring 能有效地组织J2EE应用各层的对象.不管是 ...

  2. Connection reset by peer的常见原因

    1,如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer). Socket默认连接60秒 ...

  3. TestNG深入理解

    以下内容引自: http://blog.csdn.net/wanglha/article/details/42004695 TestNG深入理解 转载 2014年12月18日 13:56:11 参考文 ...

  4. 不吹不擂,你想要的Python面试都在这里了【315+道题】

    写在前面 近日恰逢老男孩全栈8期毕业季,课程后期大家“期待+苦逼”的时刻莫过于每天早上内容回顾和面试题问答部分[临近毕业每天课前用40-60分钟对之前内容回顾.提问和补充,专挑班里不爱说话就的同学回答 ...

  5. B20J_2733_[HNOI2012]永无乡_权值线段树合并

    B20J_2733_[HNOI2012]永无乡_权值线段树合并 Description:n座岛,编号从1到n,每座岛都有自己的独一无二的重要度,按照重要度可以将这n座岛排名,名次用1到 n来表示.某些 ...

  6. 【爆料】-《悉尼大学毕业证书》USYD一模一样原件

    ☞悉尼大学毕业证书[微/Q:2544033233◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...

  7. 如何解析C语言的声明

    一个声明:int *p[] 分为四部分: (1)p (2)p右面的符号(可以什么都没有) (3)p左面的符号(可以什么都没有) (4)最左面的类型说明符 解读一个声明先从p开始,然后的顺序是:右左右左 ...

  8. python 格式化输出日志记录

    # 格式化打印提示输出示例已logging模块为例. service_name = "Booking" logger.error('%s service is down!' % s ...

  9. Flask导入静态文件问题

    然而如果使用flask开发web,并且需要在本地导入已经写好的css js 文件或者image一系列,这些文件是静态文件,需要另外建一个文件夹static;并且在html文件修改导入方法,exampl ...

  10. Bootstrap优秀模板-ColorAdmin.4.3.0

    老牌的Bootstrap商业模板,提供HTML.AngularJS.AngularJS5.Vue.React多种环境,风格覆盖Flat.Material.Apple.Transparent甚至Face ...