SpringCloud 使用 Hystrix 实现服务端接口【熔断】

Hystrix 在服务端接口默认开启并支持了熔断机制,可以使用 @HystrixCommand 注解中的 commandProperties 进行熔断参数自定义配置,默认情况下对于一个接口来说,5 秒钟发生 20 次降级,或者失败率达到 50% 时,就会触发熔断机制。

所谓熔断机制,可以举一个简单的例子进行解释:当 A 服务调用 B 服务,B 服务可能会由于访问量过大或其它原因发生崩溃,如果没有熔断机制,很可能会导致 A 服务也崩溃,从而造成整个系统级联崩溃(雪崩)。有了熔断机制之后,B 服务就会在熔断期间直接返回降级结果,减轻自己的访问压力,A 服务就能够得到 B 服务的快速响应,不会进行线程资源积压消耗殆尽而崩溃。当 B 服务的熔断时间结束后,A 服务如果再次请求 B 服务时,B 服务会允许其中一个访问线程访问来验证 B 服务的可用性,如果可用则 A 服务的所有访问线程后续都可以正常调用 B 服务,反之则继续开启新一轮的熔断周期。

结合上面对熔断机制的描述,我们发现熔断有 3 种状态:

  • 正常状态:A 服务的每次请求都可以按照预期正常调用 B 服务。
  • 熔断状态:A 服务在熔断时间内调用 B 服务,B 服务不执行自身逻辑代码,直接返回降级结果。
  • 半熔断状态:熔断时间结束后,A 服务中仅有一个请求被允许尝试调用 B 服务(其它请求则继续熔断),如果请求成功,则 A 服务的所有请求都可以正常请求 B 服务(恢复到正常状态),否则继续开始新一轮的熔断周期。

本篇博客的 demo 在之前的基础上进行了简单的修改,主要是介绍如何修改服务端接口熔断的配置参数,以及测试的结果介绍。在本篇博客的最后会提供源代码的下载。


一、搭建工程

采用 Maven 搭建 springcloud_hystrix3 父工程,下面包含 3 个子工程:

image

对于 springcloud_hystrix3 父工程 pom 文件内容如下所示:

<?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>

    <groupId>com.jobs</groupId>
    <artifactId>springcloud_hystrix3</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>eureka_app</module>
        <module>provider_app</module>
        <module>consumer_app</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <!--spring boot-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/>
    </parent>

    <!--Spring Cloud-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR12</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

有关【Eureka 注册中心】的搭建过程,这里就省略了,跟之前博客一模一样。


二、服务提供者搭建

对于服务提供者,是本篇博客的重点,因为熔断的配置就是在服务端进行的。

服务提供者的 pom 文件和 application.yml 配置文件,与之前的 demo 相比,没啥变化,这里不再列出。

下面仅列出服务提供者,提供的 2 个接口,供客户端消费者调用进行测试验证。

package com.jobs.provider.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@RequestMapping("/provider")
@RestController
public class ProviderController {

    //该服务端接口可以正常访问,不会发生异常,不受熔断机制影响
    //也就是说:其它接口发生了熔断而无法访问,不会导致该接口也熔断无法访问
    @RequestMapping("/test1/{id}")
    public Map GetData(@PathVariable("id") int id) {

        Map result = new HashMap();
        result.put("status", 0);
        result.put("msg", "success");
        result.put("get_id_value", id);
        result.put("version", UUID.randomUUID().toString());

        return result;
    }

    //-----------------------------------

    //该方法会发生异常,从而调用 @HystrixCommand 方法所配置的降级方法
    @HystrixCommand(fallbackMethod = "GetErrorFallback", commandProperties = {

            //想要查找可以设置的 HystrixProperty 值,可以按两下 shift 键,
            //输入 HystrixCommandProperties,然后查看 HystrixCommandProperties 类的构造方法

            //设置Hystrix的超时时间,默认1秒
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
            //设置熔断监控时间 默认5秒
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),
            //设置导致熔断的失败次数。默认20次
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            //设置导致熔断的失败率 默认50%
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "80")
    })
    @RequestMapping("/test2/{id}")
    public Map GetError(@PathVariable("id") int id) {

        int num = 0;
        if (id == 1) {
            //执行此处,会发生异常,导致调用降级方法
            num = id / 0;
        }

        Map result = new HashMap();
        result.put("status", 0);
        result.put("msg", "success");
        result.put("data", num);
        result.put("version", UUID.randomUUID().toString());

        return result;
    }

    //定义 GetError 的降级方法
    //方法的返回值、参数列表、参数类型,要与原方法保持一致
    public Map GetErrorFallback(int id) {

        Map result = new HashMap();
        result.put("status", 1);
        result.put("msg", "fallback...");
        result.put("method", "GetError");
        result.put("get_id_value", id);
        result.put("remark", "GetError服务端接口发生异常,导致降级...");

        return result;
    }
}

