IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Cloud(09)——Hystrix断路器 -> 正文阅读

[Java知识库]Spring Cloud(09)——Hystrix断路器

Spring Cloud(09)——Hystrix断路器

Spring Cloud(07)——Ribbon负载均衡服务的介绍和使用Spring Cloud(08)——Feign服务接口调用中,解决了服务之间调用的问题,现在来处理分布式场景下服务的熔断和降级。

1、背景知识

分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败。

在这里插入图片描述

如上图:上面的请求需要调用A,P,H,I四个服务,如果一切顺利则没有什么问题,关键是如果I服务超时会出现什么情况呢?将会出现服务雪崩效应。

服务雪崩

对个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和C又调用其他的微服务,这就是所谓的‘“扇出效应”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统的崩溃,这就是“雪崩效应”。

2、Hystrix断路器概述

Hystrix简介

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

“断路器”本身是一种开关装置,当某个服务单元发生故障时,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hystrix的设计目的

  • 服务降级
  • 服务熔断
  • 接近实时的监控
  • 。。。。。。

Hystrix GitHub地址

在Hystrix的GitHub官网上有这样一句话:

Hystrix is no longer in active development, and is currently in maintenance mode.

意思是:Hystrix不再处于积极开发中,目前处于维护模式

虽然hystrix组件目前已经停止更新了,但是项目中使用的热度不减少。

3、Hystrix重要概念

服务降级

向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常

哪些情况会发生服务降级:

  • 程序运行异常
  • 超时
  • 服务熔断触发服务降级
  • 线程池/信号量打满也会导致服务降级

服务熔断

类比保险丝,达到最大服务访问量后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。

服务限流

限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。

4、Hystrix使用环境搭建+Jmeter高并发测试

4.1、Hystrix服务提供者环境搭建

1、创建cloud-provider-hystrix-payment8001模块

2、导入pom依赖

<dependencies>
    <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>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>com.cheng.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

</dependencies>

3、编写yml配置文件

server:
  port: 8001

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


#eureka的配置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/   单机版
#      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
    register-with-eureka: true #将自己注册进Eureka Server
    #是否从eurekaServer抓取已有的注册信息,默认为true。单个eureka无所谓,eureka集群必须设置为true,才能配合ribbon使用负载均衡
    fetchregistry: true
  instance:
    instance-id: springcloud-provider-payment8001   #自定义状态信息
    prefer-ip-address: true  #访问路径显示ip地址
info:
  app.name: springcloud-pengcheng
  company.name: wanli

4、创建主启动类

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

5、编写业务类

两个方法:一个直接执行,另一个延迟3秒后再执行

package com.cheng.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {

    public String paymentInfo_Ok(Integer id){
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O哈哈~";

    }

    //延迟3秒钟执行
    public String paymentInfo_TimeOut(Integer id){
        int timeout = 3;
        try {
            TimeUnit.SECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O哈哈~"+"耗时(s):"+timeout;

    }
}

6、编写controller

package com.cheng.springcloud.controller;

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

import javax.annotation.Resource;

@RestController
@Slf4j
public class PaymentController {

    @Resource
    private PaymentService paymentService;

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

    @GetMapping(value = "/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(value = "/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("=====result:"+result);
        return result;
    }
}

7、测试

  1. 启动cloud-eureka-server7001和cloud-eureka-server7002模块
  2. 启动cloud-provider-hystrix-payment8001模块

访问请求:http://localhost:8001//payment/hystrix/ok/1

在这里插入图片描述

访问请求:http://localhost:8001//payment/hystrix/timeout/1 延迟3秒后再执行

在这里插入图片描述

自测OK!

4.2、Jmeter高并发测试

上述在非高并发情形下,还能勉强满足,下面我们使用Jmeter压测测试:

开启Jmeter,用20000个并发访问8001,20000个请求都去访问paymentInfo_TimeOut服务

  1. 在Jmeter中添加一个线程组

在这里插入图片描述

2、在线程组中设置20000个并发,并保存

在这里插入图片描述

3、创建http请求

在这里插入图片描述

4、在请求中设置访问路径,并保存

在这里插入图片描述

5、启动20000个并发进行测试:

在这里插入图片描述

6、查看结果

  1. 启动cloud-eureka-server7001和cloud-eureka-server7002模块
  2. 启动cloud-provider-hystrix-payment8001模块
  3. 启动cloud-consumer-feign-hystrix-order80模块

访问请求:http://localhost:8001//payment/hystrix/ok/1

访问请求: http://localhost/consumer/payment/hystrix/timeout/2

?

在这里插入图片描述

结果显示:

paymentInfo_TimeOut和paymentInfo_Ok服务都不是立即执行,很显然,因为两个服务只在同一个微服务下,由于paymentInfo_TimeOut的并发量太大,把tomcat的默认的工作线程数打满了,没有多余的线程来分解压力和处理,所以paymentInfo_Ok服务的执行会出现延迟现象。

4.3、加入服务消费者进行高并发测试

1、创建cloud-consumer-feign-hystrix-order80模块

2、导入pom依赖

<dependencies>

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

    <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>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>com.cheng.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

</dependencies>

3、编写yml配置文件

server:
  port: 80


eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
      
#设置feign客户端超时时间
ribbon:
  #建立连接所用的时间
  ReadTimeout: 5000
  #建立连接后从服务器读取到可用资源的时间
  ConncetTimeout: 5000

4、创建主启动类

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

5、编写feign接口

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

    @GetMapping(value = "/payment/hystrix/ok/{id}")
    public String paymentInfo_Ok(@PathVariable("id") Integer id);

    @GetMapping(value = "/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);

}

6、编写controller

@RestController
@Slf4j
public class PaymentHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

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

    @GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }
}

