SpringCloud Alibaba 之 Sentinel

接上:

SpringCloud 之 feign_初涉人事。。。-优快云博客

什么是Sentinel?

是阿里开源的一套用于服务容错的总和性解决方案,它以流量为切入点。

从流量控制、熔断降级、系统负载保护等多个纬度来保护服务的稳定性。

容错的思想:

1、保证自己不被上游服务压垮  例如: 限流,减少单位时间内的访问量

2、保证自己不被下游服务拖垮  例如:熔断降级,减少或者不访问下游,等下游正常再访问

3、保证外界环境良好  例如:服务器、cpu、内存

Sentinel特征:介绍 · alibaba/Sentinel Wiki · GitHub

1、丰富的应用场景

      如秒杀(突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等

2、完备的实时监控

      sentinel提供了实时监控功能。通过控制台可以看到接入应用的单台机器秒级数据,甚至500台一下规模的集群的汇总运行情况

3、广泛的开源生态

        提供了开箱即用与其他开源库/框架的整合,如:SpringCloud、dubbo、grpcdeng

        只要引入相应的依赖进行简单的配置即可快读的接入

4、完善的SPI扩展

      提供了简单、易用、完善的SPI扩展接口,可以通过实现扩展接口来快速定值逻辑。

       如定值规则管理、适配动态数据源等

Sentinel分为两个部分:

核心库: 不依赖任何框架/库能够运行与所有java运行时环境,同时对Dubbo/SpringCloud等框架也有较好的支持

控制台:基于springboot开发,打包后可直接运行,不需要额外的Tomcat等应用容器

控制台:

Releases · alibaba/Sentinel · GitHub

直接下载jar包运行即可

或者git下载项目找到sentinel-dashboard 添加端口 直接运行

访问:localhost:端口

用户名密码默认都是:sentinel

集成:

在项目中引入依赖:

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

配置文件:

spring:
    cloud:
        sentinel:
          transport:
            port: 8081   #可随意指定一个不冲突的端口,用于跟控制台交流的端口
            dashboard: localhost:8080 # 指定控制台服务

重启服务:如果刷新还是没有数据,那就访问一下对应的服务,跟sentinel的加载机制有关

刷新sentinel控制台

sentinel 基本概念和功能

资源:

资源就是sentinel要保护的东西。

资源是sentinel的关键概念,它可以是java应用程序中的任何内容,可以是一个服务、一个方法、一段代码

规则:

作用在资源之上,定义以什么样的方式保护资源,主要包括:

流量控制规则、熔断降级规则、系统保护规则

sentinel 主要功能

sentinel的主要功能就是容错,主要提现在下面三种方式

1、流量控制:

sentinel可以根据需要把随机的请求调整成合适的形式

2、熔断降级:

检测到调用链路中某个资源出现不稳定的表现(如:请求响应时间长或者异常比例升高),则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源导致级联故障

sentient对熔断提供了两种方案:

1:通过并发线程数进行限制

2:通过响应时间对资源进行降级。通过响应时间来快速降级不稳定的资源,当依赖的资源出现响应故障后,所有对该资源的访问都会被直接拒绝,直到符合指定的时间窗口之后才会重新回复

与hystrix:

相同点:原则是一致的,当一个资源出现问题时快速让其失败,不波及到其他服

区别:

hystrix采用的是线程池的隔离方式,优点是做到了资源之间的隔离,缺点是增加了线程切换成本

sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制

3、系统负载保护

当系统负载较高的时候,还持续进行访问,可能导致系统崩溃,无法响应,在集群环境下,会把本应当前机器成灾的流量转发到其他的机器上去,如果其他机器也处于一个边缘状态,sentinel会提供对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围内处理最多的请求

总之对sentinel的使用最多的就是对sentinel的资源上进行各种规则配置,来实现各种容错的功能

sentinel规则

1、流控规则

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

1.1、点击簇点链路---》流控

规则设置: 

资源名:唯一名称,默认是请求路径,可自定义

针对来源:指定对哪个服务进行限流,默认是不区分,全部限制。(A、B 服务对C服务访问,可设置A的流量是多少,B又是多少,可分开限制,如果默认,则全部限制)

阈值类型/单机阈值:

        QPS:(每秒请求数量),当该接口QPS达到阈值时,进行限流

        线程数据:当调用接口的线程达到阈值的时候,进行限流

流控模式:

直接:默认,接口达到限流条件时,开启限流

关联:当关联的资源达到限流条件时,开启限流

         例:接口a关联接口b当接口b达到限流时a接口开启限流

         适合用于查询和更新接口关联,当更新接口达到阈值时,限制查询

链路:当从某个接口过来的资源达到限流条件时,开启限流,针对的是上级接口/微服务

链路需要在业务层添加注解,控制其中多个方法访问该接口 

  ​​​​​​

 实际测试:

 链路不生效

解决方式:

配置文件添加配置:

cloud:
  sentinel:
    filter:
      enabled: false

添加filter配置:

package wdz.order.config;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SentinelFilterContextConfig {

    @Bean
    public FilterRegistrationBean sentinelFilterRegistration(){
        FilterRegistrationBean filter = new FilterRegistrationBean();
        filter.setFilter(new CommonFilter());
        filter.addUrlPatterns("/*");
        // 入口资源关闭聚合
        filter.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
        filter.setName("sentinelFilter");
        filter.setOrder(1);
        return filter;
    }

}

 生效结果:如果测试阈值超过了设置的阈值则快速报错

流控效果:

快速失败:

在阈值以外的数据,直接失败,抛出异常,不做任何额外的处理

Warm Up:

它从开始阈值到最大QPS有一个缓冲阶段,一开始的阈值时最大QPS的1/3,然后慢慢增长到最大阈值,适用于将突然增大的流量转换为缓步增长的场景

排队等待:

让请求均匀的通过,超过阈值的请求排队等待,当请求超过预设超时时间还未处理的,则会被丢弃

熔断规则:

降级策略:

RT:平均响应时间:当资源的平均响应时间超过阈值(单位:ms)之后,资源进入准降级状态。在之后的时间窗口内(单位:s)就会对这个资源进行服务降级.                                最大RT值:4900ms,如果超过这个值默认读取这个参数,如果要配置该参数,通过启动配置项来更改:-Dscp.sentinel.statistic.max.rt=******* 来配置

例如:当平均响应时间>1ms的时候,接下来的10s内服务降级,10s之后服务恢复,进行下一轮的判断

异常比例:

挡子源的每秒异常总数占通过量的比值超过阈值之后,资源进行降级状态。即在接下的时间窗口(单位:s)之内,对这个方法的调用都会自动返回,异常比率的阈值范围是[0,0,1,0]

异常数:

在统计的时间内有超过当前设置的异常数时,并且最小请求为5个,降级5秒,5秒之后恢复正常,进行新的一轮判断 

热点规则:

注意热点规则必须在方法上添加资源注解

 

热点必须添加在资源注解上,否则不生效,如下配置只对接口参数索引为0的(param1)也就是请求中包含第一个参数的接口在阈值之外进行限制,而不包含索引0param1)参数的请求正常访问

 以上是对参数进行熔断的,下面的是直接对参数值进行熔断,当参数值为123时超过阈值进行熔断,其他参数则根据上边的熔断规则进行熔断

 授权规则:

 

