Spring Cloud Feign实例讲解学习

前面博文搭建了一个Eureka+Ribbon+Hystrix的框架,虽然可以基本满足服务之间的调用,但是代码看起来实在丑陋,每次客户端都要写一个restTemplate,为了让调用更美观,可读性更强,现在我们开始学习使用Feign。

Feign包含了Ribbon和Hystrix,这个在实战中才慢慢体会到它的意义,所谓的包含并不是Feign的jar包包含有Ribbon和Hystrix的jar包这种物理上的包含,而是Feign的功能包含了其他两者的功能这种逻辑上的包含。简言之:Feign能干Ribbon和Hystrix的事情,但是要用Ribbon和Hystrix自带的注解必须要引入相应的jar包才可以。

案例一:

Eureka注册中心:https://github.com/yejingtao/forblog/tree/master/demo-eureka-register

服务提供方:https://github.com/yejingtao/forblog/tree/master/demo-feign-freeservice

服务调用方:https://github.com/yejingtao/forblog/tree/master/demo-feign-freeconsumer

服务提供方就是个简单的EurekaClient端+web应用,提供以下方法

@RestController
@RequestMapping("/feign-service")
public class HelloServiceContorller {
  private Logger logger = LoggerFactory.getLogger(this.getClass());
  private void sleep(String methodName) {
    int sleepMinTime = new Random().nextInt(3000);
    logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime);
    try {
      Thread.sleep(sleepMinTime);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  } 

  @RequestMapping(value="/serviceGet",method=RequestMethod.GET)
  public String helloService(@RequestParam String name) {
    sleep("get");
    return "HelloServiceImpl name :"+name;
  } 

  @RequestMapping(value="/serviceHead", method=RequestMethod.HEAD)
  public String helloService(@RequestHeader String name,
      @RequestHeader String password) {
    sleep("header");
    return "helloServiceHead name :"+name +" password:"+password;
  } 

  @RequestMapping(value="/servicePost", method=RequestMethod.POST)
  public String helloService(@RequestBody UserDemo userDemo) {
    sleep("post");
    return userDemo.toString();
  }
} 

需要注意的以下注解不可以省略。

@RequestParam:Annotation which indicates that amethod parameter should be bound to a web request parameter

@RequestBody:Annotation indicating a methodparameter should be bound to the body of the web request.

@RequestHeader:Annotation which indicates that amethod parameter should be bound to a web request header.

如果缺少了以上注解,服务运行起来以后虽然不会报错,但是获取不到入参。

服务调用方项目:

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

这里只依赖了Feign,没有依赖Ribbon和Hystrix。

application.yml:

server:
 port: 9051 

spring:
 application:
  name: demo-feign-freeconsumer 

eureka:
 client:
  serviceUrl:
   defaultZone: http://peer1:1111/eureka/,http://peer2:1112/eureka/
feign:
 hystrix:
  enabled: true 

#Ribbon 超时时间设置
#ribbon:
# ConnectTimeout: 500
# ReadTimeout: 3000

hystrix这个配置坑了我好久我用的Spring Cloud是Dalston版本SR1,比网上其他材料的版本要新,因为在新版本中Feign对Hystrix的支持默认是关闭的,所以要通过配置手动打开feign.hystrix.enabled=true,这样服务降级等功能才有效果。

Application启动程序

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class DemoFeignApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoFeignApplication.class, args);
  }
}

注意这里还有个坑,我这里用的是@SpringBootApplication+@EnableEurekaClient,而不是用的@SpringCloudApplication,因为后者包含了@EnableCircuitBreaker,而@EnableCircuitBreaker又是属于Hystrix包里的内容,我的pom里并没有引入Hystrix。所以这一点Spring Cloud做的还是有不足的地方,直接用@SpringCloudApplication编译不会报错,但是启动不了。当然这里的主角还是@EnableFeignClients这个注解。

核心客户端代码

@FeignClient(name="demo-feign-freeservice",fallback=DemoFeignFallback.class)
public interface DemoFeignService{ 

  @RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET)
  String helloService(@RequestParam("name") String name); 

  @RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD)
  String helloService(@RequestHeader("name") String name,
      @RequestHeader("password") String password); 

  @RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST)
  String helloService(@RequestBody UserDemo userDemo);
} 

