SpringCloud的熔断器之Hystrix的理论与实操

本篇博客主要讲解Hystrix的理论以及实现。

首先先来讲熔断器Hystrix的理论以及两种实现方法。

熔断器理论

为什么使用熔断器

分布式系统面临的问题:雪崩效应

在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因"服务提供者"的不可用导致"服务消费者"的不可用,并将不可用逐渐放大的过程。

如下图所示:多个服务调用,A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。微服务 A 调用微服务 B ,微服务 B 调用微服务 C和D,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务 A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,也就是A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。

在这里插入图片描述
对于流量高的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更加糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟隔离和管理,以便单个依赖关系的失败不能取消整个应用程序或系统。

熔断器(CircuitBreaker)

熔断器的原理很简单,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。

熔断器开关相互转换的逻辑如下图:

在这里插入图片描述
熔断器是保护服务高可用的最后一道防线。

什么是Hystrix

Hystrix 是一个用于处理分布式系统的 延迟 和 容错 的开源库,在分布式系统里,许多依赖会不可避免的调用失败,比如超时,异常, Hystrix 能够保证在一个依赖出现问题的情况下,不会导致整体服务的失败,避免级联故障,以提高分布式系统的弹性。

Hystrix特性

1.断路器机制
(是应对雪崩效应的一种微服务链路的保护机制)

断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求,也就是不长时间的等待或者抛出异常影响系统吞吐量, 它会向调用方返回一个符合预期的、可处理的备选响应。并且断路器有自我检测并恢复的能力,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

2.Fallback

Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.

3.资源隔离

在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源.

Hystrix的两种熔断实现方式

本次的实现方法都是在之前的博客“Spring Cloud的Eureka实现服务发现注册和Feign实现服务调用:https://blog.csdn.net/weixin_42236165/article/details/92842773 的项目上改动实现的。

第一种
Feign Hystrix

因为熔断只是作用在服务调用这一端,因此我们根据之前“Spring Cloud的Eureka实现服务发现注册和Feign实现服务调用:https://blog.csdn.net/weixin_42236165/article/details/92842773
的示例代码只需要改动springcloud-eurekastudent项目相关代码就可以。因为,Feign中已经依赖了Hystrix所以在maven配置上不用做任何改动。

1.配置文件

在application.properties文件中添加开启熔断器代码

feign.hystrix.enabled=true

application.properties代码如下:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/studentdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

spring.application.name=eureka-student
server.port=8091
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/

#开启熔断器
feign.hystrix.enabled=true

2.创建回调类
创建GradeClientImpl类继承GradeClient接口实现回调的方法

package com.szh.eurekastudent.client.impl;

import com.szh.eurekastudent.client.GradeClient;
import com.szh.eurekastudent.pojo.Grade;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class GradeClientImpl implements GradeClient {

    @Override
    public List<Grade> findStudents() {
        //打印日志
        //使用消息队列发送消息
        //发送短信
        List<Grade> list = new ArrayList<>();
        Grade grade = new Grade();
        grade.setGradeId(0);
        grade.setGradeName("出错了,执行熔断器");
        list.add(grade);
        return list;
    }
}

3、添加fallback属性
在GradeClient接口添加指定fallback类,在服务熔断的时候返回fallback类中的内容。

package com.szh.eurekastudent.client;

import com.szh.eurekastudent.client.impl.GradeClientImpl;
import com.szh.eurekastudent.pojo.Grade;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;


@FeignClient(name = "eureka-grade",fallback = GradeClientImpl.class)
public interface GradeClient {

    @GetMapping("/grades")
    public List<Grade> findStudents();
}

4.测试

依次启动springcloud-eureka-server、springcloud-eureka-grade、spring-eureka-student三个项目。

在这里插入图片描述

首先在浏览器中输入:http://localhost:8091/grades

在这里插入图片描述
说明加入熔断相关信息后,不影响正常的访问。接下来我们手动停止springcloud-eureka-grade项目再次测试:
在浏览器中输入:http://localhost:8091/grades

在这里插入图片描述
“出错了,执行熔断器”

根据返回结果说明熔断成功。

第二种
@HystrixCommand注解

在 Spring Cloud 框架里熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用状况,当失败的调用到一定的阈值,缺省值是 5s 内 20 次调用失败就会启动熔断机制。熔断机制的注解是 @HystrixCommand

1.pom.xml文件
记得引入hystrix依赖

<!-- hystrix -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.szh</groupId>
    <artifactId>eureka-student</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>eureka-student</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.13</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2.application.properties文件
记得注释掉第一种方法
#开启熔断器
#feign.hystrix.enabled=true

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/studentdb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

spring.application.name=eureka-student
server.port=8091
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka/

#开启熔断器
#feign.hystrix.enabled=true

3.项目启动类
加上注解
@EnableHystrix

package com.szh.eurekastudent;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
@MapperScan("com.szh.eurekastudent.mapper")
@SpringBootApplication
public class EurekaStudentApplication {

    public static void main(String[] args) {
        SpringApplication.run(EurekaStudentApplication.class, args);
    }

}

4.controller层的业务代码

调用方法上加
@HystrixCommand(fallbackMethod = “findGrade”)注解
增加方法findGrade

package com.szh.eurekastudent.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.szh.eurekastudent.client.GradeClient;
import com.szh.eurekastudent.pojo.Grade;
import com.szh.eurekastudent.pojo.Student;
import com.szh.eurekastudent.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
public class StudentController {

    @Autowired
    private StudentService studentService;

    @Autowired
    private GradeClient gradeClient;

    @GetMapping("/students")
    public List<Student> findStudents(){
        return studentService.selAllStudent();
    }

    @GetMapping("/grades")
    @HystrixCommand(fallbackMethod = "findGrade")
    public List<Grade> findGrades(){
        return gradeClient.findStudents();
    }

    public List<Grade> findGrade() {
        //打印日志
        //使用消息队列发送消息
        //发送短信
        List<Grade> list = new ArrayList<>();
        Grade grade = new Grade();
        grade.setGradeId(0);
        grade.setGradeName("出错了,执行熔断器");
        list.add(grade);
        return list;
    }
}

5.测试

仍然依次启动springcloud-eureka-server、springcloud-eureka-grade、spring-eureka-student三个项目。在这里插入图片描述

首先在浏览器中输入:http://localhost:8091/grades
在这里插入图片描述

说明加入熔断相关信息后,不影响正常的访问。接下来我们手动停止springcloud-eureka-grade项目再次测试:
在浏览器中输入:http://localhost:8091/grades

在这里插入图片描述

“出错了,执行熔断器”

根据返回结果说明熔断成功。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章