流控应用:

该参数自定义即可,请求时可存放于request中,方便获取即可

后台代码:

package wdz.order.config;

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;

/**
 * sentinel 授权规则配置
 */
@Configuration
public class SentinelOriginAuthConfig implements RequestOriginParser {
    /**
     * 定义区分来源
     * 本职作用是通过request域来获取来源标识
     * 所以这个来源标识和自定义进行配置,存放与请求头/请求体
     */
    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 如果使用授权方式,则该参数必须传递,否则无效
        String applicationName = request.getParameter("applicationName");
        if (StringUtils.isEmpty(applicationName)){
            throw new RuntimeException("applicationName不能为空");
        }
        return applicationName;
    }
}

配置之后 黑白名单相反,符合条件的直接通过,否则抛出异常

 以上的流控、热点、熔断、授权都是对上游/下游的配置,满足容错思想的1和2

系统授权规则

该规则则是满足于容错思想的3,系统环境的影响

 系统保护规则是从应用级别的入口流量进行控制。从单台机器的总体Load、RT、入口QPS、CPU使用率和线程数五个纬度监控应用数据。让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,不是上边的资源纬度的,并且仅对入口流量(进入应用的流量)生效。

Load:仅对Linux/Unix-like机器生效,当系统load1超过阈值,且系统当前的并发线程超过系统容量时才会触发系统保护,系统容量有系统的maxQPS*minRT计算得出,设定参考值一般是CPU cores*2.5

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

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

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

