SpringCloud:Hystrix服务熔断实现

郎家岭伯爵 2022年06月21日 389次浏览

背景

前面我们实现了微服务的集群化,以及微服务之间的调用、负载均衡的实现,我们通过RibbonRestTemplatefeign接口类和注解的两种方式分别进行了实现。Ribbon实现feign实现

接下来我们将介绍服务熔断服务降级

由于分布式体系结构中微服务的依赖关系十分复杂,服务调用失败的情况难以避免。因此我们需要通过一些服务熔断与服务降级的操作来规避这些问题。

服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和C又调用其它的微服务,这就是扇出。如果扇出链路上的某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进行引起系统崩溃,这就是雪崩效应

对于高并发的应用来说,单一的后端依赖可能会导致所有的服务器的资源都在几秒中饱和。而且这些微服务还可能引发其它服务之间的延迟增加、备份队列、线程和其它资源紧张,造成整个系统发生更多的级联故障。

这就需要对故障和延迟进行隔离和管理,避免单个服务的故障影响到整个系统。

Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库。

在分布式系统里,依赖不可避免的会调用失败,比如超时、异常等,Hystrix可以保证在某个服务异常的情况下,不会导致整个系统出问题,避免级联故障,以提高分布式系统的弹性。

断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似保险丝熔断),向调用方返回一个预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hystrix的功能:

  • 服务熔断
  • 服务降级
  • 服务限流
  • 接近实时的服务监控

服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制。

当扇出链路的某个微服务不可用或者响应时间过长,Hystrix会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。

SpringCloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况。当失败的调用达到阈值后,例如5秒内20次调用失败,就会启动熔断机制。

Hystrix实现熔断机制的注解是@HystrixCommand

实现

此模块与springcloud-provider-dept-8001模块的功能是基本一致的,因此核心代码用一样的即可,后续再视需要完善Hystrix部分的代码。

本案例的核心代码直接从Windows文件管理器中的springcloud-provider-dept-8001src目录中复制过来:

创建模块

编辑pom.xml

引入以下依赖:

<dependencies>
        <!-- 需要拿到实体类,因此需要配置api Module。这个类是我们刚才自己写的Module! -->
        <dependency>
            <groupId>com.langjialing</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>

        <!-- 此依赖用于完善监控信息 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>
</dependencies>

修改application.yaml

修改DeptController

package com.langjialing.springcloud.controller;

import com.langjialing.springcloud.pojo.Dept;
import com.langjialing.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.experimental.Accessors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


//提供Restful服务的控制器
@RestController
public class DeptController {

    @Autowired
    private 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) {

        Dept dept = new Dept();
        dept.setDeptno(id);
        dept.setDname("该ID:" + id + "没有对应的信息,null--@HystrixCommand");
        dept.setDb_source("no this database in MySQL");

        return dept;
    }

}

修改主启动类

package com.langjialing.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;

@SpringBootApplication
@EnableEurekaClient // 开启注解,在服务启动后会自动注入到Eureka中
@EnableDiscoveryClient  // 服务发现
@EnableHystrix // 开启Hystrix
public class DeptProviderHystrix_8001 {
    public static void main(String[] args){
        SpringApplication.run(DeptProviderHystrix_8001.class, args);
    }
}

测试功能

注意:

  • 这里除了启动Eureka注册中心之外,需要启动的是springcloud-provider-dept-hystrix-8001模块;
  • 为了更好地观察到效果,其它的服务提供者模块应该处于关机状态。

总结

本文我们使用Hystrix实现了服务熔断,接下来我们继续介绍Hystrix实现服务降级。