手把手教你搭建SpringCloud项目(十一)集成Hystrix之服务降级

Spring Cloud全文目录

源码

什么是微服务?有手就行

SpringCloud简介与5大常用组件

一、手把手教你搭建SpringCloud项目(一)搭建Maven父工程,傻瓜式操作

二、手把手教你搭建SpringCloud项目(二)生产者与消费者

三、手把手教你搭建SpringCloud项目(三)集成Eureka服务注册中心

四、手把手教你搭建SpringCloud项目(四)EurekaServer集群版搭建

五、手把手教你搭建SpringCloud项目(五)生产者集群版搭建

六、手把手教你搭建SpringCloud项目(六)actuator微服务信息完善与Eureka自我保护机制

七、手把手教你搭建SpringCloud项目(七)Eureka实现服务发现(Discovery)

八、手把手教你搭建SpringCloud项目(八)集成Consul服务注册中心

九、手把手教你搭建SpringCloud项目(九)集成Ribbon负载均衡器

十、手把手教你搭建SpringCloud项目(十)集成OpenFeign服务接口调用

十一、手把手教你搭建SpringCloud项目(十一)集成Hystrix之服务降级

十二、手把手教你搭建SpringCloud项目(十二)集成Hystrix之服务熔断

十三、手把手教你搭建SpringCloud项目(十三)集成Hystrix之图形化Dashboard实时监控

十四、手把手教你搭建SpringCloud项目(十四)集成Gateway新一代服务网关

十五、手把手教你搭建SpringCloud项目(十五)集成Config分布式配置中心

十六、手把手教你搭建SpringCloud项目(十六)集成Bus消息总线

十七、手把手教你搭建SpringCloud项目(十七)集成Stream消息驱动

十八、手把手教你搭建SpringCloud项目(十八)集成Sleuth分布式链路跟踪

文章持续更新中,欢迎关注!

微服务架构的雪崩效应

什么是微服务中的雪崩效应?

在这里插入图片描述

首先介绍两个概念:扇入扇出

对于微服务中某个服务来说,调用它的服务请求称为扇入,它调用其它服务的请求称为扇出。

微服务中,⼀个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调⽤响应时间过⻓或者不可⽤,对微服务A的调⽤就会占⽤越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。

雪崩效应的解决方案

服务熔断
熔断机制是应对雪崩效应的⼀种微服务链路保护机制。当扇出链路的某个微服务不可⽤或者响应时间太⻓时,熔断该节点微服务的调⽤,进⾏服务的降级,快速返回错误的响应信息。当检测到该节点微服务调⽤响应正常后,恢复调⽤链路。【通常与服务降级一起使用】

服务降级
服务降级是从系统整体考虑,当某个服务熔断之后,服务器不再被调⽤时,客户端可以为发送的请求准备⼀个本地的fallback回调,返回⼀个与方法返回值类型相同的缺省值,这样做,虽然服务水平下降,但整体仍然可用,比直接熔断要好。

服务限流
当服务为系统的核心功能,不能使用熔断与降级降低服务时,可以使用服务限流。

限流措施也很多,如:

  • 限制总并发数(比如数据库连接池、线程池)
  • 限制瞬时并发数(如nginx限制瞬时并发连接数)
  • 限制时间窗⼝内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
  • 限制远程接⼝调⽤速率、限制MQ的消费速率等

Hystrix简介

Hystrix是什么

在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。在springcloud中断路器组件就是Hystrix。Hystrix也是Netflix套件的一部分。他的功能是,当对某个服务的调用在一定的时间内(默认10s),有超过一定次数(默认20次)并且失败率超过一定值(默认50%),该服务的断路器会打开。返回一个由开发者设定的fallback。fallback可以是另一个由Hystrix保护的服务调用,也可以是固定的值。fallback也可以设计成链式调用,先执行某些逻辑,再返回fallback。

Hystrix解决了什么问题

复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。

例如,对于一个依赖于30个服务的应用程序,每个服务都有99.99%的正常运行时间,你可以期望如下:
在这里插入图片描述
现实通常是更糟糕

当一切正常时,请求看起来是这样的:
在这里插入图片描述
当其中有一个系统有延迟时,它可能阻塞整个用户请求:
在这里插入图片描述
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)
在这里插入图片描述

Hystrix三大作用

  • 服务降级(Fallback):比如当服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,Fallback,会发生降级的几种情况:程序运行异常、超时、服务熔断触发服务降级。

  • 服务熔断(Break):类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。三个步骤先进行服务的降级、进而熔断、恢复调用链路。

  • 服务限流(Flowlimit):比如秒杀高并发等操作,严禁一窝蜂的过来拥挤、安排大家排队,一秒钟N个请求,有序进行。

搭建生产者微服务模块