CPU使用率:单台机器上所有入口流量的CPU使用率达到阈值即触发系统保护

自定义触发保护时的异常返回:

package wdz.order.config;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
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 com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * sentinel 触发保护之后返回信息处理
 */
@Component
public class SentinelExceptionPage implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        // 中文乱码问题
        response.setContentType("application/json;charset=utf-8");
        ResponseData data = null;
        if (e instanceof FlowException) {
            data = new ResponseData(-1,"资源限流异常");
        }else if (e instanceof DegradeException) {
            data = new ResponseData(-1,"资源降级异常");
        }else if (e instanceof ParamFlowException) {
            data = new ResponseData(-1,"资源参数限流异常");
        }else if (e instanceof AuthorityException) {
            data = new ResponseData(-1,"资源授权异常");
        }else if (e instanceof SystemBlockException) {
            data = new ResponseData(-1,"系统负载异常");
        }
        response.getWriter().write(JSONObject.toJSONString(data));
    }
}

@Data
@AllArgsConstructor // 全参构造
@NoArgsConstructor  // 无参构造
class ResponseData {

    private int code;

    private String message;

}

 测试结果:

 @SentinelResource作用

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.alibaba.csp.sentinel.annotation;

import com.alibaba.csp.sentinel.EntryType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
    // 资源名称
    String value() default "";

    EntryType entryType() default EntryType.OUT;

    int resourceType() default 0;
    // 定义当资源内部发生了BlockException 应该进入的方法,捕获的是Sentinel定义的异常
    String blockHandler() default "";
    // 定义当资源内部发生了BlockException 应该进入的公用handler类
    Class<?>[] blockHandlerClass() default {};
    // 定义当资源内部发生了Throwable 应该进入的方法
    String fallback() default "";

    String defaultFallback() default "";
    // 定义当资源内部发生了Throwable 应该进入的类 用于
    Class<?>[] fallbackClass() default {};

    Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};

    Class<? extends Throwable>[] exceptionsToIgnore() default {};
}

1:定义一个资源

2:定义当资源内部发生异常时的处理逻辑,建议使用

fallback处理指定方法的所有异常处理
 @RequestMapping("heart")
    @SentinelResource(value = "heart-sentinel", blockHandler = "blockHandler", fallback = "fallback")
    public String heart(String param1, String param2) {
        return param1 + param2;
    }

    /**
     * 处理的是SentinelResource中定义的5个异常
     * 要求:
     * 1:返回参数必须与SentinelResource注解所在的方法返回值一致,接收的参数一致
     * 2:方法名必须与注解中blockHandler属性值一致
     * <p>
     * 允许参数在参数列表的最后加入一个参数BlockExceprion,用来接收原方法中发生的异常
     *
     * @return
     */
    public String blockHandler(String param1, String param2, BlockException exceprion) {
        // 自定义异常业务处理
        log.info("-----异常BlockException----:{}", exceprion);
        return "BlockException";
    }

    /**
     * 处理的是所有的异常
     * 要求:
     * 1:返回参数必须与SentinelResource注解所在的方法返回值一致,接收的参数一致
     * 2:方法名必须与注解中blockHandler属性值一致
     * <p>
     * 允许参数在参数列表的最后加入一个参数Throwable,用来接收原方法中抛出的异常
     *
     * @return
     */
    public String fallback(String param1, String param2, Throwable throwable) {
        // 自定义异常业务处理
        log.info("-----异常Throwable----:{}", throwable);
        return "throwable";
    }

 

通过抽取独立处理方式

