ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringCloud 使用 Hystrix 实现【服务端】降级

2022-09-04 00:04:36  阅读:319  来源: 互联网

标签:Map 降级 Hystrix SpringCloud result put import id 服务端


Hystrix 是 Netflix 公司提供的一个开源免费组件,主要用于降级熔断服务调用,防止系统出现级联失败(也就是通常所说的雪崩)。我们在实际开发中,需要在服务端和客户端都有降级措施,结合后续将要介绍的熔断,最大限度的保护系统的正常运行。

SpringCloud 从 2020.0.1 版本已经移除了 Hystrix 组件,因此要想使用的话,需要手动引入依赖。

本篇博客使用截止目前最新版的 SpringCloud(版本 2021.0.3)和 SpringBoot(版本 2.6.11)介绍如何在服务端使用 Hystrix 组件实现降级措施,在本篇博客的最后会提供源代码的下载。


一、搭建工程

采用 Maven 搭建 springcloud_hystrix1 父工程(之所以取名 springcloud_hystrix1 ,主要是因为后续还会针对 hystrix 做一些 demo),下面包含 3 个子工程:

image

对于 springcloud_hystrix1 父工程 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_hystrix1</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.6.11</version>
        <relativePath/>
    </parent>

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

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


二、搭建服务提供者

这里是本篇博客的重点,因为本篇博客的 Demo 就是在这里使用 Hystrix 实现服务端降级方案。

首先需要在 pom 中引入有关 Hystrix 的相关依赖。

<?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">
    <parent>
        <artifactId>springcloud_hystrix1</artifactId>
        <groupId>com.jobs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>provider_app</artifactId>

    <dependencies>
        <!--spring boot web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!-- hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-hystrix</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>1.5.18</version>
        </dependency>
    </dependencies>
</project>

从上面的 pom 文件可以看出,主要导入了 spring-cloud-netflix-hystrix 和 hystrix-javanica 两个依赖包。

在引入了 Hystrix 依赖后,在服务端实现降级的步骤如下:

1 在 SpringBoot 启动入口类上,增加 @EnableHystrix 注解

package com.jobs.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

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

2 为服务的接口,定义所使用的降级方法

3 在服务接口上,使用 @HystrixCommand 注解,通过 fallbackMethod 指定降级方法

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.RestController;

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

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

    /*
    服务提供者会在两种情况下发生降级:
    1 发生异常
    2 业务逻辑处理超时(
    --- 对于 Hystrix 来说,默认必须在 1 秒内处理完业务逻辑,否则就会超时
    --- 可以针对具体的接口方法,配置 Hystrix 的业务逻辑超时时间,比如配置为 3 秒
     */

    //这个是正常方法,不会发生降级
    @RequestMapping("/test1/{id}")
    public Map GetData(@PathVariable("id") int id) {

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

        return result;
    }

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

    //该方法会发生异常,从而调用 @HystrixCommand 方法所配置的降级方法
    @HystrixCommand(fallbackMethod = "GetErrorFallback")
    @RequestMapping("/test2/{id}")
    public Map GetError(@PathVariable("id") int id) {

        //执行此处,会发生异常,导致调用降级方法
        int 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", "服务发生异常,导致降级...");

        return result;
    }

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

    //该方法会发生业务逻辑处理超时,因为 Hstrix 默认超时时间是 1 秒
    //从而调用 @HystrixCommand 方法所配置的降级方法
    @HystrixCommand(fallbackMethod = "GetTimeOutFallback")
    @RequestMapping("/test3/{id}")
    public Map GetTimeOut1(@PathVariable("id") int id) {

        /*
        默认情况下,Hstrix 要求必须在 1 秒内处理完业务逻辑并返回结果
        在此休眠 2 秒钟,导致服务端接口的业务逻辑处理超时,导致调用降级方法
        注意:
            ---超时是由服务端接口造成的。
            ---如果此时对客户端进行配置,想要避免超时,属于瞎折腾,白费功夫。
        */

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        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;
    }

    //定义 GetTimeOut 的降级方法
    //方法的返回值、参数列表、参数类型,要与原方法保持一致
    public Map GetTimeOutFallback(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", "服务业务处理超时,导致降级...");

        return result;
    }

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

    //该方法处理业务逻辑处理,不会发生超时,
    //因为 Hstrix 配置的【处理时间】大于【实际所需要的业务逻辑处理时间】
    @HystrixCommand(fallbackMethod = "GetTimeOutFallback", commandProperties = {
            //设置 Hystrix 的允许的业务逻辑处理超时时间为 3 秒
            //想要查找可以设置的 HystrixProperty 值,可以按两下 shift 键,
            //输入 HystrixCommandProperty,然后查看 HystrixCommandProperties 类的构造方法
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                    value = "3000")})
    @RequestMapping("/test4/{id}")
    public Map GetTimeOut2(@PathVariable("id") int id) {

        //这里虽然休眠 2 秒,但是设置了 Hystrix 允许业务逻辑处理时间为 3 秒,所以不会超时
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        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;
    }
}

