CloudAlibaba-Sentinel

由来

雪崩效应:

微服务架构:
在这里插入图片描述

例如:现在有个高并发的服务系统. 开始每个微服务都是正常的. 现在A服务挂了. 而这时B还在疯狂调用A服务
每个调用都是一个线程. 如果挂了,线程就会等待. 时间下去,请求会积累,资源会被耗尽. 比如:内存,CPU.

基础服务故障,并且导致上层服务故障. 故障在不断的方法.称为雪崩效应
也称为级联失效,级联故障

容错方案

超时

每次请求,设置超时时间,比如:1 000ms. 也就是1 秒钟. 如果请求没响应,则释放掉这个线程

限流

根据每个微服务承受的QPS,设置请求数量的阈值. 超过这个阈值,再有请求,直接拒绝.

仓壁模式

例如,在泰坦尼克号当中. 每个船仓用钢板焊死. 这样的话.进水的船舱不会影响其他的船舱. 当初泰坦尼克号
最大能够容忍俩个船舱进水,而不影响行驶.
在软件中. 每个Controller 有一个独立的线程池. THREAD-POOL-1: coreThread:10. 
而第二个 Controller也有个线程池

断路器

电闸
在这里插入图片描述

监控某个微服务的请求流量. 某个API进行监控. 5 秒 内的错误次数. 达到一定的阈值. 
那么,认为这个API出错了. 就不调用这个API了

在这里插入图片描述

1. 某个服务错误次数过多. 打开断路器.
2. 打开一定时间. 之后再次测试一次. 
3. 这次调用通过,则断路器关闭.
4. 否则,断路器 继续打开

总结:

类比:

1. 超时: 释放够快,就不会死了
2. 限流: 只有一碗的饭量.给再多也不吃
3. 仓壁模式: 不把鸡蛋放在一个篮子里
4. 断路器: 监控 + 开关

Sentinel

轻量级流量控制,熔断降级 Java库

➕依赖

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba.sentinel</artifactId>
</dependency>

注解:没有
配置:没有

验证:

添加 actuator 查看sentinel的各项信息

添加 actuator 依赖. 验证
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
sentinel:
	trantsport:
		dashboard: localhost:8080
# actuator 的配置
management:
  endpoint:
    web:
      exposure:
        include: '*'
启动内容中心.  查看 Sentinel 端点... ...

下载sentinel.jar验证
官网

这是我使用的版本. 和 项目的Maven依赖版本一致

启动流程

java -jar sentinel-dashboard-1.6.3.jar

这里下载的是sentinel的jar. 默认使用的是 8080端口. 自己项目需要指定别的端口

这时候 sentinel-dashboard还是没有数据. 是因为 sentinel是懒加载的. 
需要访问过一次API之后才能在sentinel-dashboard看到数据

在这里插入图片描述

流控规则

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

sentinel 也是饥饿加载. 需要访问一次之后.才能够在sentinel-dashboard看到 资源. 之后点击簇点链路.
就可以看到

资源名:

控制台自动填写我们API的路径

针对来源:

sentinel针对调用者进行限流. 针对不同的微服务进行不同的限流规则. 一般为default.不区分来源.
如果区分来源,是需要扩展支持

阈值:

选择QPS和线程数.
当QPS达到阈值或线程数达到阈值的时候,进行限流操作

流控模式:

1. 直接:访问该API 达到一定次数就限流
2. 关联: 当关联资源达到一定QPS 就限流
3. 链路: 

在这里插入图片描述

当/ actuator/sentinel/ 达到阈值 1时,就限流 /shares/1 这个API
也就是限流自己

关联场景

我们通常修改和查询是一起使用的.
如果 查询或修改速度过快. 那么,修改过快,查询就进行限流.
让修改的 API  能够快速执行
4. 链路: 只记录链路上的流量,当指定链路上流量达到之后,开始限流
@Slf4j
@Controller
@RequestMapping("/userCenter")
public class TestController {
    @GetMapping("test-a")
    public String testA(){
        log.info("test-a,go to common");
        return  userService.common();
    }
    @GetMapping("test-b")
    public String testB(){
        log.info("test-b,go to common");
        return  userService.common();
    }
}
@Slf4j
@Service
public class UserService {
    @SentinelResource("common")
    public String common(){
        log.info("common 访问到了");
        return "common";
    }
}

这里的 @SentinelResource 能够直接出现在簇点链路中
也能够直接访问这个路径

在 sentinel中的簇点链路能够看到对应的访问路径.

在这里插入图片描述

这里为 test-a 做阈值限定. 如果test-a 达到阈值之后. 则限流

流控效果:

5. 快速失败: 直接抛出异常
6. Warm up: 预热. 又一个默认预热因子(3). 比如: 设置的预热时长为10. 
7. 那么, 100/3. 经过10s之后才达到阈值.

在这里插入图片描述

8. 排队等待:均速排队. 让请求以均匀的速度通过. 那么此类型的阈值类型必须设为QPS

降级规则

降级策略:

1. RT 
	平均响应时间.  /shares/1 的API 秒级统计是1ms. 并且在5s内通过的请求大于等于 5次.触发降级

