前言:

本文内容:Feign:使用接口方式调用服务、Hystrix:服务熔断、服务降级

推荐免费SpringCloud基础教程视频:【狂神说Java】SpringCloud最新教程IDEA版_哔哩哔哩_bilibili

Feign:使用接口方式调用服务

简介

Feign是声明式的web service客户端,它让微服务的调用变得更简单了,类似Controller调用service。SpringCloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。

只需要创建一个接口,然后添加注解即可!

Feign的作用

  • Feign意在使编写Java Http客户端变得更容易
  • 使用Ribbon+RestTemplate时,利用RestTemplate对Http请求封装处理,形成了一套模板化的调用方法,在Feign的实现下,我们只需要创建一个接口并使用注解的方式来配置它,(类似于以前Dao接口上标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可)即可完成对服务提供方的接口绑定。
  • Feign集成了Ribbon

简单体验

  1. 复制springcloud-consumer-dept-80Module为新的springcloud-consumer-dept-feign

  2. pom下添加依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
    </dependency>
  3. 修改主启动类名字为FeignDeptConsumer_80,并修改代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.jokerdig.springcloud;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.ComponentScans;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:28
    **/
    @SpringBootApplication
    @EnableEurekaClient
    @EnableFeignClients(basePackages = {"com.jokerdig.springcloud"})
    @ComponentScans({@ComponentScan("com.jokerdig.springcloud.service")})
    public class FeignDeptConsumer_80 {
    public static void main(String[] args) {
    SpringApplication.run(FeignDeptConsumer_80.class,args);
    }
    }
  4. 删除myrule包及包下所有类

  5. 打开springcloud-api,新建com.jokerdig.springcloud.service

  6. pom下添加依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    <version>1.4.6.RELEASE</version>
    </dependency>
  7. service包下新建DeptClientService接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.jokerdig.springcloud.service;

    import com.jokerdig.springcloud.pojo.Dept;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;

    import java.util.List;

    /**
    * @author Joker大雄
    * @data 2022/8/8 - 11:07
    **/
    @Component
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT")
    public interface DeptClientService {

    @GetMapping("/dept/get/{id}")
    public Dept queryById(@PathVariable("id") Long id);
    @GetMapping("/dept/list")
    public List<Dept> queryAll();
    @PostMapping("/dept/add")
    public boolean addDept();
    }
  8. 修改springcloud-consumer-dept-feign下的DeptConsumerController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package com.jokerdig.springcloud.controller;

    import com.jokerdig.springcloud.pojo.Dept;
    import com.jokerdig.springcloud.service.DeptClientService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import java.util.List;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:10
    **/
    @RestController
    public class DeptConsumerController {
    // 消费者不会有service
    // RestTemplate 供我们调用
    // {url,实体:Map,Class<T> responseType}
    @Autowired
    private DeptClientService deptClientService = null;;

    // Feign实现

    // 添加
    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept){
    return this.deptClientService.addDept(dept);
    }

    // 通过id查询
    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
    return this.deptClientService.queryById(id);
    }

    // 查询
    @RequestMapping("/consumer/dept/list")
    public List<Dept> queryAll(){
    return this.deptClientService.queryAll();
    }
    }
  9. 启动7001,8001,8002,fegin,运行测试

    http://localhost/consumer/dept/list

    1
    [{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]

Hystrix:服务熔断

什么是Hystrix

提到Hystrix就不得不提雪崩效应,什么是“雪崩效应”?

多个微服务之间调用的时候,假设A调用B和C,B和C又在调用其他的微服务,这种情况被称为扇出,这个时候有一个微服务出现问题,或这长时间未响应,对A微服务的占用的越来越多的系统资源,这就是所谓的“雪崩效应“

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,是对雪崩效应的一种微服务链路的保护机制。避免连级故障,提高分布式系统弹性。

断路器本身是一种开关装置,但某个服务单元发生故障,通过断路器的故障监控,向调用方返回一个服务预期且可处理的备选响应(FallBack),而不是长时间等待或抛出异常,避免服务方线程被长时间不必要的占用,避免发生雪崩问题。

Hystrix主要功能

  • 服务降级

  • 服务熔断

  • 服务限流

  • 实时监控

Netflix/Hystrix

服务熔断

服务熔断是应对雪崩效应的一种微服务链路保护机制,注解:@HystrixCommand

当调用链路的某个微服务不可用或者响应时间太长时,会进行服务熔断,不再有该节点微服务的调用,快速返回错误的响应信息。在Spring Cloud框架里,熔断机制通过Hystrix实现。 Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。

简单测试

  1. 复制项目springcloud-provider-dept-8001springcloud-provider-dept-hystrix-8001

  2. 修改主启动类名为HystrixDeptProvider_8001,并启动Hystrix

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package com.jokerdig.springcloud;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.hystrix.EnableHystrix;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 11:34
    **/
    // 启动类
    @SpringBootApplication
    @EnableEurekaClient //开启eureka
    @EnableDiscoveryClient // 服务发现
    @EnableHystrix
    public class HystrixDeptProvider_8001 {
    public static void main(String[] args) {
    SpringApplication.run(HystrixDeptProvider_8001.class,args);
    }
    }
  3. pom文件中引入依赖

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  4. 修改controller包下的DeptController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    package com.jokerdig.springcloud.controller;

    import com.jokerdig.springcloud.pojo.Dept;
    import com.jokerdig.springcloud.service.DeptService;
    import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.*;

    import java.util.List;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 11:29
    **/
    // 提供Restful服务
    @RestController
    public class DeptController {
    @Autowired
    DeptService deptService;

    @HystrixCommand(fallbackMethod = "hystrixGet")
    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id){
    Dept dept = deptService.queryById(id);
    if(dept==null){
    throw new RuntimeException("id=>"+id+",该用户不存在");
    }
    return dept;
    }
    // 备选方案
    public Dept hystrixGet(@PathVariable("id")Long id){
    return new Dept()
    .setDeptno(id)
    .setDname("id=>"+id+",该用户不存在")
    .setDb_source("no this database in MySQL");
    }
    }
  5. 运行7001,hystrix进行测试

    http://localhost:8001/dept/get/1

    1
    {"deptno":1,"dname":"开发部","db_source":"db01"}

    http://localhost:8001/dept/get/9

    1
    {"deptno":9,"dname":"id=>9,该用户不存在","db_source":"no this database in MySQL"}