package wdz.order.config;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SentinelHandler {


    /**
     * 处理的是SentinelResource中定义的5个异常
     * 要求:
     * 1:返回参数必须与SentinelResource注解所在的方法返回值一致,接收的参数一致
     * 2:方法名必须与注解中blockHandler属性值一致
     * <p>
     * 允许参数在参数列表的最后加入一个参数BlockExceprion,用来接收原方法中发生的异常
     * 注意:方法抽取后需要static,否则不生效
     * @return
     */
    public static String blockHandler(String param1, String param2, BlockException exceprion) {
        // 自定义异常业务处理
        log.info("-----异常BlockException----:{}", exceprion);
        return "BlockException";
    }

}
package wdz.order.config;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SentinelFallback {


    /**
     * 处理的是所有的异常
     * 要求:
     * 1:返回参数必须与SentinelResource注解所在的方法返回值一致,接收的参数一致
     * 2:方法名必须与注解中blockHandler属性值一致
     * <p>
     * 允许参数在参数列表的最后加入一个参数Throwable,用来接收原方法中抛出的异常
     * 注意:方法抽取后需要static,否则不生效
     * @return
     */
    public static String fallback(String param1, String param2, Throwable throwable) {
        // 自定义异常业务处理
        log.info("-----异常Throwable----:{}", throwable);
        return "throwable";
    }

}

    @RequestMapping("heart")
    @SentinelResource(value = "heart-sentinel",
            blockHandlerClass = SentinelHandler.class,
            blockHandler = "blockHandler",
            // 成对出现的方式配置抽取
            fallbackClass = SentinelFallback.class,
            fallback = "fallback"
    )
    public String heart(String param1, String param2) {
        return param1 + param2;
    }

Sentinel 持久化

sentinel 规则默认是在内存中的,重启服务后规则全部清空,

持久化:当设置规则之后在存储内存的同时发送一份到所依赖的服务中,进行持久化

1、代码:

package wdz.order.config;

import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;

import java.io.File;
import java.io.IOException;
import java.util.List;

import static com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager.*;

public class FilePersistence implements InitFunc {

    @Value("spring.application:name")
    private String appcationName;

    @Override
    public void init() throws Exception {
        //指定文件生成目录
        String ruleDir = System.getProperty("user.home") + "/sentinelrules/" + appcationName;
        String flowRulePath = ruleDir + "/flow-rule.json";
        String degradeRulePath = ruleDir + "/degrade-rule.json";
        String systemRulePath = ruleDir + "/system-rule.json";
        String authorityRulePath =
                ruleDir + "/authority-rule.json";
        String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
        this.mkdirIfNotExits(ruleDir);
        this.createFileIfNotExits(flowRulePath);
        this.createFileIfNotExits(degradeRulePath);
        this.createFileIfNotExits(systemRulePath);
        this.createFileIfNotExits(authorityRulePath);
        this.createFileIfNotExits(paramFlowRulePath);
        // 流控规则
        ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(flowRulePath, flowRuleListParser);

        FlowRuleManager.register2Property(flowRuleRDS.getProperty());
        WritableDataSource<List<FlowRule>> flowRuleWDS = new
                FileWritableDataSource<>(
                flowRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new
                FileRefreshableDataSource<>(
                degradeRulePath,
                degradeRuleListParser
        );
        register2Property(degradeRuleRDS.getProperty());
        WritableDataSource<List<DegradeRule>> degradeRuleWDS = new
                FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
        // 系统规则
        ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new
                FileRefreshableDataSource<>(
                systemRulePath,
                systemRuleListParser);

        SystemRuleManager.register2Property(systemRuleRDS.getProperty());
        WritableDataSource<List<SystemRule>> systemRuleWDS = new
                FileWritableDataSource<>(
                systemRulePath,
                this::encodeJson);
        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
        // 授权规则
        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new
                FileRefreshableDataSource<>(
                authorityRulePath,
                authorityRuleListParser
        );
        AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
        WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new
                FileWritableDataSource<>(
                authorityRulePath,
                this::encodeJson
        );
        WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
        // 热点参数规则
        ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new
                FileRefreshableDataSource<>(
                paramFlowRulePath,
                paramFlowRuleListParser
        );
        ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
        WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new
                FileWritableDataSource<>(paramFlowRulePath,
                this::encodeJson
        );

        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
    }

    private Converter<String, List<FlowRule>> flowRuleListParser = source ->
            JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
            });
    private Converter<String, List<DegradeRule>> degradeRuleListParser = source
            -> JSON.parseObject(
            source,
            new TypeReference<List<DegradeRule>>() {
            });
    private Converter<String, List<SystemRule>> systemRuleListParser = source ->
            JSON.parseObject(
                    source,
                    new TypeReference<List<SystemRule>>() {
                    });
    private Converter<String, List<AuthorityRule>> authorityRuleListParser =
            source -> JSON.parseObject(
                    source,
                    new TypeReference<List<AuthorityRule>>() {
                    });


    private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser =
            source -> JSON.parseObject(
                    source,
                    new TypeReference<List<ParamFlowRule>>() {
                    });

    private void mkdirIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    private void createFileIfNotExits(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
    }

    private <T> String encodeJson(T t) {
        return JSON.toJSONString(t);
    }

}

