Sentinel详解【流控、降级、热点、授权】

一、Docker

1、docker-compose

sentinel:
  image: bladex/sentinel-dashboard
  container_name: sentinel-dashboard
  restart: always
  environment:
    JAVA_OPTS: "-Dserver.port=8858 -Dcsp.sentinel.dashboard.server=localhost:8858 -Dproject.name=sentinel-dashboard"
  ports:
    - "8858:8858"
  volumes:
    - /var/docker/server/sentinel/logs:/root/logs
2、启动
http://192.168.52.10:8858

二、Java

1. pom

<!-- 调用 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.2.8.RELEASE</version>
</dependency>
<!--  sentinel 的数据持久化  -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.6</version>
</dependency>

2、yml

spring:
  cloud:
    #流控、降级、热点、DashBoard及设置
    sentinel:
      transport:
        # 添加sentinel的控制台地址
        dashboard: 192.168.52.10:8858
        # 指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
        port: 8858
      # 默认将调用链路收敛,需要打开才可以进行链路流控
      web-context-unify: false
    filter:
      # 默认收敛所有URL的入口Context,因此链路限流此时不生效,若使用链路流控模式则设置为false
      enabled: true

三、Sentinel几种控制方式

(一)流控:流量控制

1、QPS+直接+快速失败

/trading/add 接口每秒内只能通过1个请求,其他请求会被限流,报错429

2. 线程数+直接+快速失败

针对并发,/trading/add 同时只能有1个线程执行,若有两个线程同时访问,则其中1个请求会被限流,报错429

3. QOS+关联+快速失败

当 /trading/add 接口 同时有1个以上线程访问时,则对 /trading/getPageList 接口进行限流,但不会对/trading/add进行限流。

场景:下单和支付接口进行关联。如若支付接口达到阈值,就限定下单接口少产生支付接口,缓解压力!

4. QOS+链路+快速失败 

当在 /trading/add 接口中访问 goods 普通方法时,1秒QPS达到2个及以上,则对 /trading/add 接口进行限流,但不对影响其它接口访问 goods 方法

//在普通方法goods上加注解
@SentinelResource("goods")

5. 流控效果

5.1 快速失败

达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。

5.2 Warm Up

预热模式,是应对服务冷启动的一种方案。适用于突然增大的流量转化为缓步增长。阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。

请求阈值初始值是 maxThreshold / coldFactor,持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3

例如,我设置QPS的阈值为10,预热时间为5秒,那么初始阈值就是 10 / 3 ,也就是3,然后在5秒后逐渐增长到10

5.3 排队等待

让请求以均匀的速率通过,单机阈值为每秒通过的请求数,其余的排队等待;其次,还可以设置一个排队等待的超时时间,若请求在超时时间内还未被处理,则会被丢弃。

(二)热点限流

统计请求参数值相同的请求,判断统计时长内是否超过QPS阈值。比如在并发请求 /trading/info 接口中判断相同参数的QOS不能超过某个阀值

//在请求接口Controller /info/{id}上加注解:
@SentinelResource(value = "hot", blockHandler = "fallback_hostkey")

//并定义如下限流返回方法
public String fallback_hostkey(String p1, String p2, BlockException blockException){
    return "限流咯。。。。";
}

 

 

(三)降级熔断

熔断建议设置监控接口里的Feign远程调用,因为一旦发生熔断和隔离是不允许外界访问该接口。监控远程调用是因为远程调用使用的feign-api模块对远程调用接口方法写了发生熔断和隔离时返回空对象。如果监控外部接口,一旦发生熔断则直接报错,阻止用户访问接口并不会返回空对象(因为该接口方法没写发生熔断和隔离时返回空对象)。

1、慢调用比例

当 1s 内持续进入 N 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx 来配置。

2、异常比例

