六、Hystrix服务降级与熔断

本文介绍了分布式系统面临的雪崩问题及其解决方案——Hystrix。通过实例展示了如何使用Hystrix实现服务降级和熔断,以应对高并发场景下的服务稳定性。详细讲解了服务降级的三种策略,并提供了代码实现和压测分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

卑微程序员:各位客官,走过看过,不求三联,只需点赞


SpringCloud系列目录:


一、前言

  当今环境下分布式已是当今系统架构的发展方向,但分布式同样面临着许多的问题。本文将从分布式系统面临的问题、解决方案、能解决哪些问题,并结合代码去演示。文章中的代码都已提交到Github,如需自取。

二、分布式系统面临的问题

在这里插入图片描述
服务雪崩:

多个微服务之间调用的时候,假设微服务A调用B和微服务C,微服务B和微服务C又调用其他的服务,这就是所谓的扇出
。如果扇出的连路上某个微服务的调用响应时间过长或者是不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统更崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的资源在几秒钟之内饱和。这些应用程序还会导致服务之间的延迟增加,备份队列,现成和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统

解决方案(Hystrix)

当依赖的服务发生故障时,对该服务做降级或者是熔断处理,降低对整个应用的影响
官网地址:https://github.com/Netflix/Hystrix/wiki/How-To-Use
目前Hystrix已不再更新,进入维护阶段。当然以后很多优秀的可用的方案,比如Sentinel。受限于篇幅,此文只涉及服务降级与熔断

2.1 能干什么

1)服务降级

服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好的提示
降级触发:程序运行异常、调用超时、服务熔断触发降级、线程池/信号量打满

2)服务熔断

达到设定的最大的服务访问,直接拒绝访问,然后调用服务降级方法并返回友好提示
熔断过程:服务降级->熔断->恢复调用链路

3)服务限流

秒杀等高并发操作,设定并发量

2.1 服务降级
2.1.1 项目搭建与测试

:项目可以单独建立,不适用Eureka,如果需要使用请参考之前的几篇文章,项目中所有的代码均已提交到github

项目搭建,结构如下:
在这里插入图片描述

1)新建cloud-provider-hystrix-payment8001
2)添加pom依赖

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

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-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>

3)修改yml

server:
  port: 8001

eureka:
  client:
    register-with-eureka: true    #表识不向注册中心注册自己
    fetch-registry: true   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
    service-url:
      #设置与eureka server交互的地址查询服务和注册服务都需要依赖这个地址(此处我有修改hosts配置文件)
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

spring:
  application:
    name: cloud-provider-hystrix-payment
 

4)主启动类

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

5)业务代码

@Service
public class PaymentService {

    //成功
    public String paymentInfo_OK(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_OK,id:  "+id+"\t"+"哈哈哈"  ;
    }

    //失败
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 3;
        try { TimeUnit.SECONDS.sleep(timeNumber); }catch (Exception e) {e.printStackTrace();}
        return "线程池:"+Thread.currentThread().getName()+"   paymentInfo_TimeOut,id:  "+id+"\t"+"呜呜呜"+" 耗时(秒)"+timeNumber;
    }

}


@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

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

测试两个接口
先启动两个Eureka服务,然后再启动8001服务,这里我们分别测试两个接口。

在这里插入图片描述
在这里插入图片描述
代码很简单,服务也是可以跑通的。如果在高并发的情况下上述的服务又会如何呢?接下来我们使用Jmater来简单的压测一下,当然在实际的项目中仅仅使用Jmater是远远不够的,此处仅供测试使用

使用压测timeout接口

1)新建一个线程组
在这里插入图片描述
2)在线程组下新建一个http请求
在这里插入图片描述
3)设置协议、服务器名称、端口号、路径
在这里插入图片描述
4)ctrl+s保存之后执行
在这里插入图片描述

5)访问没有休眠的方法发现该方法有了延时
http://localhost:8001/payment/get/info/ok/3
在这里插入图片描述

以上是对服务提供者进行压测,如果有客户端调用8001服务,那结果同样是等待

故障现象和原因

8001同一层次的其他接口服务迪被调用过多,导致tomcat线程中的线程全部被占用,如果再调用8001服务中的其他接口,就需要等待线程被释放。

2.1.2 解决方式
  • 超时导致服务器变慢:超时不再等待
  • 出错(宕机或程序运行出错)出错有兜底

通过设置自身调用超时时间峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作为降级的fallback

1)业务类启用@HystrixCommand

一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法。当方法paymentInfo_Timeout()执行时间超过设定好的之间自动执行降级方法paymentInfo_TimeOutHandler(),当然此处也可以写自己的降级逻辑

@Service
public class PaymentServiceImpl implements IPaymentService {

    @Override
    public String paymentInfo_Ok(Integer id) {
        return "线程池" + Thread.currentThread().getName() + " paymentInfo_Ok id" + id;
    }

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "3000")
    })
    public String paymentInfo_Timeout(Integer id) {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池" + Thread.currentThread().getName() + " id" + id;
    }

    public String paymentInfo_TimeOutHandler(Integer id) {
        return "线程池" + Thread.currentThread().getName() + " paymentInfo_TimeOutHandler方法超时" + id;
    }

2)主启动类激活

添加新注解@EnableCircuitBreaker

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

注:服务端与客户端的降级使用注解以及配置略有差别,当然原理是相同的

3)降级测试

接下来我们直接调用timeout方法来进行测试

2.2 服务熔断

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值