2、配置:

在resources下创建文件夹:META-INF.services

创建文件:com.alibaba.csp.sentinel.init.InitFunc   # 上边代码实现类全路径

写入内容:wdz.order.config.FilePersistence   # 上边代码全路径

sentinel 网关限流 gateway

1.6版本开始,提供了gateway 适配模块,提供两种资源纬度的限流

1:route维度:在spring配置文件的路由条目,资源名对应的routeId

2:自定义API维度:利用sentinel提供的API来定义自己的一些API分组

route维度方式:

引入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

添加配置代码:

package com.wdz.cloud.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

@Configuration
public class SentinelGatewayConfig {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public SentinelGatewayConfig(ObjectProvider<List<ViewResolver>> viewResolverProvider,
                                 ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolverProvider.getIfAvailable();
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    // 初始化限流过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    // 配置初始化限流参数
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                new GatewayFlowRule("wdz_product") // 路由中的id
                        .setCount(1) // 限流阈值
                        .setIntervalSec(1) // 统计时间窗口(多长时间为单位进行统计),单位是秒,默认1秒

        );
        GatewayRuleManager.loadRules(rules);
    }

    // 配置限流异常处理类
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    // 自定义限流异常处理
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", "303");
                map.put("message", "流量超载,暂时限流");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

当访问配置限流的路由时,超出阈值返回异常配置

自定义维度:将下面代码更新到上面代码即可

 // 配置初始化限流参数
    @PostConstruct
    public void initGatewayRules() {
        // 使用路由限流初始化设置
//        Set<GatewayFlowRule> rules = new HashSet<>();
//        rules.add(
//                new GatewayFlowRule("wdz_product")
//                        .setCount(1) // 限流阈值
//                        .setIntervalSec(1) // 统计时间窗口(多长时间为单位进行统计),单位是秒,默认1秒
//
//        );
//        GatewayRuleManager.loadRules(rules);
        // 使用自定义限流设置
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("api001").setCount(1).setIntervalSec(1));
        rules.add(new GatewayFlowRule("api002").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }
// 自定义API分组
    @PostConstruct
    private void initCustomeizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        // HashSet<ApiPredicateItem> predicateItems = new HashSet<ApiPredicateItem>();
        // ApiPathPredicateItem e = new ApiPathPredicateItem().setPattern("/prudect/**");
        // e.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);
        // predicateItems.add(e);
        // ApiDefinition apiDefinition = new ApiDefinition("").setPredicateItems(predicateItems);
        // apiName 可随意设置,不重复即可
        ApiDefinition apiDefinition = new ApiDefinition("api001")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    ApiPathPredicateItem e = new ApiPathPredicateItem().setPattern("/product/query/api_1/**");
                    e.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);
                    add(e);
                }});
        ApiDefinition apiDefinition2 = new ApiDefinition("api002")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    // 配置规则,域名后的uri
                    ApiPathPredicateItem e = new ApiPathPredicateItem().setPattern("/product/query/api2/**");
                    e.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX);
                    add(e);
                }});
        definitions.add(apiDefinition);
        definitions.add(apiDefinition2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值