7-1、hystrix-1:基础知识

一、雪崩

微服务中,⼀个请求可能需要多个微服务接⼝才能实现,会形成复杂的调⽤链路。

在这里插入图片描述
解释说明:

在这里插入图片描述

  • 扇⼊:代表着该微服务被调⽤的次数,扇⼊⼤,说明该模块复⽤性好
  • 扇出:该微服务调⽤其他微服务的个数,扇出⼤,说明业务逻辑复杂

扇⼊⼤是⼀个好事,扇出⼤不⼀定是好事

在微服务架构中,⼀个应⽤可能会由多个微服务组成,微服务之间的数据交互通过远程过程调⽤完成。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调⽤响应时间过⻓或者不可⽤,对微服务A的调⽤就会占⽤越来越多的系统资源,进⽽引起系统崩溃,所谓的“雪崩效应”。

如图中所示,最下游简历微服务响应时间过⻓,⼤量请求阻塞,⼤量线程不会释放,会导致服务器资源耗尽,最终导致上游服务甚⾄整个系统瘫痪。

二、雪崩效应解决⽅案

应对微服务中的雪崩效应的三种常用手段

1、服务熔断

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

注意:
1)服务熔断重点在“”,切断对下游服务的调⽤
2)服务熔断和服务降级往往是⼀起使⽤的,Hystrix就是这样。

2、服务降级

通俗讲就是整体资源不够⽤了,先将⼀些不是特别重要的服务停掉(调⽤我的时候,给你返回⼀个预留的值,也叫做兜底数据),待渡过难关⾼峰过去,再把那些服务打开。

服务降级⼀般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调⽤,此刻客户端可以⾃⼰准备⼀个本地的fallback回调,返回⼀个缺省值,这样做,虽然服务⽔平下降,但好⽍可⽤,⽐直接挂掉要强。

3、服务限流

服务降级是当服务出问题或者影响到核⼼流程的性能时,暂时将服务屏蔽掉,待⾼峰或者问题解决后再打开;但是有些场景并不能⽤服务降级来解决,⽐如秒杀业务。
这样的核⼼功能,这个时候可以结合服务限流来限制这些场景的并发/请求量限流措施也很多,⽐如

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

三、Hystrix简介

在这里插入图片描述

四、Hystrix的应用

目的:简历微服务长时间没有响应,服务消费者—>⾃动投递微服务快速失败给用户提示
在这里插入图片描述

1、pom添加jar包

<!--熔断器Hystrix-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2、服务消费者⼯程的启动类中添加熔断器开启注解

@EnableCircuitBreaker // 开启熔断

在这里插入图片描述

3、定义服务降级处理⽅法。