7、测试

  1. 启动cloud-eureka-server7001和cloud-eureka-server7002模块
  2. 启动cloud-provider-hystrix-payment8001模块
  3. 启动cloud-consumer-feign-hystrix-order80模块

访问请求:http://localhost:8001//payment/hystrix/ok/1

访问请求: http://localhost/consumer/payment/hystrix/timeout/2

在这里插入图片描述

结果分析:

8001微服务同一层次的其他接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕。80此时调用8001的服务,客户端访问响应缓慢,甚至报超时异常。

总结

在上面的测试中遇到的问题:

  • 超时导致服务器变慢
  • 出错(宕机或程序运行出错)

如何解决:

  • 服务提供者8001超时了,服务消费者80不能一直卡死等待,必须有服务降级
  • 服务提供者8001宕机了,服务消费者80不能一直卡死等待,必须有服务降级
  • 服务提供者8001正常,服务消费者80自己出故障或自我有要求(如自己的等待时间小于服务提供者处理业务的时间),自己处理降级

5、服务降级

5.1、服务提供者8001进行服务降级

设置超时异常:在8001服务提供者提供的服务中,设置自身调用超时时间的峰值(3s),峰值内可以正常运行,超过了峰值就需要有备选的方法处理,作为服务降级的fallback。

1、修改cloud-provider-hystrix-payment8001模块中的服务:

package com.cheng.springcloud.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.concurrent.TimeUnit;

@Service
public class PaymentService {

    public String paymentInfo_Ok(Integer id){
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O哈哈~";

    }

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") //规定正常业务时间峰值为3s
    })
    public String paymentInfo_TimeOut(Integer id){
        int timeout = 5;
        try {
            TimeUnit.SECONDS.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O哈哈~"+"耗时(s):"+timeout;
    }

    public String paymentInfo_TimeOutHandler(@PathVariable("id") Integer id){
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOutHandler,id: "+id+"\t"+"o(╥﹏╥)o";
    }

}

2、主启动类添加注解@EnableCircuitBreaker

3、启动cloud-provider-hystrix-payment8001模块测试

访问请求:http://localhost:8001/payment/hystrix/timeout/1

在这里插入图片描述

服务时间到达峰值后,进行服务降级,执行了备选方法。

把上面的超时异常换成计算异常 int age = 10/0

    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000") //规定正常业务时间峰值为3s
    })
    public String paymentInfo_TimeOut(Integer id){
        int age = 10/0;
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O哈哈~";
    }

    public String paymentInfo_TimeOutHandler(@PathVariable("id") Integer id){
        return "线程池: "+Thread.currentThread().getName()+" paymentInfo_TimeOutHandler,id: "+id+"\t"+"o(╥﹏╥)o";
    }

访问请求:http://localhost:8001/payment/hystrix/timeout/1

在这里插入图片描述

服务执行异常,进行服务降级,同样执行了备选方法

