【SpringCloud】Sentinel 之隔离与降级

一、上集回顾

上级文章地址:【SpringCloud】Sentinel 之流量控制_面向架构编程的博客-CSDN博客

上一篇文章我们讲解了Sentinel 流量控制、流控效果、热点参数限流的用法,统称为限流,它是一种预防措施,可以尽量避免因高并发而引起的服务故障

但是,服务还会因为其它原因而故障,生产环境上的情况多种多样,有着一定的不可预见性,所以,我们要将这些故障控制在一定范围,要避免雪崩的发生,就要靠线程隔离(舱壁模式)熔断降级手段了。

回顾-线程隔离与熔断降级

文章地址:【SpringCloud】Sentinel的基础概念及使用_面向架构编程的博客-CSDN博客

不管是线程隔离还是熔断降级,都是客户端(调用方)的保护。需要在调用方发起远程调用时线程隔离、或者服务熔断

微服务远程调用都是基于Feign(OpenFeign)来完成的,因此我们需要将OpenFeign与Sentinel整合在OpenFeign里面实现线程隔离和服务熔断

二、FeignClient整合Sentinel

SpringCloud中,微服务调用都是通过Feign来实现的,因此做客户端保护必须整合OpenFeign和Sentinel

1.修改配置,开启sentinel功能

修改order-serviceapplication.yml文件,开启Feign的Sentinel功能

feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持

2.编写失败降级逻辑

业务失败后,不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。

给FeignClient编写失败后的降级逻辑

①方式一:FallbackClass,无法对远程调用的异常做处理(一般不用)
②方式二: FallbackFactory,可以对远程调用的异常做处理(主流!!!)

注意:这里将的FeignClient其实就是@FeignClient注解,用于客户端服务发现。

失败降级处理流程

(1)在feing-api项目中定义类,实现FallbackFactory接口

@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {
        return new UserClient() {
            @Override
            public User findById(Long id) {
                log.error("查询用于异常",throwable);
                // 返回一个空的用户对象
                return new User();
            }
        };
    }
}

(2)在DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean

public class DefaultFeignConfiguration {

    @Bean
    public Logger.Level logLevel() {
        return Logger.Level.BASIC;
    }

    /**
     * sentinel-失败降级
     * 将UserClientFallbackFactory注册为一个Bean
     */
    @Bean
    public UserClientFallbackFactory userClientFallbackFactory() {
        return new UserClientFallbackFactory();
    }
}

(3)在feing-api项目中的UserClient接口中使用UserClientFallbackFactory

@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

(4)运行

打开Sentinel控制台,访问网址:localhost:8080/order/101

可以看到新的簇点链路:

小结

Sentinel支持的雪崩解决方案

  • 线程隔离(仓壁模式)

  • 降级熔断

Feign整合Sentinel的步骤

  • 在application.yml中配置:feign.sentienl.enable=true

  • 给FeignClient编写FallbackFactory注册为Bean

  • 将FallbackFactory配置到FeignClient

三、线程隔离(舱壁模式)

线程隔离的实现方式(2种)

  • 线程池隔离

  • 信号量隔离(Sentinel默认采用)

两者图解如下:

线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果

信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求。

两者的优缺点

线程池隔离

优点: 支持主动超时、支持异步调用
缺点:线程额外开销大
场景:低扇出

信号量隔离

优点: 轻量级、无额外的开销
缺点:不支持主动超时、不支持异步调用
场景:高频调用、高扇出

sentinel的线程隔离(采用信号量模式)

配置说明

在 "流控"按钮里面,之前选择的是QPS,当我们选择的是"线程数" 时,其实就是在指定 "信号量"的阈值

线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现线程隔离(舱壁模式)。

案例需求

给 order-service服务中的UserClient的查询用户接口设置流控规则,线程数不能超过 2。然后利用jemeter测试。

具体实现

1.配置隔离规则

选择feign接口后面的流控按钮:

填写表单:

2.Jmeter压测

启动Jmeter

一次发生10个请求,有较大概率并发线程数超过2,而超出的请求会走之前定义的失败降级逻辑

发现虽然结果都是通过了,不过部分请求得到的响应是降级返回的null信息

Sentinel控制台的情况:

四、熔断降级

1.什么是熔断降级?

熔断降级是解决雪崩问题的重要手段!

其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。

断路器控制熔断放行是通过状态机来完成的!

2.什么是状态机?

状态机是为断路器提供熔断放行的依据。它有3种状态,断路器会根据不同的状态执行相应的策略!

状态机的3种状态

  • closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态

  • open:打开状态,服务调用被熔断,访问被熔断服务的请求都会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态

  • half-open:半开状态,放行一次请求根据执行结果来判断接下来的操作。

  • 请求成功:则切换到closed状态(放行)

  • 请求失败:则切换到open状态(熔断)

图解状态机工作原理

3.断路器熔断策略(三种)

断路器从close进入open,需要判断服务有没有触发熔断的条件,而该条件的判断是依据熔断策略(3种)——慢调用异常比例异常数

慢调用

业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。

配置说明

RT超过500ms的调用是慢调用,统计最近10s内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。

案例需求

需求:给 UserClient的查询用户接口设置降级规则,慢调用的RT阈值为50ms,统计时间为1秒,最小请求数量为2,失败阈值比例为0.4,熔断时长为5

具体实现

(1)设置慢调用

由于我们这里都是测试数据,基本上不可能出现慢调用,所以我们这里要用sleep来模拟一下。

修改user-service中的/user/{id}这个接口的业务。通过休眠模拟一个延迟时间。

@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,@RequestHeader(value = "Truth", required = false) String truth) {
    // 为了测试熔断,模拟其发生慢调用!
    if (id == 1) {
        try {
            Thread.sleep(60); // 60ms
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    return userService.queryById(id);
}

重启user-service模块

orderId=101的订单,关联的是id为1的用户,调用时长肯定会>60ms(这里是112ms)

orderId=102的订单,关联的是id为2的用户,调用时长为非常短<50ms

(2)设置熔断规则

配置规则

超过50ms的请求都会被认为是慢请求

(3)测试

在浏览器访问:http://localhost:8088/order/101快速刷新n次,可以发现

触发了熔断,请求时长缩短至十几ms,快速失败了,并且走降级逻辑,返回的null

再访问(5秒内)http://localhost:8088/order/102,竟然也被熔断了!!!

异常比例、异常数

含义

统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断

配置说明

解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常比例不低于0.4,则触发熔断。

解读:统计最近1000ms内的请求,如果请求量超过10次,并且异常次数不低于2次,则触发熔断。

异常比例、异常数的使用方式与上述讲的 慢调用类似!这里就不用案例来说明了!

下一节——Sentinel授权规则与规则持久化

posted @ 2023-02-02 23:57  金鳞踏雨  阅读(126)  评论(0编辑  收藏  举报  来源