分布式系统Sentinel整合Open-Feign限流


一、前言

提示:本文讲到的代码部分来自上文
上文链接地址

二、sentinel控制台

1、调用与被调用方引入依赖

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

2、控制台搭建

控制台搭建地址
下载安装包sentinel-dashboard-1.8.0.jar

3、启动

//启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本,
//-Dserver.port=8100 用于指定 Sentinel 控制台端口为 8100 
//默认用户名和密码都是 sentinel
​
//普通进程
java -Dserver.port=8100 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar

//守护进程
nohup java -Dserver.port=8100 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar &

在这里插入图片描述

三、限流配置

1、多个微服务接入Sentinel配置

调用与被调用都需配置port不一样

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080 
        port: 9999#dashboard: 8080 控制台端口
#port: 9999 本地启的端口,随机选个不能被占用的,与dashboard进行数据交互,会在应用对应的机器上启动一个 Http Server,该 Server 会与 Sentinel 控制台做交互, 若被占用,则开始+1一次扫描

在这里插入图片描述
设置QPS单机阀值为2,一秒大于两个会被流控
在这里插入图片描述
效果图
在这里插入图片描述

四、面板介绍

资源名:默认是请求路径,可自定义
针对来源:对哪个微服务进行限流,默认是不区分来源,全部限流,这个是针对区分上游服务进行限流, 比如 other1服务 被 other2服务、other3服务调用,就可以针对来源进行限流
在这里插入图片描述

  • 流量控制(flow control)
    原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

  • 两种规则

    • 基于统计并发线程数的流量控制
      并发数控制用于保护业务线程池不被慢调用耗尽
      Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目)
      ​如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。

    • 基于统计QPS的流量控制
      当 QPS 超过某个阈值的时候,则采取措施进行流量控制(一秒内访问)

  • 流量控制的效果包括以下几种

    • 直接拒绝:默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝
    • Warm Up:冷启动/预热,如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值
    • 匀速排队:严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法,主要用于处理间隔性突发的流量,如消息队列,想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求
      • 匀速排队等待策略是 Leaky Bucket 算法结合虚拟队列等待机制实现的。
        匀速排队模式暂时不支持 QPS > 1000 的场景

Warm Up:冷启动/预热,如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值

五、基于并发线程数进行限流配置

流控规则会下发到微服务,微服务如果重启,则流控规则会消失可以持久化配置

@RestController
@RequestMapping("other2")
public class OtherController2 {
    /**
     * 基于并发线程数进行限流配置,测试
     * @return
     */
    @RequestMapping("getusername6")
    String getusername6() {
        try {
            TimeUnit.SECONDS.sleep(3);//让程序睡三秒中达到延迟效果
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "other2的getUserName6反回的数据";
    }
}

在这里插入图片描述

在这里插入图片描述

六、降级熔断策略

  • 慢调用比例(响应时间): 选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用

    • 比例阈值:修改后不生效-目前已经反馈给官方那边的bug
    • 熔断时长:超过时间后会尝试恢复
    • 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断
  • 异常比例:当单位统计时长内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断

    • 比例阈值
    • 熔断时长:超过时间后会尝试恢复
    • 最小请求数:熔断触发的最小请求数,请求数小于该值时,即使异常比率超出阈值也不会熔断
  • 异常数:当单位统计时长内的异常数目超过阈值之后会自动进行熔断

    • 异常数:
    • 熔断时长:超过时间后会尝试恢复
    • 最小请求数:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断

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

1、熔断实操

测试代码

@RestController
@RequestMapping("other2")
public class OtherController2 {
    int temp = 0;

    /**
     * 熔断,测试
     * @return
     */
    @RequestMapping("getusername7")
    String getusername7() {
        temp++;
        if(temp%3 == 0){
            throw  new RuntimeException();
        }
        return "other2的getUserName7反回的数据";
    }
}

在这里插入图片描述

在这里插入图片描述

2、自定义降级异常数据

  • 异常种类
    • FlowException //限流异常
      DegradeException //降级异常
      ParamFlowException //参数限流异常
      SystemBlockException //系统负载异常
      AuthorityException //授权异常

实现BlockExceptionHandler并且重写handle方法

调用方

@Component
public class OtherConfig2 implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse   httpServletResponse, BlockException e) throws Exception {
        Map<String, Object> backMap = new HashMap<>();
        if (e instanceof FlowException) {
            backMap.put("code", -1);
            backMap.put("msg", "限流-异常啦");
            backMap.put("data", null);
        } else if (e instanceof DegradeException) {
            backMap.put("code", -2);
            backMap.put("msg", "降级-异常啦");
            backMap.put("data", null);
        } else if (e instanceof ParamFlowException) {
            backMap.put("code", -3);
            backMap.put("msg", "热点-异常啦");
            backMap.put("data", null);
        } else if (e instanceof SystemBlockException) {
            backMap.put("code", -4);
            backMap.put("msg", "系统规则-异常啦");
            backMap.put("data", null);
        } else if (e instanceof AuthorityException) {
            backMap.put("code", -5);
            backMap.put("msg", "认证-异常啦");
            backMap.put("data", null);
        }

        // 设置返回json数据
        httpServletResponse.setStatus(200);//响应消息的状态码
        httpServletResponse.setHeader("content-Type", "application/json;charset=UTF-8");
        httpServletResponse.getWriter().write(JSON.toJSONString(backMap, SerializerFeature.WriteMapNullValue));

    }
}

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

七、Sentinel整合OpenFeign配置容错设计实战

但是Feign在调用远程服务的时候,由于远程服务的原因,可能会产生异常。就需要进行相应的容错设计。
当Feign远程调用服务出现问题后,进入到了容错工厂类中的同名方法,执行容错逻辑。而且,Feign调用产生的异常信息也可以获取到。

1、加入依赖

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

2、开启Feign对Sentinel的支持

调用方

#开启Feign对Sentinel的支持
feign:
  sentinel:
    enabled: true

3、配置feign容错类

调用方的service

@FeignClient(value = "spring-other1" ,fallback = OtherServiceFallBack2.class)//服务名称记得和nacos保持一样

例如

@FeignClient(value = "spring-other1" ,fallback = OtherServiceFallBack2.class)//服务名称记得和nacos保持一样
public interface OtherService2 {

    /**
     * 新建接口用于调用Other的getUserName2接口服务
     *
     * @param userName
     * @return
     */
    @RequestMapping("/other1/getusername2")
    String getOther1UserName2(@RequestParam("name") String userName);
}

4、FallBack类继承外调service接口

@Service
public class OtherServiceFallBack2 implements OtherService2 {
    @Override
    public String getOther1UserName2(String userName) {
        return userName+"——该返回为异常返回";
    }
}

5、调用方controller

@RestController
@RequestMapping("other2")
public class OtherController2 {
    /**
     * 使用Open-Feign
     *
     * @param userName
     * @return
     */
    @RequestMapping("getusername4")
    String getUserName4(@RequestParam("name") String userName) {
        String other1UserName2 = otherService2.getOther1UserName2(userName);
        //写入服务名即可
        return other1UserName2 + "-该字段为other2的getUserName反回的数据";
    }
}

6、当被调用方服务出现宕机异常

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

八、升级jdk11

下载jdk11
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
maven项目配置

    <properties>
        <java.version>11</java.version>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>