• Sentinel(笔记)


    一、Sentinel与雪崩问题

    1.1 雪崩问题与解决方案

    • 服务A依赖服务B
    • 服务B故障,导致A阻塞
    • 导致A不会释放tomcat连接
    • 长期则导致A资源耗尽,A故障
    • 依赖A的服务同理

    于是类似滚雪球,故障的范围越来越大。

    处理思路:

    1. 超时处理:设定超时时间,请求超过一定时间没有响应则返回错误信息,不会无休止等待。该方式为缓解作用。
    2. 舱壁模式:限定每个业务能使用的线程数目,避免耗尽整个tomcat的资源,因此也就线程隔离。浪费了一些资源。
    3. 熔断降级:有断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
    4. 流量控制:限制业务访问的QPS,避免服务因流量的突增而故障。QPS:每秒钟请求的数量。预防服务故障。

    Sentinel来控制QPS。

    1.2 Sentinel 与 Hystrix

    • 线程池隔离:给每一个独立的业务都会有个独立的线程池,通过单独的线程池来限制。
    • 信号量隔离:统计当前业务使用了几个线程,并限制业务只能使用几个线程,一但超过就被限制。
    • 基于慢调用比例:依据业务大多数情况下处理得是否很慢,来判断是否熔断。如果该业务大多数情况下都很忙,则认为会拖垮整个服务,因此在请求很多时熔断它。
    • 流量整形:指让突发流量变为匀速流量,让服务处理起来较轻松。
    SentinelHystrix
    隔离策略信号量隔离线程池隔离/信号量隔离
    熔断降级策略基于慢调用比例或异常比例基于失败比率
    实时指标实现滑动窗口滑动窗口(基于RxJava)
    规则配置支持多种数据源支持多种数据源
    扩展性多个扩展点插件的形式
    基于注解的支持支持支持
    限流基于QPS,支持基于调用关系的限流有限的支持
    流量整形支持慢启动、匀速排队模式不支持
    系统自适应保护支持不支持
    控制台开箱即用,可配置规则、查看秒级监控、机器发现等支持查看,不支持动态配置,不完善
    常见框架的适配Servlet、Spring Cloud、Dubbo、gRPC等Servlet、Spring Cloud Netflix

    1.3 安装部署

    alibaba/Sentinel
    下载如下文件即可。
    在这里插入图片描述
    下载完成解压后,就可以直接用运行即可
    账号密码都是sentinel

    java -jar sentinel-dashboard-1.8.1.jar

    1.4 整合

    在SpringBoot的消费中,导入如下依赖

    		<dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4

    设置yml文件

    spring:
        sentinel:
          transport:
            dashboard: localhost:8080
    
    • 1
    • 2
    • 3
    • 4

    如果启动不了或者显示循环依赖,请参照下面版本进行调整
    版本说明

    如果还是不行,添加如下语句,允许循环依赖

    spring:
      main:
        allow-circular-references: true
    
    • 1
    • 2
    • 3

    配置完成后,我们使用消费者多次访问提供者后,再查看Sentinel,可以看到如下结果。
    在这里插入图片描述

    在这里插入图片描述

    1.5 限流规则

    • 簇点链路:项目内的调用链路。比如controller->service->mapper,就是一个链路。链路中被检控的每个接口就是一个资源,比如默认Sentinel会监控SpringMVC中的每一个端点——Controller中的方法。

    流量控制、熔断等都是针对簇点链路中的资源拉设置的。

    用设置流控,单机阈值为5,然后用Jemeter测试
    在这里插入图片描述
    在这里插入图片描述
    结果:
    在这里插入图片描述
    在这里插入图片描述

    1.5.1 流控模式

    • 直接:统计当前资源的请求,出发阈值时,对当前资源直接限流,默认模式
    • 关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流
    • 链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

    1.5.2 关联

    一般用于竞争关系,一个优先级较高,一优先级低。但优先级高的触发阈值时,优先级低的被限制。

    我们为消费者控制层新增两个方法

     	@GetMapping("/query")
        public String queryOrder(){
            return "查询";
        }
    
        @GetMapping("/update")
        public String updateOrder(){
            return "更新";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    访问一下,后
    在这里插入图片描述
    我们想让update达到一定次数后,query被限流。
    我们想要给谁限流,就给谁加规则。
    在这里插入图片描述

    现在进行测试
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    1.5.3 链路

    • 当达到阈值时,限制某一条链路。可以理解为,某一个方法被很多模块调用,可以通过限制被哪一个模块调用的数量,来保证他模块顺利调用。
    • 有查询订单和创建订单业务,两者都要查询商品。针对从查询订单进入到查询商品的请求统计,并设置限流。

    在Service层添加一个查询的方法

    	/**
         * 手动标记该方法为资源
         * */
        @SentinelResource("goods")
        @Override
        public void queryGoods() {
            System.out.println("查询商品");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    为控制层添加

    	@GetMapping("/query")
        public String queryOrder(){
            orderService.queryGoods();
            return "查询";
        }
    
        @GetMapping("/save")
        public String saveOrder(){
            orderService.queryGoods();
            return "新增完成";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    修改

        sentinel:
          transport:
            dashboard: localhost:8080
          web-context-unify: false #关闭context整合,Sentinel默认会把Controller的方法做context。导致链路模式的流量失效
    
    • 1
    • 2
    • 3
    • 4

    接下来访问一下,
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    分别请求
    在这里插入图片描述

    可以看到,query被限制,但是update没有被限制
    在这里插入图片描述
    query
    在这里插入图片描述

    update
    在这里插入图片描述

    1.5.4 控制效果

    • 快速失败:达到阈值后,请求被立即拒绝,并抛出FlowException异常,为默认处理方式。
    • 预热模式 warm up:对超出阈值的请求,同样是拒绝跑出异常。但它的阈值会动态变化,从一个较小值逐渐增加到最大阈值。
      • 请求初始阈值 = threshold(最大阈值)/coldFacotor(冷启动因子),持续指定时长后,逐渐提高到threshold值。coldFacotor默认是3,即刚开始阈值为1/3*threshold,在设定的预热时间过后,会变为threshold。
    • 排队等待:让所有的请求进入一个队列中,然后按照阈值允许的时间间隔依次执行,后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
      • 比如:QPS = 5(每秒最多处理5个),意味着每200ms处理队列中的一个请求。当timeout=2000,即最大等待时间为2000ms,超过求拒绝并抛出异常。
      • 当前面有10个请求,且第一个刚开始处理时,第11个会直接被拒绝。
      • 流量整形,让到达的流量稳定。

    1.5.5 热点参数限流

    分别统计参数值相同的请求,判断是否超过阈值。

    • 参数索引:统计第几个参数
    • 单机阈值:就是最多多少个(集群的话就是均摊阈值,分摊到每个多少)
    • 统计窗口时长:每秒内单机阈值不能超过
      在这里插入图片描述

    参数例外项,也即是会有一些参数需要单独配置,可能它们需要高于或者低于其他参数。
    如果没有高级选项,从左侧的热点规则一栏进去新增。
    在这里插入图片描述

    • 注意:热点参数限流对默认的SpringMVC资源无效,我们需要手动标记。方式同链路一样,打一个注解即可。

    1.6 隔离与降级

    1.6.1 Feign整合Sentinel

    首先在配置中打开feign的sentinel功能。

    feign:
      client:
        config:
          user-service:
            logger-level: BASIC
      sentinel:
        enabled: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    随后编辑要降级的逻辑,有两种方式

    • 方式一:FallbackClass,无法对远程调用做异常处理
    • 方式一:FallbackFactory,可以对远程调用的异常做处理
    package com.config;
    
    import com.model.User;
    import com.service.UserClient;
    import org.springframework.cloud.openfeign.FallbackFactory;
    
    
    public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
        @Override
        public UserClient create(Throwable cause) {
            return new UserClient() {
                @Override
                public User getUserById(Long id) {
                    System.err.println("查询失败:" + cause);
                    return new User();
                }
            };
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    package com.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class Config {
        @Bean
        public UserClientFallbackFactory userClientFallbackFactory(){
            return new UserClientFallbackFactory();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    重启后,运行,访问,就可以看到

    接下来就可以进行配置了
    在这里插入图片描述

    1.6.2 线程隔离

    如下图设置即可,就完成了舱壁模式。
    在这里插入图片描述

    1.6.3 熔断降级

    success
    达到失败阈值
    快速失败
    熔断时间结束
    失败则打开断路器
    成功则关闭断路器
    Closed
    Open
    Half-Open
    尝试放行一次请求

    1.6.3.1 熔断策略

    有三种:

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

      • RT:最大响应时长,超过就算慢调用
      • 比例阈值:慢调用比例超过了0.5,就算熔断
      • 熔断时长:每次熔断持续5s
      • 最小请求数:每次统计最小要有5个
      • 统计时长:统计每1000ms内的请求
      • 即在统计时长内且超过最小请求次数的所有请求,会被用来计算比例阈值。
        在这里插入图片描述
    • 异常比例:统计指定时间内的调用,如果调用次数超过指定请求数目,并且出现异常比例达到比例阈值,则触发熔断。
      在这里插入图片描述

    • 异常数:统计指定时间内的调用,如果调用次数超过指定请求数目,并且出现异常数目超过指定异常数目,则触发熔断。
      在这里插入图片描述

    二、Sentinel 授权规则

    为什么需要?避免服务地址暴露,被访问者绕过网关直接访问。
    白名单:来源(origin)在白名单的调用者允许访问
    黑名单:来源(origin)在黑名单的调用者不允许访问
    在这里插入图片描述

    2.1 授权规则

    Setinel 通过 RequestOriginParser 这个接口的parseOrigin来获取请求来源,但无论是走网关还是直接访问默认都是default,无法区分。

    因此我们需要自己去实现它。

    public interface RequestOriginParser{
    	/**
    	* 从请求request对象中获取origin,获取方式自定义
    	*/
    	String parseOrigin(HttpServletRequest request);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    为消费者实现该方法。

    package com.config;
    
    import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
    import com.alibaba.nacos.common.utils.StringUtils;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    public class HeaderOriginParser implements RequestOriginParser {
    
        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            /**
             * 如果浏览器或者网关获取的origin头不一样,那么就可以区分
             * */
            String origin = httpServletRequest.getHeader("origin");
            if(StringUtils.isEmpty(origin)){
                return "blank";
            }
            return origin;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    为网关添加过滤器,将我们上面的来源加上

    server:
      port: 10010
    spring:
      application:
        name: gateway
      cloud:
        nacos:
          server-addr: localhost:8848
        gateway:
          routes: # 网关路由配置
            - id: user-service #路由id,自定义,但需要唯一
              uri: lb://user-service #路由目标地址,lb即负载均衡,后面跟着服务名称
              # uri: http://127.0.0.1:8001 # 也可以这样写,但是地址固定,不推荐
              predicates: # 路由断言,即判断是否符合要求
                - Path=/user/** # 这个规则是,只要以/user/开头,就算符合要求
            - id: order-service
              uri: lb://order-service
              predicates:
                - Path=/order/**
          default-filters:
            - AddRequestHeader=origin,gateway # , 是等于的意思
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述
    重启后
    在这里插入图片描述
    在这里插入图片描述

    2.2 自定义异常

    所有异常默认的都是限流异常,我们接下来自定义异常,避免抛异常时不知道是哪里出错

    public interface BlockExceptionhanlder{
    	/**
    	* 处理请求被限流、降级、授权拦截时抛出的异常
    	*/
    	void handle(HttpServletRequest request,HttpServletResponse response,BlockException e) throws Exception;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    BlockException包含很多子类

    异常说明
    FlowException限流异常
    ParamFlowExcetion热点参数限流异常
    DegradeException降级异常
    AuthorityException授权规则异常
    SystemBlockException系统规则异常
    package com.config;
    
    import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @Component
    public class SentinelBolckhandler implements BlockExceptionHandler {
        @Override
        public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
            String msg = "未知异常";
            int status = 429;
            if(e instanceof FlowException){
                msg = "请求被限流了!";
            }
            else if(e instanceof DegradeException){
                msg = "请求被降级了!";
            }
            else if(e instanceof ParamFlowException){
                msg = "热点参数被限流了!";
            }
            else if(e instanceof AuthorityException){
                msg = "请求没有权限!";
                status = 401;
            }
            httpServletResponse.setContentType("application/json;charset=utf-8");
            httpServletResponse.setStatus(status);
            httpServletResponse.getWriter().println("{\"message\":\""+msg+"\",\"status\":"+status+"}");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    在这里插入图片描述

    2.3 规则持久化

    有三种模式:

    • 原始模式: 默认模式,规则保存在内存之中,重启服务会丢失
    • pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或者数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。缺点就是不是实时的,导致数据不一致。
    1.推送规则
    2.内存中更新规则
    3.将规则更新至文件
    SentinelDashboard
    Sentinel客户端
    规则缓存
    本地数据库
    • push模式:控制台将配置规则推送到远程配置中心(nacos)。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。该方式实时更新,推荐该模式。
      但是该模式最为复杂,因为需要修改Sentinel控制台源代码。

    我们为消费者引入如下依赖

    		<dependency>
                <groupId>com.alibaba.cspgroupId>
                <artifactId>sentinel-datasource-nacosartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    spring:
      application:
        name: order-service
      profiles: # 环境
        active: dev
      cloud:
        nacos:
          config:
            enabled: true
            server-addr: localhost:8848 #nacos地址
            file-extension: yaml #文件后缀名
        sentinel:
          transport:
            dashboard: localhost:8080
          web-context-unify: false #关闭context整合,Sentinel默认会把Controller的方法做context。导致链路模式的流量失效
          datasource:
            flow:
              nacos:
                server-addr: localhost:8848
                data-id: orderservice-flow-rules
                group-id: SENTINEL_GROUP
                rule-type: flow #此处我们选择限流,也可以选:degrade、authority、param-flow
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    随后重启服务
    下载源码
    alibaba/Sentinel
    接下来我们修改源码:
    在源码中找到dashboard
    在这里插入图片描述
    在其中如下图进行注释
    在这里插入图片描述
    将该类的test中的nacos,复制到main中对应的位置
    在这里插入图片描述

    在这里插入图片描述
    在main中,修改nacosConfig类

    /*
     * Copyright 1999-2018 Alibaba Group Holding Ltd.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;
    
    import java.util.List;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.nacos.api.config.ConfigFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Configuration
    @ConfigurationProperties(prefix = "nacos")
    public class NacosConfig {
        private String addr;
        @Bean
        public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
            return JSON::toJSONString;
        }
    
        @Bean
        public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
            return s -> JSON.parseArray(s, FlowRuleEntity.class);
        }
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            return ConfigFactory.createConfigService(addr);
        }
        public String getAddr(){
            return addr;
        }
        public void setAddr(String addr){
            this.addr = addr;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    随后在Properties文件中,添加Nacos地址

    nacos.addr=localhost:8848
    
    • 1

    接着修改数据源在这里插入图片描述
    修改如下

    
        @Autowired
        @Qualifier("flowRuleNacosProvider")
        private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
        @Autowired
        @Qualifier("flowRuleNacosPublisher")
        private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接着修改前端页面
    在这里插入图片描述
    取消该部分注释
    在这里插入图片描述

    <li ui-sref-active="active" ng-if="entry.appType==0">
                <a ui-sref="dashboard.flow({app: entry.app})">
                  <i class="glyphicon glyphicon-filter">i>  流控规则-NACOSa>
              li>
    
    • 1
    • 2
    • 3
    • 4

    随后打包
    在这里插入图片描述
    启动后如下:
    在这里插入图片描述

    点击流控规则Nacos,并添加一个规则
    在这里插入图片描述
    可以看到多出来了一个规则
    在这里插入图片描述

    同理,如果要改其他的,也是需要把页面进行修改。

    参考文献

    [1]黑马程序员Java微服务

  • 相关阅读:
    ZZNUOJ_C语言1035:分段函数求值(完整代码)
    盲盒小程序 跨平台兼容性测试策略:打造无缝体验
    掌握这个技巧,一键实现把文字转语音,用过都说好
    Linux之ssh
    猿创征文|瑞吉外卖——管理端_后台登录与退出
    【Python 初学者】从零开始构建自己的神经网络
    TCP串流场景剖析
    java高手进阶之:消息模板
    解决办法:使用Node.js访问MySQL8的时候会报错ER_NOT_SUPPORTED_AUTH_MODE
    pymysql调用存储过程
  • 原文地址:https://blog.csdn.net/weixin_46949627/article/details/126426976