当资源的每秒请求量 >= N(可配置),并且每秒异常总数占通过量的比值超过阈值(DegradeRule 中的 count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

3、异常数

当资源近 1 分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若 timeWindow 小于 60s,则结束熔断状态后仍可能再进入熔断状态。

(四)授权

授权规则可以对请求方来源做判断和控制。(通过判断请求方的请求头是否携带指定的参数来判断)

1. sentinel授权介绍

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式。

白名单:来源(origin)在白名单内的调用者允许访问

黑名单:来源(origin)在黑名单内的调用者不允许访问

点击左侧菜单的授权,可以看到授权规则:

资源名:就是受保护的资源,例如/order/

流控应用:是来源者的名单,

  如果是勾选白名单,则名单中的来源被许可访问。

  如果是勾选黑名单,则名单中的来源被禁止访问。
比如:

我们允许请求从gateway到order-service,不允许浏览器访问order-service,那么白名单中就要填写网关的来源名称(origin)。 

如何得到origin呢?Sentinel是通过RequestOriginParser这个接口的parseOrigin来获取请求的来源的

public interface RequestOriginParser {
    /**
     * 从请求request对象中获取origin,获取方式自定义
     */
    String parseOrigin(HttpServletRequest request);
}

这个方法的作用就是从request对象中,获取请求者的origin值并返回。

默认情况下,sentinel不管请求者从哪里来,返回值永远是default,也就是说一切请求的来源都被认为是一样的值default。

因此,我们需要自定义这个接口的实现,让不同的请求,返回不同的origin。

2. sentinel设置授权

2.1 给网关添加请求头

既然获取请求origin的方式是从reques-header中获取origin值,我们必须让所有从gateway路由到微服务的请求都带上origin头。

这个需要利用之前学习的一个GatewayFilter来实现,AddRequestHeaderGatewayFilter。

修改gateway服务中的application.yml,添加一个defaultFilter:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=origin,gateway #逗号前是key,后面是value
      routes:
       # ...略

这样,从gateway路由的所有请求都会带上origin头,值为gateway。而从其它地方到达微服务的请求则没有这个头。

2.2 获取请求的origin

例如order-service服务中,我们定义一个RequestOriginParser的实现类:我们会尝试从request-header中获取origin值。

@Component
public class HeaderOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 1.获取请求头
        String origin = request.getHeader("origin");
        // 2.非空判断
        if (StringUtils.isEmpty(origin)) {
            origin = "blank";
        }
        return origin;
    }
}

2.3 sentinel操作

我们添加一个授权规则,放行origin值为gateway的请求。配置如下:

 现在,我们直接跳过网关,访问order-service服务:

通过网关访问: 

、系统自适应限流

之前的配置操作,采取的是针对单个请求地址、请求别名进行配置的限流操作,sentinel同时也提供了一种很潮流的方式实现配置操作—系统自适应限流。

系统规则中的系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

Load 自适应(仅支持Linux/Unix-like)

系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。(系统核心数*2.5)

CPU usage(1.5.0+ 版本)

当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。

平均 RT

当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。

并发线程数

当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。

入口 QPS

当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。 

、自定义异常

默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。异常结果都是flow limmiting(限流)。这样不够友好,无法得知是限流还是降级还是授权拦截。

@Component
public class SentinelExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;

        if (e instanceof FlowException) {
            msg = "请求被限流了";
        } else if (e instanceof ParamFlowException) {
            msg = "请求被热点参数限流";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级了";
        } else if (e instanceof AuthorityException) {
            msg = "没有权限访问";
            status = 401;
        }

        response.setContentType("application/json;charset=utf-8");
        response.setStatus(status);
        response.getWriter().println("{\"msg\": " + msg + ", \"status\": " + status + "}");
    }
}

、规则持久化

sentinel的所有规则都是内存存储,重启后所有规则都会丢失。在生产环境下,我们必须确保这些规则的持久化,避免丢失。

1、规则管理模式

规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式:

  • 原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
  • pull模式(存储各服务器本地,一定时间内会轮询检查规则并更新)
  • push模式(存储在nacos注册中心)【推荐】

2. pull模式

pull模式:控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。

3. push模式

push模式:控制台将配置规则推送到远程配置中心,例如Nacos。Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。 

 

posted @ 2023-08-16 19:51  yifanSJ  阅读(243)  评论(0编辑  收藏  举报