在这里插入图片描述
在这里插入图片描述

RT 的最大时间为4900ms. 可以通过 配置进行修改

在这里插入图片描述

2. 异常比例
3. 异常数
	分钟级别的. 在1分钟内的异常数 大于10 .触发降级. 那么10s之后降级关闭.
	问题在于,如果时间窗口太小. 那么会在此触发降级.

在这里插入图片描述

热点规则

热点参数限流规则. sentinel 默认是不支持这些规则的. 需要进行代码修改

TestController.java
@GetMapping("/test-hot")
    @ResponseBody
    @SentinelResource("hot")
    public String testHot(
            @RequestParam(required = false) String a,
            @RequestParam(required = false) String b
    ){
        log.info("test-Hot,Hot Hot Hot");
        return "Hot Hot Hot";
    }

在这里插入图片描述

第一个参数,访问在1s内的次数超过1次,则限流. 否则不限流

参数例外项:
在这里插入图片描述

参数不是5的话,阈值时1 . 如果是5的话,阈值时1000
热点规则支持对指定的参数限流,指定的参数值限流. 注意: 参数类型必须是基本类型或String.否则不会生效

在这里插入图片描述

系统规则

支持LOAD,RT,线程数,QPS四种

LOAD: 当load1超过阈值时.且并发线程数超过系统容量时. 触发. 建议设置为 CPU核数*2.5
load1 也就是 1分钟的load. load支队Unix系统生效.window配置无效
有三档. load1,load5,load15
系统容量: maxQPS * minRt
maxQPS: 秒级统计出来的最大QPS
minRt: 秒级统计的最小响应时间

在这里插入图片描述

授权规则

在这里插入图片描述

/shares/1 这个API,只允许 test 这个微服务访问

代码配置规则

参考手记

Sentinel与控制台通信

在这里插入图片描述

在这里插入图片描述

控制台配置项

应用连接控制台配置项:
在这里插入图片描述
控制台配置项:
在这里插入图片描述

SentinelAPI 详解

保护的资源:

@GetMapping("/testSentinelApi")
    public String testSentinelApi(@RequestParam(required = false)String a){
        Entry testSentinelApi = null;
        try {
            testSentinelApi = SphU.entry("testSentinelApi");
            // 被保护的业务逻辑
//            xxx
        }
        // 如果保护的资源被限流或降级,就会排除 BlockException
        catch (BlockException e) {
            log.warn("降级或限流了");
            return "限流或降级了";
        }finally {
            if (testSentinelApi != null) {
                testSentinelApi.exit();
            }
        }
        return a;
    }
用Spu定义资源之后.Sentinel进行监控. 计算QPS,RT,线程数,等.出现错误.就会抛出BlockException
在代码中抛出的BlockException是因为,一旦该服务被降级或限流,则会抛出BlockException异常.
而之所以限流或降级,则是因为请求次数超过了阈值.

流控-针对来源:

@GetMapping("/testSentinelApi")
    public String testSentinelApi(@RequestParam(required = false)String a){
        Entry testSentinelApi = null;
        String resourceName = "testSentinelApi";
        // 第一个参数: 资源名称,第二个参数:来源:test-xxx 的微服务
        ContextUtil.enter(resourceName,"test-xxx");
        try {
            testSentinelApi = SphU.entry(resourceName);
            // 被保护的业务逻辑
//            xxx
        }
        // 如果保护的资源被限流或降级,就会排除 BlockException
        catch (BlockException e) {
            log.warn("降级或限流了");
            return "限流或降级了";
        }finally {
            if (testSentinelApi != null) {
                testSentinelApi.exit();
            }
            ContextUtil.exit();
        }
        return a;
    }

在这里插入图片描述

代码和sentinel控制台配合,指定流控规则

核心API:

Sphu: 定义资源,保护资源
Tracer: 想要的异常,进行统计
ContextUtil: 调用来源,.进行统计

@SentinelSource注解

可以区分出是降级还是限流 .fallback和blockHandler.其中fallback是降级. Block是限流

手记

@GetMapping("/testSentinelApi2")
    @SentinelResource(value = "testSentinelApi2",blockHandler = "testSentinelApiBlock")
    public String testSentinelApi2(@RequestParam(required = false)String a){

        // 被保护的业务逻辑
        //   xxx

        Entry testSentinelApi = null;
        String resourceName = "testSentinelApi";
        try {
            testSentinelApi = SphU.entry(resourceName);

        }
        // 如果保护的资源被限流或降级,就会排除 BlockException
        catch (BlockException e) {
            log.warn("降级或限流了");
            return "限流或降级了";
        }finally {
            if (testSentinelApi != null) {
                testSentinelApi.exit();
            }
            ContextUtil.exit();
        }
        return a;
    }

    public String testSentinelApiBlock(@RequestParam(required = false)String a){
        return "限流或降级了";
    }
@SentinelSource 不支持来源. 即 ContextUtil 进行来源设置.
被限流或降级,则是在blockHandler 填写方法名.BlockHandler 所填写的方法.
必须有个 被保护资源方法的参数和相同的返回值.