Hystrix:服务降级

什么是服务降级

服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的备选响应(FallBack)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。

服务熔断和降级

相同点:

  • 目标一致 都是从可用性和可靠性出发,为了防止系统崩溃;
  • 用户体验类似 最终都让用户体验到的是某些功能暂时不可用;

不同点:

  • 触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;

简单测试

  1. springcloud-apiservice包下新建 DeptClientServiceFallbackFactory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package com.jokerdig.springcloud.service;

    import com.jokerdig.springcloud.pojo.Dept;
    import feign.hystrix.FallbackFactory;
    import org.springframework.stereotype.Component;

    import java.util.List;

    /**
    * @author Joker大雄
    * @data 2022/8/8 - 12:42
    **/
    // 降级
    @Component
    public class DeptClientServiceFallbackFactory implements FallbackFactory {


    @Override
    public DeptClientService create(Throwable cause) {
    return new DeptClientService() {
    @Override
    public Dept queryById(Long id) {
    return new Dept()
    .setDeptno(id)
    .setDname("id=>"+id+",服务端提供了降级信息,该服务暂时无法访问")
    .setDb_source("暂时无法获取数据库信息");
    }

    @Override
    public List<Dept> queryAll() {
    return null;
    }

    @Override
    public boolean addDept(Dept dept) {
    return false;
    }
    };
    }
    }
  2. 修改service包下的DeptClientService

    1
    @FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
  3. 配置springcloud-consumer-dept-feign下的application

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    server:
    port: 80

    # Eureka
    eureka:
    client:
    register-with-eureka: false
    service-url:
    defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002,http://eureka7003.com:7003
    # 开启服务降级
    feign:
    hystrix:
    enabled: true
  4. 启动7001,8001,feign,进行测试

    http://localhost/consumer/dept/get/1

    1
    {"deptno":1,"dname":"开发部","db_source":"db01"}

    关闭8001,访问:http://localhost/consumer/dept/get/1

    1
    {"deptno":1,"dname":"id=>1,服务端提供了降级信息,该服务暂时无法访问","db_source":"暂时无法获取数据库信息"}