1.建立生产者module: springcloud-provider-hystrix-payment6001

在这里插入图片描述

2.pom.xml

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

    <artifactId>springcloud-provider-hystrix-payment6001</artifactId>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.dyh.springcloud</groupId>
            <artifactId>springcloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>
    </dependencies>
</project>

3.application.yml

server:
  port: 6001
spring:
  application:
    name: springcloud-provider-hystrix-payment
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.主启动类 PaymentHystrixMain6001

package com.dyh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
 * @ClassName: PaymentHystrixMain6001
 * @author: dyh
 * @since: 2021/12/22 17:24
 */

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

5. 业务类

PaymentService

package com.dyh.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName: PaymentService
 * @author: dyh
 * @since: 2021/12/22 17:26
 */
@Service
public class PaymentService {
    /**
     * 正常访问
     *
     * @param id
     * @return
     */
    public String paymentInfo_OK(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "O(∩_∩)O哈哈~";
    }

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    public String paymentInfo_TimeOut(Integer id) {
        int timeNumber = 3;
        try {
            // 暂停3秒钟
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗时(秒)" + timeNumber;
    }

}

PaymentController

package com.dyh.springcloud.controller;

import com.dyh.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


@RestController
@Slf4j
public class PaymentController {
    @Autowired
    private PaymentService paymentService;

    @Value("${server.port}")
    private String servicePort;

    /**
     * 正常访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_OK(id);
        log.info("*******************result:" + result);
        return result;
    }

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*********************result:" + result);
        return result;

    }

}

6.测试

先启动2个eureka集群 7001/7002
启动springcloud-provider-hystrix-payment6001
访问路径http://localhost:6001/payment/hystrix/ok/1,成功界面如下图:
在这里插入图片描述
访问http://localhost:6001/payment/hystrix/timeout/2,等待5秒,成功返回界面如下图:
在这里插入图片描述
在正常的情况下现在项目没有问题,两个接口都可以正常访问,以上述为根基平台,来演示从正确->错误->降级熔断->恢复的情况。下面我们进行高并发测试一下,看看还是是否正常。使用测试工具Jmeter,新建一个线程组,来20000个并发来压死8001,20000个请求都去访问http://localhost:6001/payment/hystrix/timeout/2这个接口服务,如下图:

在这里插入图片描述

配置http请求,注意请求方式、端口号、接口,如下图:
在这里插入图片描述
我们启动这个线程组,可以看到线程组可以访问到接口,如下图:
在这里插入图片描述
然后我们自己访问这两个接口,看看会出现什么情况,出现了本来可以立马得到响应,但是两个都在转圈圈,然后才得到响应,如下图:
在这里插入图片描述
在这里插入图片描述

现如下的原因就是Tomcat的默认工作线程被打满了,没有多余的线程来分解压力和处理。上面还在只是生产者服务自己6001的测试,假如此时外部的消费者服务84也来访问,那消费者只能干等,最终导致消费端服务84不满意,服务端6001直接被拖死。

搭建消费者微服务模块

1.建立消费者module: springcloud-consumer-feign-hystrix-order84

在这里插入图片描述

2.pom.xml

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

    <artifactId>springcloud-consumer-feign-hystrix-order84</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.dyh.springcloud</groupId>
            <artifactId>springcloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </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>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
            <version>1.5.18</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>

3.application.yml

server:
  port: 84
spring:
  application:
    name: springcloud-consumer-feign-hystrix-order
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.主启动类 OrderHystrixMain84

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

/**
 * @ClassName: OrderHystrixMain84
 * @author: dyh
 * @since: 2021/12/22 18:53
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients

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

}


5. 业务类

PaymentHystrixService

package com.dyh.springcloud.service;

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;

/**
 * @ClassName: PaymentHystrixService
 * @author: dyh
 * @since: 2021/12/22 18:57
 */

@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

    /**
     * 正常访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);

}


OrderHystirxController

package com.dyh.springcloud.controller;

import com.dyh.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @ClassName: OrderHystirxController
 * @author: dyh
 * @since: 2021/12/22 19:00
 */
@RestController
@Slf4j
public class OrderHystirxController {
    @Autowired
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_OK(id);
    }

    @GetMapping("/consumer/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {

        return paymentHystrixService.paymentInfo_TimeOut(id);
    }

}

6.测试

先启动2个eureka集群 7001/7002
启动springcloud-consumer-feign-hystrix-order84
在正常情况下,进行接口调用测试,我们访问http://localhost:84/consumer/hystrix/ok/1,可以看到可以立马得到响应。成功界面如下图:

在这里插入图片描述

那我们在高并发的情况下测试一下,看看是什么情况,我们启动线程组,然后再次访问http://localhost:84/consumer/hystrix/ok/1,
会出现有时需要等待几秒才会得到响应,如下图:
在这里插入图片描述

然后我们访问http://localhost:84/consumer/hystrix/timeout/1,直接出现了超时错误,如下图:
在这里插入图片描述

出现上述的故障和导致的现象就是因为6001同一层次的其他接口被困死,因为tomcat线程池里面的工作线程已经被挤占完毕,此时84消费端调用生产者6001服务,客户端访问响应缓慢,正因为有上述的故障和不良表现,才有我们的降级、容错、限流等技术的诞生。

7.如何解决上述的问题?