从上面的代码可以看出,对于具体的一个接口,我们可以通过以下 3 个属性参数自定义熔断策略:

  • circuitBreaker.sleepWindowInMilliseconds 用于设置熔断监控时间 默认5秒
  • circuitBreaker.requestVolumeThreshold 用于设置导致熔断的失败次数。默认20次
  • circuitBreaker.errorThresholdPercentage 用于设置导致熔断的失败率 默认50%

对于服务提供者提供的接口:

  • /provider/test1/{id} 接口,每次调用都会正常运行,不会发生服务端降级
  • /provider/test1/{id} 接口,当 id 为 1 时故意代码运行抛出异常,所以在服务端会产生降级,达到熔断条件后就会熔断,熔断期间调用此接口,id 传入非 1 的值,仍然会返回降级结果,直到熔断结束后才会返回正常结果

注意:/provider/test1/{id} 接口发生熔断,只是自己返回降级结果,不会影响 /provider/test1/{id} 接口的调用和返回正常结果


三、客户端消费者搭建

对于客户端消费者,只是用来验证服务提供者接口的熔断效果,与之前的 demo 相比,没啥变化。

这里列出客户端消费者提供的两个接口,用来调用服务提供者的接口,验证熔断效果。

package com.jobs.consumer.feignclient;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@FeignClient(value = "PROVIDER-APP", fallbackFactory = ProviderAppClientImpl.class)
public interface ProviderAppClient {

    @RequestMapping("/provider/test1/{id}")
    Map GetServerData(@PathVariable("id") int id);

    @RequestMapping("/provider/test2/{id}")
    Map GetServerErrorData(@PathVariable("id") int id);

}

package com.jobs.consumer.controller;

import com.jobs.consumer.feignclient.ProviderAppClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RequestMapping("/consumer")
@RestController
public class ConsumerController {

    @Autowired
    private ProviderAppClient providerAppClient;

    //该接口可以正常访问,不会其它接口熔断的影响
    @RequestMapping("/test1/{id}")
    public Map GetTest1(@PathVariable("id") int id) {

        Map result = providerAppClient.GetServerData(id);
        return result;
    }

    //服务端接口会发生异常,在服务端【有降级处理逻辑】,返回服务端接口降级处理结果
    //所以客户端这边,就不会发生降级,直接返回服务端降级后的结果
    //服务端接口已经配置了熔断策略,10 秒钟发生 10 次异常,则触发熔断机制
    //然后再过 10 秒钟,尝试放行一次请求,如果成功,则恢复正常访问,否则继续熔断 10 秒钟
    @RequestMapping("/test2/{id}")
    public Map GetTest2(@PathVariable("id") int id) {

        Map result = providerAppClient.GetServerErrorData(id);
        return result;
    }
}

客户端提供了 2 个接口,用于验证服务提供者的熔断效果:

  • /consumer/test1/{id} 接口,每次都能够正常调用成功,而且不会因为其它接口发生熔断而导致自己无法调用。
  • /consumer/test2/{id} 接口,调用服务端接口时,当传入 id 值为 1 时,服务端返回降级结果,此时不停的进行调用,当服务熔断掉件后,此时 id 值传入非 1 的参数值时,也会返回服务端降级结果,直到熔断结束后才会返回正常结果。


OK,到此为止,有关 Hystrix 的服务端接口熔断实现就介绍完毕。你可以下载本篇博客的源代码进行详细查看。最后可以运行 Demo 程序,调用客户端提供的接口,验证服务端降级效果。本篇博客的 Demo 源代码经过详细测试无误。

本篇博客的源代码下载地址:https://files.cnblogs.com/files/blogs/699532/springcloud_hystrix3.zip

posted @ 2022-09-17 20:51  乔京飞  阅读(11147)  评论(0编辑  收藏  举报