SpringCloud学习笔记 - 服务熔断、服务降级 - Hystrix断路器

发布于:2022-11-11 ⋅ 阅读:(303) ⋅ 点赞:(0)

1. Hystrix 简介

Hystrix 是什么?

在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个
延迟和容错库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。

Hystrix 有什么用?

Hystrix 旨在执行以下操作:

  • 通过第三方客户端库访问(通常通过网络)的依赖项,提供对延迟和故障的保护和控制。
  • 停止复杂分布式系统中的级联故障。
  • 快速失败并快速恢复。
  • 在可能的情况下回退并优雅地降级。
  • 实现近乎实时的监控、警报和操作控制。

Hystrix 重要概念

  1. 服务降级(fallback):
    当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
  2. 服务熔断(break):
    当服务中的服务端接口不稳定,出现频繁超时或错误时,可能会引起服务调用雪崩。您可以对应用开启服务熔断功能,使有故障的服务端及时返回错误,并释放系统资源,提高用户体验和系统性能。
  3. 服务限流(flowlimit):
    限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。

更多概念:https://zhuanlan.zhihu.com/p/74436717

2. Hystrix 服务降级的使用

服务端

  1. 引入pom依赖
<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-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>
            <version>2.2.2.RELEASE</version>
        </dependency>
        <!--mybatis&springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>
        <!--引用自定义通用工具包-->
        <dependency>
            <groupId>com.atguigu.springboot</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
  1. 在主启动类上激活Hystrix:@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}
  1. 使用@HystrixCommand注解配置服务降级的相关信息
//异常访问
    //fallbackMethod:指定回调函数的函数名,当被标注方法出现指定异常时调用
    //commandProperties:HystrixCommand的属性,值为数组类型,用来配置异常信息
    @HystrixCommand(fallbackMethod = "paymentInfo_TIMEOUT_handler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
    })
    public String paymentInfo_TIMEOUT(Integer id){
        int time = 5;
        try {
            TimeUnit.SECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "    payment_timeout, id:" + id + "\t" + "O(∩_∩)O哈哈~,耗时(秒):" + time;
    }

    public String paymentInfo_TIMEOUT_handler(Integer id){
        return "线程池:" + Thread.currentThread().getName() + "    paymentInfo_TIMEOUT_handler, id:" + id + "\t" + "o(╥﹏╥)o,请求出错了";
    }

消费端:通常服务降级的设置端

  1. 在使用openFeign做服务调用时,需要开启openFeign的hystrix支持:在yaml中进行配置
server:
  port: 80
spring:
  application:
    name: hystrix-consumer-order

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka/
feign:
  hystrix:
    enabled: true
  1. 在主启动类上激活Hystrix:@EnableHystrix
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableHystrix
public class ConsumerHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerHystrixMain80.class,args);
    }
}
  1. 使用@HystrixCommand注解配置需要服务降级时的异常相关信息
@RestController
@Slf4j
public class OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String payment_ok(@PathVariable("id") Integer id){
        String info_ok = paymentHystrixService.payment_ok(id);
        System.out.println("info_ok" + info_ok);
        return info_ok;
    }

    /*@GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String payment_timeout(@PathVariable("id") Integer id){
        String info_timeout = paymentHystrixService.payment_timeout(id);
        System.out.println("info_timeout" + info_timeout);
        return info_timeout;
    }*/
    @HystrixCommand(fallbackMethod = "payment_timeout_handler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "5000")
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String payment_timeout(@PathVariable("id") Integer id){
        String info_timeout = paymentHystrixService.payment_timeout(id);
        System.out.println("info_timeout" + info_timeout);
        return info_timeout;
    };

    public String payment_timeout_handler(Integer id){
        return "线程池:" + Thread.currentThread().getName() + "    payment_timeout_handler, id:" + id + "\t" + "o(╥﹏╥)o,请求出错了";
    }
}

3. hystrix优化配置

1. 使用注解配置通用fallback回调函数:@DefaultProperties