@FeignClient注解定义了该接口是一个Feign客户端,name指定了注册到Eureka上的服务名,fallback是服务降级后的接口实现类。

@RequestMapping里指定了请求的相对url和http请求方式,与服务端一一对应。入参里的@RequestParam、

@RequestBody、@RequestHeader注解比起服务端多了value属性,这里不能省略,需要显式的告知Feign客户端参数要如何对应。

降级服务代码:

@Component
public class DemoFeignFallback implements DemoFeignService{
  @Override
  public String helloService(String name) {
    return "get error";
  } 

  @Override
  public String helloService(String name,String password) {
    return "head error";
  } 

  @Override
  public String helloService(UserDemo userDemo) {
    return "post error";
  }
} 

发现这里的入参里我故意去掉了@RequestParam、@RequestBody、@RequestHeader注解,因为这几个注解本质上的意义就在于Feign在做微服务调用的时候对http传递参数用的,但服务降级根本不会做http请求了,所以此处可以省略。

Controller代码:

@RestController
public class DemoFeignController {
  @Autowired
  private DemoFeignService demoFeignService;
  @RequestMapping(value="/test", method=RequestMethod.GET)
  public String demoServiceTest() {
    StringBuffer sb = new StringBuffer();
    sb.append(demoFeignService.helloService("yuanyuan"));
    sb.append("\n");
    sb.append(demoFeignService.helloService("yjt","xixihaha"));
    sb.append("\n");
    sb.append(demoFeignService.helloService(new UserDemo("yejingtao","123456")));
    return sb.toString();
  }
} 

我们来看效果:

我们服务都没超时,3个方法全部正常,但是head请求没有拿到返回值,这个是因为head方式http请求的特性决定的,head不返回response的body体,一般用来做连通性测试来用。

再看一组:

运气不好head和post请求方法处理时间超过了2000ms,服务降级,实现被fallback处理类取代。

在案例一中我们总有种感觉,服务提供方和服务调用方存在重复的代码,是否可以进行优化?请看案例二。

案例二:

Eureka注册中心:https://github.com/yejingtao/forblog/tree/master/demo-eureka-register

接口API:https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceapi

服务提供方:https://github.com/yejingtao/forblog/tree/master/demo-feign-serviceimpl

服务调用方:https://github.com/yejingtao/forblog/tree/master/demo-feign-apiconsumer

案例二最大的变动是将服务能力单独写到一个API的project中,调用方和提供方pom都依赖这个API。

API:

public interface HelloService {
  @RequestMapping(value="/feign-service/serviceGet",method=RequestMethod.GET)
  String helloService(@RequestParam("name") String name); 

  @RequestMapping(value="/feign-service/serviceHead", method=RequestMethod.HEAD)
  String helloService(@RequestHeader("name") String name,
      @RequestHeader("password") String password); 

  @RequestMapping(value="/feign-service/servicePost", method=RequestMethod.POST)
  String helloService(@RequestBody UserDemo userDemo);
}

服务提供方:

@RestController
public class HelloServiceContorller implements HelloService{
  private Logger logger = LoggerFactory.getLogger(this.getClass());
  private void sleep(String methodName) {
    int sleepMinTime = new Random().nextInt(3000);
    logger.info("helloService "+methodName+" sleepMinTime: "+sleepMinTime);
    try {
      Thread.sleep(sleepMinTime);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  } 

  @Override
  public String helloService(@RequestParam("name") String name) {
    sleep("get");
    return "HelloServiceImpl name :"+name;
  } 

  @Override
  public String helloService(@RequestHeader("name") String name,
      @RequestHeader("password") String password) {
    sleep("header");
    return "helloServiceHead name :"+name +" password:"+password;
  } 

  @Override
  public String helloService(@RequestBody UserDemo userDemo) {
    sleep("post");
    return userDemo.toString();
  }
}

服务调用方:

@FeignClient(name="demo-feign-serviceimpl", fallback=FeignServiceFallback.class)
public interface FeignService extends HelloService{
} 

其它代码基本不变,效果也一样。

两种风格各有优缺点:freestyle的更自由,服务端新增方法不会影响客户端代码,缺点是服务能力不同步服务能力的变动会引起异常;API格式服务端客户端服务能力同步,但是接口的变动需要修改两边的代码,需要构建的时候就要考虑清楚。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • Spring Cloud Feign简单使用详解
  • 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法(推荐)
  • 详解spring cloud Feign使用中遇到的问题总结
  • spring cloud feign不支持@RequestBody+ RequestMethod.GET报错的解决方法
  • 详解springcloud Feign的Hystrix支持
  • SpringCloud之Feign示例详解
  • 使用Spring Cloud Feign上传文件的示例
  • spring cloud 之 Feign 使用HTTP请求远程服务的实现方法
  • Spring Cloud中关于Feign的常见问题总结
  • 解决Spring Cloud中Feign/Ribbon第一次请求失败的方法
(0)

相关推荐