  1. 出现超时导致服务器变慢(转圈),超时不再等待
  2. 出错(宕机活程序运行出错),出错 要有兜底
    当我们的生产者服务6001超时了,消费者(84)不能一直卡死等待,必须要服务降级
    当我们的生产者服务6001宕机了,消费者(84)不能一直卡死等待,必须要服务降级
    当我们的生产者服务6001正常,但是消费者(84)自己有故障或有自有要求(自己的等待时间小于生产者服务本身需要的时间)

服务降级配置

1.访问超时异常的服务降级

我们要先从生产者6001服务自身开始找存在的问题,设置自身的调用超时时间的峰值,峰值内正常可以正常运行,超过了需要有兜底的方法处理,做服务降级Fallback。就比如现在访问超时的方法设置暂停2秒,我们设置该方法的峰值为3秒,如果访问超过3秒,我们就做服务降级Fallback,如果没有超过就正常返回值。如下:

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    public String paymentInfo_TimeOut(Integer id) {
        try {
            // 暂停2秒钟
            TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗时(秒)";
    }

服务降级Fallback,我们要在业务类中先写一个兜底的方法,如下:

    /**
     * 超时访问到这里兜底
     *
     * @param id
     * @return
     */
    public String paymentInfo_TimeOutHabdler(Integer id) {
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOutHabdler,id:" + id + "\t" +
                "系统繁忙,请稍后再试****o(╥﹏╥)o" ;
    }

然后在业务类中访问超时的paymentInfo_TimeOut方法上添加注解@HystrixCommand进行配置,一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法。

fallbackMethod属性是写降级的方法名,意思就是该方法出现问题后, 这个方法paymentInfo_TimeOutHabdler进行兜底。
commandProperties属性是数组形式的,可以设置多个属性,添加该注解@HystrixProperty,然后配置里边name和value的属性。name属性就是该线程timeoutInMilliseconds,value就是设置峰值时间。具体配置如下:

    /**
     * 超时访问
     *
     * @param id
     * @return
     */

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHabdler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    } )
    public String paymentInfo_TimeOut(Integer id) {
        try {
            // 暂停2秒钟
            TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗时(秒)";
    }


然后在主启动类上加上注解@EnableCircuitBreaker,表示激活业务类上加的@HystrixCommand注解。如下:

package com.dyh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
 * @ClassName: PaymentHystrixMain6001
 * @author: dyh
 * @since: 2021/12/22 17:24
 */

@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//激活
public class PaymentHystrixMain6001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain6001.class, args);
    }
}

我们重新启动生产者服务6001,进行访问http://localhost:6001/payment/hystrix/timeout/1,可以正常访问
在这里插入图片描述

下面我们将业务类中暂停时间2秒改成5秒,峰值时间是3秒,超过了峰值时间,看一下是否可以返回到设置的Fallback的paymentInfo_TimeOutHabdler兜底的方法,如下:

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHabdler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {
        try {
            // 暂停5秒钟
            TimeUnit.MILLISECONDS.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗时(秒)";
    }

重新启动后再次访问http://localhost:6001/payment/hystrix/timeout/1,可以看到当超过峰值3秒后进行了降级服务,直接到了我们写的兜底的paymentInfo_TimeOut方法上。如下图:
在这里插入图片描述

2.运行异常的服务降级

修改业务类中的paymentInfo_TimeOut的方法,如下:

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHabdler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {
        int age = 10 / 0;
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" + "O(∩_∩)O哈哈~  耗时(秒)";
    }

然后再重启服务,再次访问http://localhost:6001/payment/hystrix/timeout/1,可以看到直接就进行了降级服务,直接到了我们写的兜底的paymentInfo_TimeOut方法上,如下图:
在这里插入图片描述

我们现在已经对生产者服务6001进行了自身的服务降级,下面要对消费者服务84进行服务降级。首先要在pom.xml文件中新增
Hystrix的依赖,如下:

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

在yml配置文件中新增Hystrix的配置,如下:

feign:
  hystrix:
    enabled: true

在主启动类上加上注解@EnableCircuitBreaker,表示激活业务类上加的@HystrixCommand注解。如下:

package com.dyh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @ClassName: OrderHystrixMain84
 * @author: dyh
 * @since: 2021/12/22 18:53
 */
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker//回路
public class OrderHystrixMain84 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain84.class, args);
    }

}