指定默认的服务降级回调方法,当方法出错且没有指定特有的回调方法时会使用默认方法。这样可以防止为每个方法指定fallback函数而导致代码膨胀

  1. 在controller上指定默认方法
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_timeout_handler")
public class OrderHystrixController {
    ......
}
  1. 使用注解@HystrixCommand指定启用服务降级的业务方法,但不单独指定处理方法。
@HystrixCommand
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String payment_timeout(@PathVariable("id") Integer id){
        String info_timeout = paymentHystrixService.payment_timeout(id);
        System.out.println("info_timeout" + info_timeout);
        return info_timeout;
    };
  1. 创建fallback处理方法
    //defaultFallback
    public String payment_Global_FallbackMethod(){
        return "payment_Global_FallbackMethod: 系统出现错误";
    }

2. 将业务处理逻辑和fallback函数解耦
解决代码混乱和耦合度高的问题

  1. 创建一个实现类,实现openFeign的服务调用接口,这里的处理逻辑之后就是我们服务报错时回调函数的处理逻辑
@Service
public class PaymentHystrixFallbackServiceImpl implements PaymentHystrixService {
    @Override
    public String payment_ok(Integer id) {
        return "payment_ok :default_global_fall_back-payment_ok o(╥﹏╥)o";
    }

    @Override
    public String payment_timeout(Integer id) {
        return "payment_ok :default_global_fall_back-payment_timeout o(╥﹏╥)o";
    }
}
  1. 为openFegin服务调用接口指定fallback的逻辑处理类
@FeignClient(value = "cloud-provider-hystrix-payment" ,fallback = PaymentHystrixFallbackServiceImpl.class)
public interface PaymentHystrixService {

    @GetMapping("/payment/hystrix/ok/{id}")
    public String payment_ok(@PathVariable("id") Integer id);
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String payment_timeout(@PathVariable("id") Integer id);
}

4. 服务熔断

在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机
在这里插入图片描述

由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断

熔断器原理

熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。比如带空开的公牛插线板,它规定了一个上限瓦数,当这个插线板上的电器发生短路瓦数突然超标,空开就会断开,通过牺牲这个插线板上的电器正常使用,而保证整个房间的电路是通畅的。
而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误

在这种模式下,服务调用方为每一个调用服务 (调用路径) 维护一个状态机,在这个状态机中有三个状态:

  • 关闭 (Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态

  • 打开 (Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复

  • 半打开 (Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到关闭状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮

在这里插入图片描述

服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回

熔断的配置和使用

在服务降级的基础下,配置:还是使用@HystrixCommand注解进行配置

    //服务熔断
    //从熔断服务启动开始,十秒内接收到10次请求(如果不够10次就算请求全部失败也不会触发断路器)计算一次“错误百分比”
    //“错误百分比”大于60%时切换为open状态,open后,在10秒空窗期内不会放行请求
    //10秒后,放行一个请求确定是否应该关闭断路器。
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 触发阈值:断路器请求量阈值
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 断路器错误百分比阈值
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 短路器打开后的空窗期时间设置
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        if(id < 0){
            throw new RuntimeException("*****id不能为负数");
        }
        UUID uuid = UUID.randomUUID();
        return Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + uuid;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id不能为负数,请稍后再试!";
    }

更多配置属性信息见HystrixCommandProperties

在这里插入图片描述

5. 服务监控HystrixDashboard

1. pom引用

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</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>

2. 主启动类启用监控服务

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

3. 在需要被监控的服务主启动类中配置servlet

@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
    /**
     *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
     *只要在自己的项目里配置上下面的servlet就可以了
     *否则,Unable to connect to Command Metric Stream 404
     */
    @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;
    }
}

4. 启动监控页面,输入需要进行监控的服务地址

启动监控页面:http://localhost:9001/hystrix
输入需要监控的服务器流地址:http://localhost:8001/hystrix.stream
在这里插入图片描述

监控页面:
在这里插入图片描述

页面内容解释:
在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到