有两种情况下,Hystrix 会让服务端的接口发生降级:【接口内部出现异常】和【接口内部业务处理时间超时】。

从上面的代码中可以看出,服务提供者一共提供了 4 个接口:(用于让客户端请求,对比验证服务端降级的实现效果)

  • /provider/test1/{id} 接口,能够正常返回,这里没有提供降级方法
  • /provider/test2/{id} 接口,会产生异常,从而导致降级,会执行降级方法并返回降级结果
  • /provider/test3/{id} 接口,业务逻辑处理时间会超时,从而导致降级,会执行降级方法并返回降级结果
  • /provider/test4/{id} 接口,由于为 Hystrix 配置了更长的时间,所以业务逻辑处理不会超时,不会降级

三、搭建客户端消费者

本篇博客的 Demo 采用 openfeign 实现对服务提供者的接口调用,具体实现如下:

1 在 pom 文件中引入 openfeign 的依赖

<?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">
    <parent>
        <artifactId>springcloud_hystrix1</artifactId>
        <groupId>com.jobs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer_app</artifactId>

    <dependencies>
        <!--spring boot web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--引入 openfeign 的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>
</project>

2 在 SpringBoot 启动入口类上,需要使用 @EnableFeignClients 注解

package com.jobs.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

//启用 Feign 客户端功能
@EnableFeignClients
@SpringBootApplication
public class ConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApp.class,args);
    }
}

3 定义 feign 声明式接口,添加注解 @FeignClient,设置 value 值为【服务提供者的】应用名称

4 定义接口方法,方法的参数列表和返回值,需要跟服务提供者的接口方法保持一致,方法名称无所谓

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("PROVIDER-APP")
public interface ProviderAppClient {

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

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

    @RequestMapping("/provider/test3/{id}")
    Map GetServerTimeOut1(@PathVariable("id") int id);

    @RequestMapping("/provider/test4/{id}")
    Map GetServerTimeOut2(@PathVariable("id") int id);
}

4 在调用服务接口的类中注入 feign 接口对象,调用 feign 接口中的方法完成远程调用

package com.jobs.consumer.controller;

import com.jobs.consumer.feignclient.ProviderAppClient;
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.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;
    }

    //服务端发生异常,在服务端降级,返回降级结果
    @RequestMapping("/test2/{id}")
    public Map GetTest2(@PathVariable("id") int id) {

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

    //服务端处理业务逻辑超时,在服务端降级,返回降级结果
    @RequestMapping("/test3/{id}")
    public Map GetTest3(@PathVariable("id") int id) {

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

    //正常调用,返回正常结果
    @RequestMapping("/test4/{id}")
    public Map GetTest4(@PathVariable("id") int id) {

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

最后列出客户端消费者的 application.yml 配置,仅仅列出有关 feign 访问服务端接口最大允许等待的超时配置:

# 这里可以设置 feign 客户端连接和获取接口数据的超时时间
feign:
  client:
    config:
      # 通过 default 可以设置 feign 默认超时时间
      default:
        # 连接超时时间(毫秒)
        ConnectTimeout: 1000
        # 等待业务处理返回结果的超时时间(毫秒)
        ReadTimeout: 1000
      # 这里可以针对具体的【服务提供者】应用名称下的所有接口,设置超时时间
      PROVIDER-APP:
        ConnectTimeout: 1000
        ReadTimeout: 3000

我们故意为服务提供者 PROVIDER-APP 配置了业务逻辑访问超时时间为 3 秒,目的就是为了测试调用服务端的 /provider/test3/{id} 接口,在测试中你会发现,客户端配置等待超时的时间大于服务端业务处理的时间,没有意义。因为在服务端的 /provider/test3/{id} 接口中,降级是由服务端业务逻辑超时导致的,跟客户端没有任何关系。



到此为止,所有工程已经搭建完毕,与之前博客重复的代码细节,这里就省略了。你可以下载本篇博客的源代码进行详细查看。最后可以运行 Demo 程序,调用客户端提供的接口,验证服务端降级效果。本篇博客的 Demo 源代码经过详细测试无误。

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

标签:Map,降级,Hystrix,SpringCloud,result,put,import,id,服务端
来源: https://www.cnblogs.com/studyjobs/p/16653984.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有