CloudAlibaba - 센티넬 - 6

유래

눈사태 효과 :

微服务架构:
그림 삽입 설명 여기

例如:现在有个高并发的服务系统. 开始每个微服务都是正常的. 现在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. 断路器: 监控 + 开关

보초

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

➕ 의존

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

注解:没有
配置:没有

확인 :

添加actuator报错

添加 actuator 依赖. 验证
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
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达到阈值的时候,进行限流操作

流控模式:

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

그림 삽입 설명 여기

当/ actuator/sentinel/ 达到阈值 1时,就限流 /shares/1 这个API
3. 链路: 只记录链路上的流量,当指定链路上流量达到之后,开始限流
@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";
    }
}
在 sentinel中的簇点链路能够看到对应的访问路径.

그림 삽입 설명 여기

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

流控效果:

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

그림 삽입 설명 여기

3. 排队等待:均速排队. 让请求以均匀的速度通过. 那么此类型的阈值类型必须设为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 这个微服务访问

코드 구성 규칙

참고 참고

콘솔과 통신 감시

그림 삽입 설명 여기

그림 삽입 설명 여기

콘솔 구성 항목

应用连接控制台配置项:
그림 삽입 설명 여기
控制台配置项:
그림 삽입 설명 여기

자세한 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 센티넬 통합

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

스위치 구성

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

관련 항목

그림 삽입 설명 여기

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

척하기 센티넬 통합

# 为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 方法即可

그림 삽입 설명 여기
그림 삽입 설명 여기

센티넬 사용 요약

그림 삽입 설명 여기

센티넬 풀 모드

每次在服务重启时,规则就消失了. 这次修复这个缺点
주의 사항

센티넬 푸시 모드

주의 사항
原理简述:

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

센티넬 생산 환경

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

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

클러스터 흐름 제어

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

그림 삽입 설명 여기
集群之后,限流降级架构演变:
그림 삽입 설명 여기

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

TokenServer 모드

1. 独立模式:

独立部署TokenServer

2. 嵌入模式:

将TokenServer集成在某个微服务上

그림 삽입 설명 여기

확장 센티넬 - 오류 페이지 최적화

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

구별 소스를 달성 센티넬

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

편안하고 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.对每个请求做操作

구성 항목 요약

주의 사항

게시 36 개 원래 기사 · 원 찬양 1 ·은 50000 +를 볼

추천

출처blog.csdn.net/DXH9701/article/details/103926333