总结:当前服务不可用了,执行服务降级,备选方案都是paymentInfo_TimeOutHandler。

  • 服务提供者8001超时了,服务消费者80不能一直卡死等待,必须有服务降级
  • 服务提供者8001宕机了,服务消费者80不能一直卡死等待,必须有服务降级

5.2、服务消费者80进行服务降级

1、设置cloud-provider-hystrix-payment8001服务提供者中paymentInfo_TimeOut服务延迟时间3s,峰值时间5s,因此8001正常执行。

2、在cloud-consumer-feign-hystrix-order80模块的yml配置文件中开启Hystrix

feign:
  hystrix:
    enabled: true

3、主启动类加注解@EnableHystrix

4、修改服务消费者80模块的controller

package com.cheng.springcloud.controller;

import com.cheng.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
@RestController
@Slf4j
public class PaymentHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;


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

    @GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") //规定正常业务时间峰值为3s
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }

    public String paymentInfo_TimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "来自服务消费者80,对方支付系统繁忙,请稍后再试或检查自己是否出错,o(╥﹏╥)o";
    }

}

我们设置消费者80的峰值时间为1.5s,但提供者8001的执行业务的时间为3s,所以会出现超时异常,然后消费者80就做服务降级,执行备选方案paymentInfo_TimeOutFallbackMethod。

访问请求http://localhost/consumer/payment/hystrix/timeout/1测试:

在这里插入图片描述

执行了备选方案paymentInfo_TimeOutFallbackMethod,符合我们的预期。

现在同样来定义一个计算异常 int age = 10/0;让80自己出故障

然后再访问请求http://localhost/consumer/payment/hystrix/timeout/1测试:

在这里插入图片描述

总结:

服务提供者8001正常,服务消费者80自己出故障或自我有要求(如自己的等待时间小于服务提供者处理业务的时间),自己处理降级

5.3、全局服务降级DefaultProperties

在5.1和5.2中,使用hystrix的时候,都是一个方法配置一个备选方案,并且备选方案和业务逻辑混在一起,这样会使代码过度膨胀,所以可以使用全局通用的方法来进行服务降级。

除了个别重要核心业务有专属的备选方案,其他普通的可以通过@DefaultProperties(defaultFallback ="")统一跳转到统一处理结果页面。

通用的专属的各自分开,避免了代码膨胀,合理减少代码量。

5.3.1、解决代码膨胀问题

设计案例

80客户端自身出现运行时异常,取消上面自定义的fallback方法,使用全局的fallback方法解决。

1、在80客户端的controller上添加全局fallback,并使用@DefaultProperties注解

package com.cheng.springcloud.controller;

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //定义全局fallback方法
public class PaymentHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;


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

    @GetMapping(value = "/consumer/payment/hystrix/timeout/{id}")
//    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutFallbackMethod",commandProperties = {
//            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1500") //规定正常业务时间峰值为3s
//    })
    @HystrixCommand //如果没有指定fallbackMethod,就使用全局定义的fallback方法,如果指定了就用指定的
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        int age = 10/0;
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }

    public String paymentInfo_TimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "来自服务消费者80,对方支付系统繁忙,请稍后再试或检查自己是否出错,o(╥﹏╥)o";
    }

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

2、测试:

  1. 启动cloud-eureka-server7001和cloud-eureka-server7002模块
  2. 启动cloud-provider-hystrix-payment8001模块
  3. cloud-consumer-feign-hystrix-order80模块

访问请求:http://localhost/consumer/payment/hystrix/timeout/1

在这里插入图片描述

根据结果看到,paymentInfo_TimeOut异常后,执行了全局fallback方法。因为paymentInfo_TimeOut没有指定fallbackMethod,所以使用全局定义的fallback方法,如果指定了就用指定的。

至此,就解决了代码膨胀额问题,下面继续解决fallback和业务逻辑混在一起,代码耦合度高的问题。

5.3.2、解决代码耦合度高问题

设计案例

本次案例服务降级处理还是在80客户端实现,假设在80客户端访问8001服务端时,服务端8001突然宕机,使用全局fallback解决。

解决方法:只需要给Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦

1、创建Feign接口的实现类,由实现类统一为接口里的方法进行异常处理:

@Component
public class PaymentFallBackService implements PaymentHystrixService{
    @Override
    public String paymentInfo_Ok(Integer id) {
        return "---PaymentFallBackService类 处理paymentInfo_Ok方法 o(╥﹏╥)o---";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "---PaymentFallBackService类 处理paymentInfo_TimeOut方法 o(╥﹏╥)o---";
    }
}

2、在Feign接口的@FeignClient注解中绑定实现类

@FeignClient增加属性:fallback = PaymentFallBackService.class

3、测试

先测试正常的服务paymentInfo_Ok,该服务没有指定降级处理的fallback方法

访问请求:http://localhost/consumer/payment/hystrix/timeout/1

在这里插入图片描述

然后把8001服务端关闭,模拟服务端宕机,再次访问请求:http://localhost/consumer/payment/hystrix/timeout/1

在这里插入图片描述

全局降级处理生效

虽然服务端宕机了,但是我们做了全局服务降级处理,让客户端在服务端不可用时也会获得提示信息,而不会一直访问耗死服务器。

创建Feign接口的实现类,由实现类统一为接口里的方法进行异常处理,可以解决解决代码膨胀 + 耦合度高的问题

6、服务熔断

6.1、服务熔断机制

熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长,会进行服务降级,进而熔断该节点的微服务调用,快速返回错误的响应信息。当检测到该节点微服务调用相应正常后,恢复调用链路。

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

熔断机制的注解是@HystrixCommand。

微服务架构提出者马丁·福勒熔断器论文:https://martinfowler.com/bliki/CircuitBreaker.html

6.2、服务熔断案例

1、在cloud-provider-hystrix-payment8001模块的业务类中添加:

//===============服务熔断案例===============
@HystrixCommand(fallbackMethod = "paymentCircuitBreak_fallback",commandProperties = {
        @HystrixProperty(name= "circuitBreaker.enabled",value = "true"), //开启断路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率达到多少后跳闸
        //总的意思就是:在10秒内,发出10次请求,若请求失败率达到60%,执行服务熔断
})
public String paymentCircuitBreak(@PathVariable("id") Integer id){
    //当我们在前端传负数时,程序异常,然后调用下面的fallback方法paymentCircuitBreak_fallback()
    if (id<0){
        throw new RuntimeException("id不能为负");
    }
    String simpleUUID = IdUtil.simpleUUID();  //IdUtil.simpleUUID()等价 UUID.randomUUID().toString()
    return Thread.currentThread().getName()+"\t"+"调用成功,流水号为:"+ simpleUUID;
}

//fallback方法
public String paymentCircuitBreak_fallback(@PathVariable("id") Integer id){
    return "id不能为负,请稍后在试o(╥﹏╥)o id:"+id;
}

2、controller调用新添加的方法:

@GetMapping(value = "/payment/circuit/{id}")
public String paymentCircuitBreak(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreak(id);
    log.info("result:"+result);
    return result;
}

3、测试:

  1. 启动cloud-provider-hystrix-payment8001模块

  2. 当我们前端传的id为正数时,正常运行

在这里插入图片描述

  1. 但是当我们传的id为负数时,就会执行fallback方法

在这里插入图片描述

  1. 熔断测试:我们上面规定:在10秒内,发出10次请求,若请求失败率达到60%,执行服务熔断,现在我不断执行id为负的错误请求,在十几次请求异常后,服务端就执行服务熔断

  2. 再传id为正数的请求,看是否能正常访问:

在这里插入图片描述

  1. 但是在时间窗口期后,正常访问次数变多,访问成功率变高后,服务又变得可以使用了。

6.3、关于服务熔断的总结

在这里插入图片描述

摘自上面的马丁·福勒熔断器论文:https://martinfowler.com/bliki/CircuitBreaker.html

熔断类型:

  • 熔断打开:请求不再调用当前的服务,内部设置MTTR(平均故障处理时间),当打开时长达到所设MTTR则进入半熔断(半开)状态
  • 熔断关闭:不会对服务进行熔断
  • 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,熔断关闭

断路器在什么情况下开始起作用:

在这里插入图片描述

涉及到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值

  1. 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的范围就是快照时间窗(时间窗口期),默认为最近的10秒。
  2. 请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断,默认为20。意味在10s内,如果该hystris命令的调用次数不足20次,即使所有的请求都超时或因其他原因失败,断路器都不会打开。
  3. 错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用中,有15次发生了异常,也就是超过50%错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。

断路器打开后:

再有请求调用的时候,将不会调用主逻辑,而是直接调用降级fallback。通过断路器,实现了自动地发现错误并降级逻辑,减少响应延迟的效果。

7、Hystrix工作流程

流程图:

img

流程步骤:

1、包装请求:

可以使用继承HystrixCommand或HystrixObservableCommand来包装业务方法;

2、发起请求:

使用调用Command的execute来执行一个业务方法调用;

Hystrix除了提供了execute方法,另外还提供了3种方来,所有的请求入口:

K       value  = command.execute();``Future<K>   fValue = command.queue();``Observable<K> ohValue = command.observe();     ``//hot observable``Observable<K> ocValue = command.toObservable();  ``//cold observable

如上图所示:

执行同步调用execute方法,会调用queue().get()方法,queue()又会调用toObservable().toBlocking().toFuture();

所以,所有的方法调用都依赖Observable的方法调用,只是取决于是需要同步还是异步调用;

3、缓存处理:

当请求来到后,会判断请求是否启用了缓存(默认是启用的),再判断当前请求是否携带了缓存Key;

如果命中缓存就直接返回;否则进入剩下的逻辑;

4、判断断路器是否打开(熔断):

断路器是Hystrix的设计核心,断路器是实现快速失败的重要手段(断路器打开就直接返回失败);

可以设置断路器打开一定时间后,可以进行尝试进行业务请求(默认是5000毫秒);

5、判断是否进行业务请求(请求是否需要隔离或降级):

是否进行业务请求之前还会根据当前服务处理质量,判断是否需要去请求业务服务;

如果当前服务质量较低(线程池/队列/信号量已满),那么也会直接失败;

线程池或信号量的选择(默认是线程池):

线程池主要优势是客户端隔离和超时设置,但是如果是海量低延迟请求时,频繁的线程切换带来的损耗也是很可观的,这种情况我们就可以使用信号量的策略;

信号量的主要缺点就是不能处理超时,请求发送到客户端后,如果被客户端pending住,那么就需要一直等待;

6、执行业务请求:

当前服务质量较好,那么就会提交请求到业务服务器去;

HystrixObservableCommand.construct()or HystrixCommand.run()

7、健康监测:

根据历史的业务方法执行结果,来统计当前的服务健康指标,为断路器是否熔断等动作作为依据;

8/9、响应失败或成功的处理结果

参考文章:https://www.cnblogs.com/souyoulang/p/11370401.html

8、服务监控hystrixDashboard

除了隔离依赖服务的调用以外,Hystrix还提供了准时的调用监控(Hystrix Dashboard),Hystrix会持续的记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败。Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。

1、创建cloud-consumer-hystrix-dashboard9001模块

2、导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
    </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>com.cheng.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

</dependencies>

3、编写yml配置文件

server:
  port: 9001

4、主启动类

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

5.测试

访问请求:localhost:9001/hystrix

在这里插入图片描述

hystrixDashboard监控平台搭建完成!

现在开始使用hystrixDashboard监控服务提供者被多少请求访问,其中又有多少请求成功,多少请求失败等。

1、确保cloud-provider-hystrix-payment8001模块的依赖中有:

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

2、填坑,在主启动类里面添加下面方法:

/*
* 此配置是为了服务监控而配置,与服务容错本身无关,只是springcloud升级后的坑
* ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream"
* 所以要在自己项目里配置好下面的servlet就可以
* */
 @Bean
public ServletRegistrationBean getServlet(){
    HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
    ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsStreamServlet");
    return registrationBean;
}

3、测试

  1. 启动cloud-eureka-server7001和cloud-eureka-server7002模块
  2. cloud-consumer-hystrix-dashboard9001模块
  3. 启动cloud-provider-hystrix-payment8001模块

在9001hystrixDashboard监控平台中监控8001模块:

在这里插入图片描述

访问8001模块中正常的服务:http://localhost:8001/payment/circuit/1

持续访问多次,查看hystrixDashboard监控界面:

在这里插入图片描述

再访问8001模块中异常的服务: http://localhost:8001/payment/circuit/-1

持续访问多次,查看hystrixDashboard监控界面:

在这里插入图片描述

从结果可以看出,访问出现多次异常后,服务进入熔断机制,熔断器打开。

hystrixDashboard监控图形说明:

在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 16:02:30  更:2021-07-15 16:03:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 20:47:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码