在业务⽅法上使⽤@HystrixCommand的fallbackMethod属性关联到服务降级处理⽅法

   /**
     * 此方法在【findResumeOpenState】基础上改造,
     * todo 添加Hystrix熔断配置 @HystrixCommand
     * 1、Ribbon:负载均衡
     * 2、Hystrix:熔断
     * @param userId
     * @return
     */
    @HystrixCommand(
            // HystrixCommandProperties 熔断的⼀些细节属性配置
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            }
    )
    @GetMapping("/checkStateTimeOutV1/{userId}")
    public Integer findResumeOpenStateTimeOutV1(@PathVariable Long userId) {
        String serviceName = "LAGOU-SERVICE-RESUME";
        String url = "http://" + serviceName + "/resume/openstate/" + userId;
        System.out.println("===============>>>从EurekaServer集群获取服务实例拼接的url:" + url);
        // 调用远程服务—> 简历微服务接口
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

4、lagou-service-resume-8081:web方法休眠3秒

在这里插入图片描述

5、测试

添加熔断之前

即便是调用需要的时间长,但是等待之后,能够正常返回
在这里插入图片描述

添加熔断之后

会返回异常
在这里插入图片描述

6、优化

添加熔断之后,直接返回异常信息给前端不太友好。

可以后台定义,返回兜底数据.

/**
     * 此方法在【findResumeOpenStateTimeOutV1】基础上改造,TODO 添加兜底数据的返回,避免直接把后台错误信息返回给前台
     * @param userId
     * @return
     */
    @GetMapping("/checkStateTimeOutFallBackV1/{userId}")
    @HystrixCommand(
            // HystrixCommandProperties 熔断的⼀些细节属性配置
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            },
            fallbackMethod = "myFallBack" // 回退方法
    )
    public Integer findResumeOpenStateTimeOutFallBackV1(@PathVariable Long userId) {
        // todo 添加ribbon负载均衡之后的做法
        String serviceName = "LAGOU-SERVICE-RESUME";
        String url = "http://" + serviceName + "/resume/openstate/" + userId;
        System.out.println("===============>>>从EurekaServer集群获取服务实例拼接的url:" + url);
        // 调用远程服务—> 简历微服务接口
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

    public Integer myFallBack(@PathVariable Long userId) {
        return -23333;// 兜底数据
    }

在这里插入图片描述

注意:
降级(兜底)⽅法必须和被降级⽅法相同的⽅法签名(相同参数列表、相同返回值)


测试结果如下:

可以看到,访问被熔断服务的时候,不再返回异常信息,而是返回了我们自定义的兜底函数的数据。
在这里插入图片描述

五、Hystrix舱壁模式(线程池隔离策略)

(一)存在的问题

在这里插入图片描述

默认情况下,所有添加了 @HystrixCommand注解的方法,都使用同一个线程池(默认10个)。这些方法的请求如果超过了10个,其他请求就需要等待或者拒绝连接。这样是有问题的。

如果不进⾏任何设置,所有熔断⽅法使⽤⼀个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法A的请求把10个线程都⽤了,⽅法2请求处理的时候压根都没法去访问B,因为没有线程可⽤,并不是B服务不可⽤。

(二)解决方案

为了避免问题服务请求过多导致正常服务⽆法访问,Hystrix 不是采⽤增加线程数,⽽是单独的为每⼀个控制⽅法创建⼀个线程池的⽅式,这种模式叫做“舱壁模式",也是线程隔离的⼿段。

即,每个@HystrixCommand方法,单独一个线程池
在这里插入图片描述

1、配置

在代码中进行调整(在业务方法的@HystrixCommand中添加配置)

// 添加线程池的配置
// 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
threadPoolKey = "checkStateTimeOut",
// 线程池细节属性配置
threadPoolProperties = {
		@HystrixProperty(name = "coreSize",value = "1"),// 线程数
		@HystrixProperty(name = "maxQueueSize",value = "20"), // 等待队列⻓度
},
// *************************************** hystrix :  舱壁模式【开始】 ***************************************
    /**
     * 此方法在【findResumeOpenStateTimeOutV1】基础上改造,
     * todo 添加Hystrix的舱壁模式配置
     * 1、Ribbon:负载均衡
     * 2、Hystrix:熔断
     * @param userId
     * @return
     */
    @HystrixCommand(
            // 添加线程池的配置
            // 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
            threadPoolKey = "checkStateTimeOutV2",
            // 线程池细节属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize",value = "1"),// 线程数
                    @HystrixProperty(name = "maxQueueSize",value = "20"), // 等待队列⻓度
            },
            // HystrixCommandProperties 熔断的⼀些细节属性配置
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            }
    )
    @GetMapping("/checkStateTimeOutV2/{userId}")
    public Integer findResumeOpenStateTimeOutV2(@PathVariable Long userId) {
        String serviceName = "LAGOU-SERVICE-RESUME";
        String url = "http://" + serviceName + "/resume/openstate/" + userId;
        System.out.println("===============>>>从EurekaServer集群获取服务实例拼接的url:" + url);
        // 调用远程服务—> 简历微服务接口
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }

    /**
     * 此方法在【findResumeOpenStateTimeOutFallBackV1】基础上改造,
     * TODO  添加Hystrix的舱壁模式配置
     * @param userId
     * @return
     */
    @GetMapping("/checkStateTimeOutFallBackV2/{userId}")
    @HystrixCommand(
            // 添加线程池的配置
            // 线程池标识,要保持唯⼀,不唯⼀的话就共⽤了
            threadPoolKey = "checkStateTimeOutFallBackV2",
            // 线程池细节属性配置
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize",value = "2"),// 线程数
                    @HystrixProperty(name = "maxQueueSize",value = "20"), // 等待队列⻓度
            },
            // HystrixCommandProperties 熔断的⼀些细节属性配置
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
            },
            fallbackMethod = "myFallBack" // 回退方法
    )
    public Integer findResumeOpenStateTimeOutFallBackV2(@PathVariable Long userId) {
        // todo 添加ribbon负载均衡之后的做法
        String serviceName = "LAGOU-SERVICE-RESUME";
        String url = "http://" + serviceName + "/resume/openstate/" + userId;
        System.out.println("===============>>>从EurekaServer集群获取服务实例拼接的url:" + url);
        // 调用远程服务—> 简历微服务接口
        Integer forObject = restTemplate.getForObject(url, Integer.class);
        return forObject;
    }
// *************************************** hystrix :  舱壁模式【结束】 ***************************************

在这里插入图片描述

2、测试

使用postman的批量测试功能进行测试,批量跑完之后,观察线程情况
在这里插入图片描述

配置线程池之前:

对3个方法,分别请求10次:
在这里插入图片描述

3个方法中,有两个加了@HystrixCommand注解,应该是有20个线程,但是,实际只有10个线程

配置线程池之后:

对3个方法,分别请求10次:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值