  • 解决Spring Cloud中Feign/Ribbon第一次请求失败的方法

    前言 在Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码.而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了.知道原因后,我们来总结一下解决放你. 解决方案有三种,以feign为例. 方法一 hystrix.command.default.execution.

  • 详解springcloud Feign的Hystrix支持

    本文介绍了springcloud Feign的Hystrix支持,分享给大家,具体如下: 一.Feign client中加入Hystrix的fallback @FeignClient(name="springboot-h2", fallback=HystrixClientFallback.class) //在fallback属性中指定断路器的fallback public interface UserFeignClient { // @GetMapping("/user/{i

  • 使用Spring Cloud Feign上传文件的示例

    最近经常有人问Spring Cloud Feign如何上传文件.有团队的新成员,也有其他公司的兄弟.本文简单做个总结-- 早期的Spring Cloud中,Feign本身是没有上传文件的能力的(1年之前),要想实现这一点,需要自己去编写Encoder 去实现上传.现在我们幸福了很多.因为Feign官方提供了子项目feign-form ,其中实现了上传所需的 Encoder . 注:笔者测试的版本是Edgware.RELEASE.Camden.Dalston同样适应本文所述. 加依赖 <depen

  • SpringCloud之Feign示例详解

    Feign简介 Feign 是一个声明web服务客户端,这便得编写web服务客户端更容易,使用Feign 创建一个接口并对它进行注解,它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign. 声明式REST

  • 详解spring cloud Feign使用中遇到的问题总结

    本文介绍了spring cloud Feign使用中遇到的问题总结,分享给大家,具体如下: 问题一: 在前面的示例中,我们讲过 @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) @GetMapping("/user/{id}") 这两个注解的效果是等价的,但是在Feign使用中,只能用上面的那种方式,不能直接用@GetMapping,下面我们将前面的那个示例中,改成@GetMappin

  • Spring Cloud Feign简单使用详解

    概述 在Spring Cloud EureKa Ribbon 服务注册-发现-调用一文中简单的介绍了在Spring Cloud中如何使用EureKa和Ribbon.文章中使用了RestTemplate去访问其他的restful微服务接口.其实在Spring Cloud还可以使用Feign来访问其他的restful微服务接口.使用起来更加的简洁明了. 集成Feign 修改一下Spring Cloud EureKa Ribbon 服务注册-发现-调用中order service的pom配置,把Feg

  • Spring Cloud中关于Feign的常见问题总结

    一.FeignClient接口,不能使用@GettingMapping 之类的组合注解 代码示例: @FeignClient("microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable(&quo

  • spring cloud feign不支持@RequestBody+ RequestMethod.GET报错的解决方法

    1.问题梳理: 异常:org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported 很明显是最终feign执行http请求时把这个方法认定为POST,但feign client中又定义了RequestMethod.GET 或 @GetMapping,冲突导致报错 那么为什么feign会认为这个方法是post呢? 源码追踪: 1.我们从feignClient注解

  • spring cloud 之 Feign 使用HTTP请求远程服务的实现方法

    一.Feign 简介 在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Apache的Http Client.Netty的异步HTTP Client, Spring的RestTemplate.但是,用起来最方便.最优雅的还是要属Feign了. Feign是一种声明式.模板化的HTTP客户端.在Spring Cloud中使用Feign, 我们可以做到使用

  • 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法(推荐)

    在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Apache的Http Client.Netty的异步HTTP Client, Spring的RestTemplate.但是,用起来最方便.最优雅的还是要属Feign了. Feign简介 Feign是一种声明式.模板化的HTTP客户端.在Spring Cloud中使用Feign, 我们可以做到使用HTT

随机推荐