• springCloudAlibaba之服务熔断组件---sentinel


    sentinel学习

    • 服务雪崩

    服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将不可用逐渐放大的过程。
    在这里插入图片描述

    sentinel容错机制

    常见的容错机制有超时机制、服务限流、隔离、服务熔断

    • 超时机制
      在不做任何处理的情况下,服务提供者不可用回导致消费者请求线程强制等待,而造成系统资源耗尽。加入超时机制,一旦超时,就释放资源。由于释放资源较快,一定程度上可以抑制资源耗尽的问题。
    • 服务限流
      某个服务达到QPS设定最大值则抛异常。
    • 服务熔断
      在这里插入图片描述
    • 服务降级
      有服务熔断,必然要有服务降级。
      所谓降级,就是当某个服务熔断之后,服务将不再被调用,此时客户端可以自己准备一个本地的fellback(回退),回调,返回一个缺省值。例如:(备用接口/缓存/mock数据),这样做,虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。(服务熔断之后进入客户端降级方法)

    使用代码方式进行QPS流控-流控规则初体验

    • 引入sentinel相关包
      <dependencies>
        <dependency>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-starter-webartifactId>
        dependency>
    
        
        <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-coreartifactId>
          <version>1.8.0version>
        dependency>
    
        <dependency>
          <groupId>org.projectlombokgroupId>
          <artifactId>lombokartifactId>
        dependency>
    
        
        <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-annotation-aspectjartifactId>
          <version>1.8.0version>
        dependency>
    
        <dependency>
          <groupId>junitgroupId>
          <artifactId>junitartifactId>
          <version>3.8.1version>
          <scope>testscope>
        dependency>
    
        
        <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-transport-simple-httpartifactId>
          <version>1.8.0version>
        dependency>
      dependencies>
    
    • 代码实现
    package com.sentinel.controller;
    
    import com.alibaba.csp.sentinel.Entry;
    import com.alibaba.csp.sentinel.SphU;
    import com.alibaba.csp.sentinel.Tracer;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.RuleConstant;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.PostConstruct;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author : zhouzhiqiang
     * @date : 2024/6/5 23:25
     * @description :
     */
    @Slf4j
    @RestController
    @RequestMapping("/hello")
    public class HelloController {
    
        private static final String RESOURCE_NAME="hello";
        private static final String USER_RESOURCE_NAME="user";
        private static final String DEGRADE_RESOURCE_NAME="degrade";
    
        @RequestMapping("/helloSentinel")
        public String hello(){
    
            Entry entry=null;
            try {
                entry= SphU.entry(RESOURCE_NAME);
                String str="hello world";
                log.info("======="+str+"=======");
                return str;
            } catch (BlockException e) {
                log.info("block");
                return "被限流了!";
            }catch (Exception e){
                Tracer.traceEntry(e,entry);
            }finally {
                if (entry != null) {
                    entry.exit();
                }
            }
            return null;
        }
    
        @PostConstruct
        public static void initFlowRules(){
            //流控规则
            List<FlowRule> rules=new ArrayList<>();
    
            //流控
            FlowRule rule = new FlowRule();
            //为哪个资源进行流量控制
            rule.setResource(RESOURCE_NAME);
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            rule.setCount(1);
            rules.add(rule);
    
            //加载流控规则
            FlowRuleManager.loadRules(rules);
        }
    }
    

    使用@SentinelResource注解进行流控

    要使用这个注解需要引入相关包和配置SentinelResourceAspect的bean

    • 引入包
        <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-annotation-aspectjartifactId>
          <version>1.8.0version>
        dependency>
    
    • 配置bean
      @Bean
        public SentinelResourceAspect sentinelResourceAspect(){
            return new SentinelResourceAspect();
        }
    
    • 使用注解方式

    value:定义资源(接口名称)
    blockHandler :设置流控降级后的处理方法,默认该方法,必须声明在一个类中
    fallback:接口中出现异常了,就可以交给fellback指定的处理方法

    @PostConstruct
        public static void initFlowRules(){
            //流控规则
            List<FlowRule> rules=new ArrayList<>();
    
            //流控
            FlowRule rule = new FlowRule();
            //为哪个资源进行流量控制
            rule.setResource(USER_RESOURCE_NAME);
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            rule.setCount(1);
            rules.add(rule);
    
            //加载流控规则
            FlowRuleManager.loadRules(rules);
        }
    
        @RequestMapping("/user")
        @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForGetUser",fallback = )
        public User getUser(String id){
            return new User("zzq");
        }
    
        /**
         * 注意:
         * 1、一定是public
         * 2、返回值一定得和原方法返回值一致,包含方法的参数
         * 3、可以在参数的最后添加BlockException,可以区分是什么规则的处理方法
         * @param id
         * @param e
         * @return
         */
        //降级方法
        public User blockHandlerForGetUser(String id,BlockException e){
            e.printStackTrace();
            return new User("被流控了!");
        }
    

    通过代码方式设置降级规则-降级规则初体验

    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        private static final String DEGRADE_RESOURCE_NAME="degrade";
    
        @RequestMapping("/degrade")
        @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerForJob")
        public User degrade(String id){
            int i=1;
            int sum=i/0;
            return new User("成功");
        }
    
        public User blockHandlerForJob(String id, BlockException e){
            e.printStackTrace();
            return new User("触发降级规则");
        }
    
        @PostConstruct
        public void initDegradeRule(){
            List<DegradeRule> rules=new ArrayList<>();
    
            DegradeRule rule = new DegradeRule();
            rule.setResource(DEGRADE_RESOURCE_NAME);
            rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
            //触发熔断异常数
            rule.setCount(2);
            //触发熔断的最小请求数
            rule.setMinRequestAmount(2);
            //统计时长:单位ms  1分钟
            rule.setStatIntervalMs(60*1000);
            //一分钟内:执行了两次 ,出现两次异常 就会触发熔断
    
            /**
             * 熔断持续时间,单位秒。一旦触发了熔断,再次请求接口则直接调用降级方法。
             * 10秒后,---半开状态,恢复接口调用。如果再次请求,则会熔断,不再根据熔断规则进入熔断,而是直接进入熔断
             */
            rule.setTimeWindow(10);
    
    
            rules.add(rule);
            DegradeRuleManager.loadRules(rules);
        }
    }
    

    流控规则一般设置在服务生产方,而降级规则一般设置在服务消费方

    sentinel控制台部署

    下载dashboard--https://github.com/alibaba/Sentinel/releases

    运行jar包:java -jar sentinel-dashboard-1.8.0.jar。运行完毕进行访问,默认端口号8080。用户名和密码默认sentinel
    在这里插入图片描述

    客户端整合服务端

    客户端需要引入Transport模块来与sentinel控制台进行通信

    
        <dependency>
          <groupId>com.alibaba.cspgroupId>
          <artifactId>sentinel-transport-simple-httpartifactId>
          <version>1.8.0version>
        dependency>
    
    • 配置启动
      客户端启动时需要加入JVM参数-Dcsp.sentinel.dashboard.server=192.168.1.15:8080,指定控制台地址和端口
      在这里插入图片描述

    springcloud整合sentinel

    • 添加依赖
         <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            dependency>
    
    • yaml配置文件
    server:
      port: 8081
    spring:
      application:
        name: order-sentinel
      cloud:
        sentinel:
          transport:
            dashboard: 192.168.184.15:8080
            port: 8719
            client-ip: 192.168.184.1
    

    在虚拟机上部署dashboard的时候,主机和虚拟机一直网络不通,解决方法:
    1、关闭本机和虚拟机的防火墙
    在这里插入图片描述
    2、本机不动,配置虚拟机ip,网段和本地vmnet8保持一致,网关查看虚拟网络编辑器
    在这里插入图片描述

    ONBOOT="yes"
    IPADDR="192.168.184.15"
    NETMASK="255.255.255.0"
    GATEWAY="192.168.184.2"
    DNS1="8.8.8.8"
    DNS2="8.8.4.4"
    

    3、最重要的一步,设置网络连接模式为net模式
    在这里插入图片描述
    原因:解释: 1:当我们安装VMware Workstation后,在宿主机(物理电脑)上会多出两个网卡,VMNet1、VMNet8。 2:vmnet1是为host-only方式服务的,vmnet8是为NAT方式服务的

    QPS流控规则

    添加流控规则:QPS为2,超过2触发流控降级
    在这里插入图片描述
    在这里插入图片描述

    • 代码中指定自定义降级方法,dashboard定义流控规则,代码自定义降级方法
        /**
         * 流控测试接口
         * @return
         */
        @RequestMapping("/flow")
        @SentinelResource(value = "flow",blockHandler = "blockHandlerForFlow")
        public String flow(){
            return "正常访问";
        }
    
        public String blockHandlerForFlow(BlockException e){
            e.printStackTrace();
            System.out.println("触发流控,快速失败");
            return "触发流控,快速失败";
        }
    

    并发线程数-流控规则

    • dashboard配置
      在这里插入图片描述
    • 代码
        @RequestMapping("/flowThread")
        @SentinelResource(value = "flowThread",blockHandler = "blockHandlerForFlow")
        public String flowThread(){
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "正常访问";
        }
    
        public String blockHandlerForFlow(BlockException e){
            e.printStackTrace();
            System.out.println("触发流控,快速失败");
            return "触发流控,快速失败";
        }
    
    • 测试
      开两个浏览器模拟两个线程
    • 测试结果
      在这里插入图片描述

    BlockExceptionHandler统一异常处理,此时可以不加@SentinelResource注解

    • 代码
    @Data
    public class Result <T>{
        private Integer code;
        private String msg;
        private T data;
    
        public Result(Integer code, String msg, T data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        public Result(Integer code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public static Result error(Integer code,String msg){
            return new Result(code,msg);
        }
    }
    
    @Component
    @Slf4j
    public class MyBlockExceptionHandler implements BlockExceptionHandler {
    
        public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
            //规则的详细信息
            log.info("BlockExceptionHandler BlockException=============="+e.getRule());
            Result r = null;
            if (e instanceof FlowException) {
                r=Result.error(100,"接口被限流了!");
            }else if (e instanceof DegradeException){
                r=Result.error(101,"服务降级了!");
            }else if (e instanceof ParamFlowException){
                r=Result.error(102,"热点参数限流了!");
            }else if (e instanceof SystemBlockException){
                r=Result.error(103,"触发系统保护规则!");
            }else if (e instanceof AuthorityException){
                r=Result.error(104,"授权规则不通过!");
            }
    
            response.setStatus(500);
            response.setCharacterEncoding("utf-8");
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            new ObjectMapper().writeValue(response.getWriter(),r);
        }
    }
    
    • 测试代码
      此时在dashboar的为这个接口设置流控规则后,可以看到设置的同一异常起效果了
     @RequestMapping("/exceptionHandler")
        public String exceptionHandler(){
            return "正常访问";
        }
    

    关联流控模式

    流控模式有三种直接、关联、链路
    在这里插入图片描述

    关联模式:当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写的速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢。

    • dashboard设置
      当生成订单QPS>=2的时候,查询订单接口就被限流
      在这里插入图片描述
    • 代码
     @RequestMapping("/add")
        public String add(){
            System.out.println("下单成功");
            return "生成订单";
        }
    
        @RequestMapping("/get")
        public String get(){
            return "查询订单";
        }
    
    • 生成接口和查询接口测试
      生成接口和查询接口不好一起测试,生成接口使用apifox进行压测,查询接口手动点
      在这里插入图片描述
    • 测试结果
      查询接口触发流控规则
      在这里插入图片描述
    • 或者使用JMeter去做压测
      在这里插入图片描述
      在这里插入图片描述

    链路流控模式

    可以对资源进行流控
    在这里插入图片描述

    • 代码
    public interface IUserService {
        User getUser();
    }
    

    对getUser添加@SentinelResource注解

    @Service
    public class IUserServiceImpl implements IUserService{
    
        @SentinelResource(value = "getUser",blockHandler = "blockHandlerForUser")
        public User getUser() {
            User user = new User();
            user.setId("001");
            user.setName("针对业务方法进行流控");
            return user;
        }
    
        public User blockHandlerForUser(BlockException e){
            e.printStackTrace();
            User user = new User();
            user.setId("001");
            user.setName("ddddd");
            return new User();
        }
    }
    

    test1和test2调用getUser(),当getUser(),达到QPS2的时候,对test1或者test2做流控

    @RestController
    @RequestMapping("/link")
    public class LinkController {
    
        @Autowired
        private IUserService userService;
    
        @RequestMapping("/test1")
        public User test1(){
            return userService.getUser();
        }
    
        @RequestMapping("/test2")
        public User test2(){
            return userService.getUser();
        }
    }
    
    • 配置文件配置
      web-context-unify: false # 默认将调用链路收敛了
    server:
      port: 8081
    spring:
      application:
        name: order-sentinel
      cloud:
        sentinel:
          transport:
            dashboard: 192.168.184.15:8080
            port: 8719
            client-ip: 192.168.184.1
          web-context-unify: false  # 默认将调用链路收敛了
    
    • dashboar配置
      在这里插入图片描述
    • 测试结果
      当test1qps达到2的时候触发流控
      在这里插入图片描述

    流控效果介绍

    流控效果有快速失败、预热、排队等待。预热:适用于激增流量的情况
    在这里插入图片描述

    预热流控效果

    warm up:预热冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过“冷启动”,让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

    • dashboard配置
      qps最大为10,10秒内达到10
      在这里插入图片描述

    排队等待

    激增流量:长时间处于低水平,某个时间段处于高水平,可以使用预热的方式进行处理。

    脉冲流量:一段时间处于低水平某个时刻到达高水平,然后又变为低水平,循环往复

    匀速排队:严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过

    • 测试
      设置流控效果为快速失败,单机QPS阈值为5,使用JMeter测试,一秒执行10次循环5次
      在这里插入图片描述
      在这里插入图片描述
      测试结果:可以看到一秒内10个QPS5个通过,5个拒绝
      在这里插入图片描述
    • 使用排队等待流控效果
      dashboard配置:单机阈值5,超时时间5秒。比如有10个线程,每次进去5个,其他5个等待。执行完一个进去一个,如果5个线程在5秒内没有执行完则直接拒绝
      在这里插入图片描述
    • 测试结果
      所有请求都执行成功
      在这里插入图片描述

    熔断降级规则

    慢调用比例:慢调用占调用总数的比例

    • dashboard配置
      最大RT:单位毫秒,不能超过1秒,超过1秒则触发降级。比例阈值:慢调用占请求次数的比例。最小请求数:配合比例阈值,10次请求有一次触发慢调用则触发降级
      在这里插入图片描述
    • 使用Jmetter进行测试
      在这里插入图片描述
    • 测试结果
      触发降级
      在这里插入图片描述

    sentinel-整合openfeign降级

    nacos服务注册必须是一个web项目
    按此步骤完成微服务搭建后,发现服务并没有注册到nacos注册中心,在官方文档查看springboot和springcloud版本也并没有冲突,尝试引入nacos管理依赖后还是不行,最后想到要在nacos注册中心注册的微服务应该必须是一个web项目,而在springboot环境下开发web项目需要引入springMvc的相关启动依赖,加上后发现服务成功注册到了nacos注册中心

    • 在消费端引入依赖
    <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
            dependency>
    
    
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-openfeignartifactId>
            dependency>
    
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
    • 消费端配置yml
      特别加上:server: port: 8084 spring: application: name: product-service cloud: nacos: username: zzq password: 1w2e3r4! server-addr: 192.168.184.15:8848 discovery: namespace: 31a14d31-cda4-4a37-a4ba-7717aaddd97d feign: sentinel: enabled: true
    server:
      port: 8084
    spring:
      application:
        name: product-service
      cloud:
        nacos:
          username: zzq
          password: 1w2e3r4!
          server-addr: 192.168.184.15:8848
          discovery:
            namespace: 31a14d31-cda4-4a37-a4ba-7717aaddd97d
    feign:
      sentinel:
        enabled: true
    

    消费端feing调用

    • feing
    @FeignClient(value = "stock-service",path = "/person",fallback = IPersonServiceFallback.class)
    public interface IPersonService {
    
        @RequestMapping("/getPerson")
        public Person getPerson();
    }
    

    必须要有一个feingclint的实现类,并且注入进spring

    @Component
    public class IPersonServiceFallback implements IPersonService {
        public Person getPerson() {
            Person person = new Person();
            person.setId("001");
            person.setName("未找到用户");
            return person;
        }
    }
    

    消费端调用服务端

       @RestController
    @RequestMapping("/product")
    public class ProductController {
    
        @Autowired
        private IPersonService personService;
    
        @RequestMapping("/getProduct")
        public String getProduct(){
            Person person = personService.getPerson();
            return "调用成功:"+person.getName();
        }
    }
    
    
    • 测试结果
      调用异常执行降级方法
      在这里插入图片描述

    热点参数流控

    场景:接口参数对某些值的访问QPS比其他高。典型的电商系统中,商品查询接口,某些商品的访问QPS要远远高于其他商品,比如商品ID为1的商品,访问请求远远高于其他商品。

    • dashboard配置
      在这里插入图片描述

    sentinel规则持久化

    在这里插入图片描述
    结合nacos配置中心使用推模式进行规则的持久化

    基于Nacos配置中心控制台实现推送

    • 引入依赖
      <dependency>
                <groupId>com.alibaba.cspgroupId>
                <artifactId>sentinel-datasource-nacosartifactId>
       dependency>
    
  • 相关阅读:
    谈一谈Python中的装饰器
    机器学习中的 K-均值聚类算法及其优缺点。
    c语言之二级指针
    盘点Go中的开发神器
    开通经营收款码要手续费吗
    第二十二章 alibaba sentinel详解-sentinel持久化
    BERT源码实现与解读(Pytorch)
    接口自动化测试 —— Jmeter 6种定时器应用
    解密一致性哈希算法:实现高可用和负载均衡的秘诀
    内核实战教程第五期 _ SQL 执行引擎的设计与实现
  • 原文地址:https://blog.csdn.net/weixin_43216437/article/details/139483410