然后在OrderHystirxController业务类上加上注解与里边的相关配置,然后再加上服务降级兜底的方法,如下:

    @GetMapping("/consumer/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
                    value = "1000")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }
    //服务降级的兜底的方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消费者84,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
    }

在访问消费者84服务时,要确保生产者6001服务可以正常运行,不会出现降级服务,将生产者6001服务修改如下:

/**
     * 超时访问
     *
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHabdler",commandProperties = {
                @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
            } )
    public String paymentInfo_TimeOut(Integer id) {
        try {
            // 暂停2秒钟
            TimeUnit.MILLISECONDS.sleep(2000);
 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗时(秒)";
 
  }

访问生产者6001服务,http://localhost:6001/payment/hystrix/timeout/1,没有进行服务降级,成功界面如下:
在这里插入图片描述

生产者6001服务测试没有问题,那消费者84服务,我们做了服务降级,因为生产者服务超时的方法需要等到暂停2秒,而我们消费84服务调用该接口时,只等待 1秒,所有会出现消费者84服务降级,如下图:
在这里插入图片描述
现在生产者6001服务与消费者84服务都做了服务降级,都是没有问题的。但是我们可以发现当前的问题,如果每个业务都对应一个兜底的方法,出现了代码膨胀。解决这个问题就是如果不是特殊的业务,我们就使用全局的服务降级的兜底方法,如果是特殊的业务就要有对应的专属的服务降级的方法。

优化代码膨胀问题

开始整合,那就需要在消费者84服务的的业务类OrderHystirxController中一个全局的服务降级的兜底的方法,如下:

 /**
     * 全局fallback
     *
     * @return
     */
    public String payment_Global_FallbackMethod() {
        return "Global异常处理信息,请稍后重试.o(╥﹏╥)o";
    }

然后在类上加上默认使用全局的服务降级兜底的方法,如下:

@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")//全局fallback方法的名字

然后在需要服务降级的方法上加上注解,如下:

    @GetMapping("/consumer/hystrix/timeout/{id}")
    @HystrixCommand//默认的fallback注解
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }

那现在重启消费者84服务,再次访问http://localhost:84/consumer/hystrix/timeout/1,该方法已经使用了全局默认的服务降级兜底的方法出现如下界面:
在这里插入图片描述

业务逻辑与代码混乱整合

关于服务的降级,是消费者服务去调用生产者服务,如果生产者服务出现宕机或者关闭,那我们就要对消费者服务进行服务降级处理,与生产者服务没有关系,值需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦。为了防止与业务逻辑与代码混在一块,看起来特别混乱,所以要新建一个类PaymentFallbackService,实现该业务类PaymentHystrixService接口,统一为接口里面的方法进行异常处理。如下:

package com.dyh.springcloud.service;

import org.springframework.stereotype.Component;

/**
 * @ClassName: PaymentFallbackService
 * @author: dyh
 * @since: 2021/12/24 12:54
 */
@Component
public class PaymentFallbackService implements PaymentHystrixService {
    @Override
    public String paymentInfo_OK(Integer id) {
        return "PaymentFallbackService fall  paymentInfo_OK 服务器出现故障,o(╥﹏╥)o";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "PaymentFallbackService fall  paymentInfo_TimeOut 服务器出现故障,o(╥﹏╥)o";
    }

}

也要在PaymentHystrixService业务类上加上@FeignClient注解,表示FeignClient注解的作用目标在接口上,如下:

package com.dyh.springcloud.service;

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;

/**
 * @ClassName: PaymentHystrixService
 * @author: dyh
 * @since: 2021/12/22 18:57
 */

@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackService.class)
//value属性是从该服务中获取,fallback属性当服务出现故障时,该类进行服务降级处理
public interface PaymentHystrixService {

    /**
     * 正常访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    /**
     * 超时访问
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);

}

那我们启动服务,访问http://localhost:84/consumer/hystrix/ok/1的方法,可以成功访问,如下图:
在这里插入图片描述
那我们关闭生产者8001服务,再次访问http://localhost:84/consumer/hystrix/ok/1,可以看到开启了服务降级处理,让消费者服务在生产者服务不可用时,也会提示信息而不会挂起耗死服务器。如下图:
在这里插入图片描述
可以看到paymentInfo_OK方法上都没有进行配置服务降级,但是当生产者服务关闭后,也进行了服务降级,这里因为我们配置在了PaymentFallbackService类中,这样就解决了业务逻辑与代码混合在一起的问题。

    @GetMapping("/consumer/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_OK(id);
    }

全部配置学习完是不是so easy!
在这里插入图片描述
下一篇文章继续学习服务熔断,继续更新,欢迎关注!
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值