修改之后的代码:
在这里插入图片描述

@GetMapping("/testSentinelApi2")
    @SentinelResource(value = "testSentinelApi2",blockHandler = "testSentinelApiBlock",fallback = "fallback")
    public String testSentinelApi2(@RequestParam(required = false)String a){

        // 被保护的业务逻辑
        //   xxx

        Entry testSentinelApi = null;
        String resourceName = "testSentinelApi";
        try {
            testSentinelApi = SphU.entry(resourceName);

        }
        // 如果保护的资源被限流或降级,就会排除 BlockException
        catch (BlockException e) {
            log.warn("降级或限流了");
            return "限流或降级了";
        }finally {
            if (testSentinelApi != null) {
                testSentinelApi.exit();
            }
            ContextUtil.exit();
        }
        return a;
    }

    public String testSentinelApiBlock(@RequestParam(required = false)String a){
        return "限流或降级了";
    }

    public String fallback(@RequestParam(required = false)String a){
        return "限流或降级了";
    }

修正:

限流和降级方法在同一个类中. 可以使用blockHandlerClass 更改, 
将 block方法修正为 statistical方法. 使用该属性配置即可实现
fallbackClass 同理

RestTemplate整合Sentinel

@Bean
@LoadBalanced
@SentinelRestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}

配置开关

# 关闭 @SentinelRestTemplate 注解
resttemplate.sentinel.enabled: false
该配置,是使Sentinel的规则不生效. 在代码功能没有最终版之前,不被干扰

相关源码

在这里插入图片描述

@SentinelRestTemplate 注解提供了blockHandler fallback 和对应的class属性.可以指定
对应的异常处理类和异常处理方法.

Feign整合Sentinel

# 为feign 整合 sentinel
fiegn.sentinel.enabled: true

异常应对:

发生了错误,降级或限流. 想要自己处理.做一些逻辑
1. 修正UserCenterFeignClient 接口
2. 在注解添加属性 fallback = X.class
3. X.class Implements UserCenterClient
4. 重写调用的方法即可

在这里插入图片描述
在这里插入图片描述
获得异常:

调用了fallback是一种异常状态. 怎么才能获取到异常的信息??

fallback 和fallbackFactory是俩种状态.二选一

5. 在@FeignClient注解行添加属性:fallbackFactory = X.class
6. 实现 FallbackFactory 
7. 重写 create 方法即可

在这里插入图片描述
在这里插入图片描述

Sentinel 使用总结

在这里插入图片描述

Sentinel 拉模式

每次在服务重启时,规则就消失了. 这次修复这个缺点
手记

Sentinel 推模式

手记
原理简述:

将规则推送到 Nacos或其他配置中心.
Sentinel连接Nacos.获取规则配置.

生产环境Sentinel

1. 规则持久化
	1.1 推模式更佳
2. AHAS:	阿里云推出一款在线生产的控制台.
	2.1 开通地址: https://ahas.console.aliyun.com
	2.2 开通说明: https://help.aliyun.com/document_detail/90323.html

AHAS

实现了规则持久化. 适合在生产环境中使用. 依照 AHAS 开通提示使用

集群流控

在流控和热点规则里.新增规则,有集群选项.

在这里插入图片描述
集群之后,限流降级架构演变:
在这里插入图片描述

TokenServer 处理 来自TokenClient的请求. 根据集群流控规则.判断是否要限流

TokenServer模式

1. 独立模式:

独立部署TokenServer

2. 嵌入模式:

将TokenServer集成在某个微服务上

在这里插入图片描述

Sentinel扩展-错误页优化

Sentinel接口提供了URLBlockHandler 进行错误也的扩展

Sentinel 实现区分来源

Sentinel提供了处理来源的接口: RequestOriginParse
@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        // 从 请求中获取到 origin 参数并返回,
        // origin 就是来源的参数,找不到 就抛异常
        String origin = httpServletRequest.getParameter("origin");
        if (StringUtils.isNotBlank(origin)) {
            throw new IllegalArgumentException(" origin must not blank");
        }
        return origin;
    }
}
将来源,origin 的值拼接在参数中不太合适.这种类型的数据适合放在Header中.

RESTful Url支持

目标

访问 shares/1,shares/2 使用相同的规则. 其中一个流控,另一个也流控. 因为他们都是用了 shares/ 开头的API
Sentinel提供了 UrlCleaner 接口.用来实现
@Slf4j
@Component
public class MyUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String originUrl) {
        log.info("originUrl = {}",originUrl);
        String[] split = originUrl.split("/");
        return Arrays.stream(split)
                .map(string -> {
                    if(NumberUtils.isNumber(string)){
                        return "{number}";
                    }
                    return string;
                })
                .reduce((a,b) -> a+"/"+b)
                .orElse("");
    }
}

Sentinel-透过现象看本质

在这里插入图片描述

当我们默认访问Servlet(每个API,Controller的路径时). 
doFilter(req,resp);
CommonFilter 实现了Filter.对每个请求做操作

配置项总结

手记


版权声明:本文为DXH9701原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。