• SpringCloudAlibaba项目之Sentinel流量控制


    SpringCloudAlibaba随笔目录

    一、SpringCloudAlibaba项目之父工程搭建

    二、SpringCloudAlibaba项目之Nacos搭建及服务注册

    三、SpringCloudAlibaba项目之生产者与消费者

    四、SpringCloudAlibaba项目之Ribbon负载均衡

    五、SpringCloudAlibaba项目之OpenFeign远程调用

    六、SpringCloudAlibaba项目之Nacos-config配置中心

    七、SpringCloudAlibaba项目之Sentinel流量控制

    八、SpringCloudAlibaba项目之Seata分布式事务

    九、SpringCloudAlibaba项目之GateWay网关

    十、SpringCloudAlibaba项目之SkyWalking链路

    SpringCloudAlibaba项目之Sentinel流量控制

    1、Sentinel简介

      Sentinel是阿里开源的项目,提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。

    官网:https://github.com/alibaba/Sentinel/wiki

    Sentinel主要特性:

     2、Sentinel与Hystrix的区别

      

    关于Sentinel与Hystrix的区别见:https://yq.aliyun.com/articles/633786/

    总体来说:

      Hystrix常用的线程池隔离会造成线程上下切换的overhead比较大;Hystrix使用的信号量隔离对某个资源调用的并发数进行控制,效果不错,但是无法对慢调用进行自动降级;Sentinel通过并发线程数的流量控制提供信号量隔离的功能;

    此外,Sentinel支持的熔断降级维度更多,可对多种指标进行流控、熔断,且提供了实时监控和控制面板,功能更为强大。

      Sentinel 的所有规则都可以在内存态中动态地查询及修改,修改之后立即生效。同时 Sentinel 也提供相关 API,供您来定制自己的规则策略。

    Sentinel 支持以下几种规则:流量控制规则、熔断降级规则、系统保护规则、来源访问控制规则 和 热点参数规则。

    3、使用Sentinel 核心库体验流量控制

      我们先使用springboot应用程序,快速搭建使用,体验流控效果。

     官网:https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97#%E5%85%AC%E7%BD%91-demo

     pom.xml中添加依赖

    <!-- springweb 启动依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- sentinel 核心库 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-core</artifactId>
        <version>1.8.2</version>
    </dependency>
    SentinelController接口:
    /**
     * Sentinel接口流控测试
     */
    @RestController
    public class SentinelController {
        public static final String RESOURCE_NAME = "sentinel";//资源名
    
        //进行Sentinel流控
        @RequestMapping(value = "/sentinel")
        public String sentinelTest(){
            Entry entry = null;
            try {
                //sentinel针对资源进行限制
                entry = SphU.entry(RESOURCE_NAME);
                //被保护的业务逻辑
                String str = "Sentinel接口正常";
                System.out.println("====" + str + "====");
                return str;
            } catch (BlockException e) {
                e.printStackTrace();
                //资源访问阻止,被限流或被降级
                //进行相应的处理操作
                System.out.println("Sentinel接口被流控了");
                return "Sentinel接口被流控了";
            }catch (Exception e){
                // 若需要配置降级规则,需要通过这种方式记录业务异常
                Tracer.traceEntry(e,entry);
            }finally {
                if(entry != null){
                    entry.exit();
                }
            }
            return null;
        }
    
        /**
         * 定义规则
         *
         * spring的初始化方法
         */
        @PostConstruct
        private static void initFlowRules(){
            //流控规则
            List<FlowRule> rules = new ArrayList<>();
            //流控
            FlowRule rule = new FlowRule();
            //设置受保护的资源
            rule.setResource(RESOURCE_NAME);
            // 设置流控规则 QPS
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            //设置受保护资源的阈值
            // Set limit QPS to 20.
            rule.setCount(1);
            rules.add(rule);
            //加载配置好的规则
            FlowRuleManager.loadRules(rules);
        }
    }

    访问地址:http://localhost:8080/sentinel

    1秒钟之内访问一次正常,如果超过一次将被流控

     4、@SentinelResource 注解方式定义资源

     pom.xml文件添加依赖

    <!-- 使用 @SentinelResource 注解依赖 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-annotation-aspectj</artifactId>
        <version>1.8.2</version>
    </dependency>

     SentinelAspectConfiguration配置bean

    /**
     * 若您的应用使用了 Spring AOP(无论是 Spring Boot 还是传统 Spring 应用),
     * 您需要通过配置的方式将 SentinelResourceAspect 注册为一个 Spring Bean:
     */
    @Configuration
    public class SentinelAspectConfiguration {
        @Bean
        public SentinelResourceAspect sentinelResourceAspect() {
            return new SentinelResourceAspect();
        }
    }
    SentinelController:
    /**
     * Sentinel接口流控测试
     */
    @RestController
    public class SentinelController {
        public static final String USER_RESOURCE_NAME = "user";//资源名
    /**
         * 定义规则
         *
         * spring的初始化方法
         */
        @PostConstruct
        private static void initFlowRules(){
            //流控规则
            List<FlowRule> rules = new ArrayList<>();//流控
            FlowRule rule2 = new FlowRule();
            //设置受保护的资源
            rule2.setResource(USER_RESOURCE_NAME);
            // 设置流控规则 QPS
            rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
            //设置受保护资源的阈值
            // Set limit QPS to 20.
            rule2.setCount(1);
            rules.add(rule2);
    
            //加载配置好的规则
            FlowRuleManager.loadRules(rules);
        }
    
        /**
         * 使用@SentinelResource进行Sentinel流控
         * @SentinelResource注解改善接口钟资源定义和被流控降级后的处理方法
         * 使用方法:1、添加依赖
         *           2、配置bean-SentinelResourceAspect
         *  value:定义流控资源
         *  blockHandler:设置流控降级后的处理方法(默认该方法必须声明在同一个类)
         *      如果不想在同一个类中,可以使用 blockHandlerClass 指定,但是方法必须是static
         *  fallback:当接口出现异常,就可以交给fallback指定的方法进行处理
         *      如果不想在同一个类中,可以使用 fallbackClass 指定,但是方法必须是static
         *
         *  注意:如果blockHandler和fallback方法同时指定了,则blockHandler优先级更高
         * @param id
         * @return
         */
        @RequestMapping(value = "/user")
        @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForUserTest",fallback = "fallbackForUserTest")
        public User userTest(String id){
            int a = 1/0;
            return new User("张三");
        }
    
        /**
         * userTest流控降级后的处理方法
         * 注意:
         * 1、一定要是public
         * 2、返回值一定要和源方法(userTest)保证一致,包含源方法的参数
         * 3、可以在参数最后添加BlockException,可以区分是什么规则的处理方法
         * @param id
         * @param ex
         * @return
         */
        public User blockHandlerForUserTest(String id,BlockException ex){
            ex.printStackTrace();
            return new User("流控!");
        }
    
        /**
         * userTest异常后的处理方法
         * 注意:
         * 1、一定要是public
         * 2、返回值一定要和源方法(userTest)保证一致,包含源方法的参数
         * 3、可以在参数最后添加Throwable,可以区分是什么异常
         * @param id
         * @param e
         * @return
         */
        public User fallbackForUserTest(String id,Throwable e){
            e.printStackTrace();
            return new User("异常处理!");
        }
    }

    特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandlerfallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。

    访问地址:http://localhost:8080/user

     5、服务降级规则体验

    /**
     * Sentinel接口流控测试
     */
    @RestController
    public class SentinelController {
        public static final String DEGRADE_RESOURCE_NAME = "degrade";//降级资源名
    /**
         * 定义服务降级规则
         *
         * spring的初始化方法
         */
        @PostConstruct
        private static void initDegradeRules(){
            //降级规则
            List<DegradeRule> DegradeRules = new ArrayList<>();
            //流控
            DegradeRule degradeRule = new DegradeRule();
            //设置受保护的资源
            degradeRule.setResource(DEGRADE_RESOURCE_NAME);
            // 设置规则测率: 异常数
            degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
            //设置异常数
            degradeRule.setCount(2);
            degradeRule.setTimeWindow(10);//10秒内发生的异常
            degradeRule.setMinRequestAmount(2);//最小请求数
    
            DegradeRules.add(degradeRule);
            //加载配置好的规则
            DegradeRuleManager.loadRules(DegradeRules);
        }
    
        @RequestMapping("/degrade")
        @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerForDegrade")
        public User degrade(String id) throws InternalException {
            // 异常数/比例
            throw new RuntimeException("异常");
    
            //慢调用比例
            /*TimeUnit.SECONDS.sleep(1);
            return new User("正常");*/
        }
    /**
         * degrade服务降级的处理方法
         *
         * @param id
         * @param ex
         * @return
         */
        public User blockHandlerForDegrade(String id,BlockException ex){
            ex.printStackTrace();
            return new User("降级处理!");
        }
    }

    访问地址:http://localhost:8080/degrade

     6、控制台部署

    控制台文档:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

    github下载地址(下载对应版本):https://github.com/alibaba/Sentinel/releases

    组件版本关系:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

    从 release 页面 下载最新版本的控制台 jar 包,使用如下命令启动控制台:

    java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

    其中 -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080。

    从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。可以参考 鉴权模块文档 配置用户名和密码。

    注:若您的应用为 Spring Boot 或 Spring Cloud 应用,您可以通过 Spring 配置文件来指定配置,详情请参考 Spring Cloud Alibaba Sentinel 文档

    7、客户端接入控制台(整合SpringCloud Alibaba)

     

     pom.xml文件添加依赖

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

    application.properties

    # 应用名称
    spring.application.name=service-sentinel
    # 应用服务 WEB 访问端口
    server.port=8081
    # Sentinel 控制台地址
    spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858
    # 取消Sentinel控制台懒加载
    # 默认情况下 Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包
    # 配置 sentinel.eager=true 时,取消Sentinel控制台懒加载功能
    spring.cloud.sentinel.eager=true
    # 如果有多套网络,又无法正确获取本机IP,则需要使用下面的参数设置当前机器可被外部访问的IP地址,供admin控制台使用
    # spring.cloud.sentinel.transport.client-ip=
    OrderController
    /**
     * 订单服务
     */
    @RestController
    @RequestMapping("/order")
    public class OrderController {
    
        /**
         * 新增订单
         * @return
         */
        @RequestMapping("/addOrder")
        public String addOrder(){
            System.out.println("订单新增成功");
    
            return "订单服务-订单新增成功";
        }
    }

    整合效果:

     8、BlockException统一异常处理

     

     MyBlockExceptionHandler异常处理类:

    注意:想使用BlockException统一异常处理时,不能添加@SentinelResource

    /**
     * BlockException统一异常处理
     */
    @Component
    public class MyBlockExceptionHandler implements BlockExceptionHandler {
    
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
            System.out.println("资源规则的详细信息:" + e.getRule());
            JSONObject resultObj = new JSONObject();
            if(e instanceof FlowException){
                resultObj.put("code",100);
                resultObj.put("msg","接口限流");
            }if(e instanceof DegradeException){
                resultObj.put("code",101);
                resultObj.put("msg","服务降级");
            }if(e instanceof ParamFlowException){
                resultObj.put("code",102);
                resultObj.put("msg","热点参数限流");
            }if(e instanceof SystemBlockException){
                resultObj.put("code",103);
                resultObj.put("msg","触发系统保护规则");
            }if(e instanceof AuthorityException){
                resultObj.put("code",104);
                resultObj.put("msg","授权规则不通过");
            }
    
            //返回json数据
            response.setStatus(500);
            response.setCharacterEncoding("UTF-8");
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.getWriter().write(resultObj.toString());
        }
    }

    控制台添加流控规则

    测试效果:

     9、整合openFeign降级

     

     pom.xml添加依赖

    <!-- nacos 服务注册发现(客户端)依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- sentinel 流量控制依赖 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <!-- openfeign 远程调用依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    application.properties添加配置

    # openfeign整合sentinel
    feign.sentinel.enabled=true
    StockOpenFeignServiceFallback服务降级实现类
    @Component
    public class StockOpenFeignServiceFallback implements StockOpenFeignService{
    
        @Override
        public String subStock(String id) {
            return "subStock-服务降级了!";
        }
    
        @Override
        public String addStock() {
            return "addStock-服务降级了!";
        }
    }
    StockOpenFeignService接口添加服务降级实现类
    /**
     * 库存服务接口
     * name:指定调用rest接口所对应的服务名
     * path:指定调用rest接口所在的StockController指定的@RequestMapping
     */
    @FeignClient(name = "service-stock",path = "stock",fallback = StockOpenFeignServiceFallback.class)
    public interface StockOpenFeignService {
    
        //声明需要调用的rest接口对应的方法
        /**
         * 库存扣减
         * @return
         */
        @RequestMapping("/subStock/{id}")
        //@RequestLine("GET /subStock") //feign的原生注解
        String subStock(@PathVariable("id") String id);
        //String subStock(@Param("id") String id); //@PathVariable换成@Param
    
        /**
         * 库存新增
         * @return
         */
        @RequestMapping("/addStock")
        //@RequestLine("GET /addStock") //feign的原生注解
        String addStock();
    }

    访问地址:http://localhost:8082/order/addOrder

     10、Sentinel规则持久化

      一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化,将限流配置规则持久化进Nacos保存,只要刷新到某个被流控的rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对Sentinel上的流控规则持续有效。

     pom.xml文件添加依赖

    <!-- sentinel 规则持久化依赖 -->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

    application.properties

    spring.application.name=service-sentinel
    spring.cloud.nacos.discovery.server-addr= 127.0.0.1:8848
    spring.cloud.sentinel.transport.dashboard=localhost:8080
    #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口
    spring.cloud.sentinel.transport.port= 8719  
    spring.cloud.sentinel.datasource.ds1.nacos.server-addr= 127.0.0.1:8848
    spring.cloud.sentinel.datasource.ds1.nacos.dataId= service-sentinel
    spring.cloud.sentinel.datasource.ds1.nacos.groupId= DEFAULT_GROUP
    spring.cloud.sentinel.datasource.ds1.nacos.data-type= json
    spring.cloud.sentinel.datasource.ds1.nacos.rule-type= flow

    添加Nacos规则

    [
        {
             "resource":"/order/addOrder",
             "limitApp":"default",
             "grade":1,
             "count":1,
             "strategy":0,
             "controlBehavior":0,
             "clusterMode":false
        }
    ]
    resource: 资源名称
    limitApp: 来源应用
    grade: 阈值类型,0表示线程,1表示QPS
    count: 单机阈值
    strategy: 流控模式,0表示直接,1表示关联,2表示链路
    controlBehavior: 流控效果,0表示快速失败,1表示Warm Up,2表示排队等待
    clusterMode: 是否集群
     
  • 相关阅读:
    django 标签的使用
    django(models)视图与html 简单的操作
    Django 简单的使用
    安全性测试计划
    常用缩写
    ADB 基本命令整理
    kindle升级测试
    虚拟机的类加载机制
    垃圾收集器与内存分配策略之篇三:理解GC日志和垃圾收集器参数总结
    垃圾收集器与内存分配策略之篇二:垃圾收集器
  • 原文地址:https://www.cnblogs.com/qiantao/p/15731471.html
Copyright © 2020-2023  润新知