代码配置Sentinel

配置说明参考:https://blog.csdn.net/m0_46379371/article/details/114904351
使用的依赖包:

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
            <version>2021.1</version>
        </dependency>

需求:对不同的应用进行不一样的限流策略,并且对特定接口进行通用限流

实现方法:不同应用发送的请求时,由于项目的拦截器在sentinel的拦截器之前,我会拦截所有的请求,在请求头中设置key为LimitRulesKey,value为应用code的键值对。由于项目初始化后,会读取配置配置在redis中的各个应用的限流策略并加载到sentinel中,sentinel会对匹配中拦截规则(例如正则匹配,前缀匹配等)的请求中获取请求的LimitRulesKey键值对,拿redis中的应用的code与LimitRulesKey键值对中的value进行比较,相等就进行此应用code对应的限流策略,不相等就进行配置接口的通用限流。sentinel每30分钟读取一次redis的应用限流策略,更新已配置的限流策略
sentinel的配置类

@Configuration
@Slf4j
public class SentinelConfiguration {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CommonAppLimit commonAppLimit;

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;


    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
        return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }


    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilterConfiguration() {
        return new SentinelTsfGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initCustomizedApis();
        initGatewayRules();
        List<AppNode> appNodes = initPropertyFromRedis();
        if (appNodes.size() > 0) {
            initCustomizedApisFromRedis(appNodes);
            initGatewayRulesFromRedis(appNodes);
        }
    }

    private List<AppNode> initPropertyFromRedis() {
        List<AppNode> appNodes = new ArrayList<>();
        try {
            log.info("=========执行initPropertyFromRedis方法=========");
            Set<String> keys = stringRedisTemplate.keys("sentinel*");
            if (Objects.isNull(keys)) {
                return appNodes;
            }
            for (String key : keys) {
                try {
                    String sentinelConfig = stringRedisTemplate.opsForValue().get(key);
                    if (StringUtils.isEmpty(sentinelConfig)) {
                        continue;
                    }
                    log.info("initPropertyFromRedis的sentinelConfig:" + sentinelConfig);
                    AppNode appNode = JSON.parseObject(sentinelConfig, AppNode.class);
                    appNodes.add(appNode);
                } catch (Exception e) {
                    log.error("sentinel配置反序列化失败:", e);
                }
            }
        } catch (Exception e) {
            log.error("读取redis错误", e);
        }
        return appNodes;
    }


    private void initCustomizedApis() {
        if (Objects.isNull(commonAppLimit)) {
            return;
        }
        if (commonAppLimit.getOpen()) {
            Set<ApiDefinition> definitions = new HashSet<>();
            ApiDefinition api = new ApiDefinition(commonAppLimit.getFlowRule());
            HashSet<ApiPredicateItem> apiPredicateItems = new HashSet<>();
            if (commonAppLimit.getUrls() != null && commonAppLimit.getUrls().size() > 0) {
                for (String limitUrl : commonAppLimit.getUrls()) {
                    apiPredicateItems.add(new ApiPathPredicateItem().setPattern(limitUrl)
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
                }
            }
            if (commonAppLimit.getPreUrls() != null && commonAppLimit.getPreUrls().size() > 0) {
                for (String limitUrl : commonAppLimit.getPreUrls()) {
                    apiPredicateItems.add(new ApiPathPredicateItem().setPattern(limitUrl)
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }
            }
            api.setPredicateItems(apiPredicateItems);
            definitions.add(api);
            GatewayApiDefinitionManager.loadApiDefinitions(definitions);
        }
        log.info("initCustomizedApis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
    }

    private void initGatewayRules() {
        if (Objects.isNull(commonAppLimit)) {
            return;
        }
        if (commonAppLimit.getOpen()) {
            Set<GatewayFlowRule> rules = new HashSet<>();
            rules.add(new GatewayFlowRule(commonAppLimit.getFlowRule())
                    .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                    .setCount(commonAppLimit.getCount())
                    .setBurst(commonAppLimit.getBurst())
                    .setIntervalSec(commonAppLimit.getIntervalSec())
                    .setParamItem(new GatewayParamFlowItem()
                            .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
                            .setFieldName("LimitRulesKey"))
            );
            GatewayRuleManager.loadRules(rules);
        }
        log.info("initCustomizedApis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
    }


    private void initCustomizedApisFromRedis(List<AppNode> appNodes) {
        Set<ApiDefinition> definitions = GatewayApiDefinitionManager.getApiDefinitions();
        if (Objects.nonNull(appNodes) && appNodes.size() > 0) {
            for (AppNode appNode : appNodes) {
                ApiDefinition api = new ApiDefinition(appNode.getFlowRule());
                if (Objects.nonNull(appNode.getUrls()) && appNode.getUrls().size() > 0) {
                    HashSet<ApiPredicateItem> apiPredicateItems = new HashSet<>();
                    for (String testUrl : appNode.getUrls()) {
                        apiPredicateItems.add(new ApiPathPredicateItem().setPattern(testUrl)
                                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                    }
                    api.setPredicateItems(apiPredicateItems);
                }
                definitions.add(api);
            }
        }
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
        log.info("initCustomizedApisFromRedis方法的GatewayApiDefinitionManager:" + GatewayApiDefinitionManager.getApiDefinitions());
    }

    private void initGatewayRulesFromRedis(List<AppNode> appNodes) {
        Set<GatewayFlowRule> rules = GatewayRuleManager.getRules();
        if (Objects.nonNull(appNodes) && appNodes.size() > 0) {
            for (AppNode appNode : appNodes) {
                rules.add(new GatewayFlowRule(appNode.getFlowRule())
                        .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                        .setCount(appNode.getCount())
                        .setBurst(appNode.getBurst())
                        .setIntervalSec(appNode.getIntervalSec())
                        .setParamItem(new GatewayParamFlowItem()
                                .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
                                .setFieldName("LimitRulesKey")
                                .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
                                .setPattern(appNode.getCode())
                        )
                );
            }
        }
        GatewayRuleManager.loadRules(rules);
        log.info("initGatewayRulesFromRedis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
    }


    @Scheduled(fixedDelay = 30 * 60 * 1000, initialDelay = 30 * 60 * 1000)
    public void loadSentinelConfig() {
        log.info("=======执行loadSentinelConfig方法=========");
        List<AppNode> appNodes = initPropertyFromRedis();
        if (appNodes.size() == 0) {
            return;
        }
        log.info("appNodes:" + appNodes);
        initCustomizedApisFromRedis(appNodes);
        initGatewayRulesFromRedis(appNodes);
    }
}

自定义的handle


@Slf4j
public class JsonSentinelGatewayBlockExceptionHandler  implements WebExceptionHandler {
    private List<ViewResolver> viewResolvers;
    private List<HttpMessageWriter<?>> messageWriters;

    private final Supplier<ServerResponse.Context> contextSupplier = () -> {
        return new ServerResponse.Context() {
            public List<HttpMessageWriter<?>> messageWriters() {
                return JsonSentinelGatewayBlockExceptionHandler.this.messageWriters;
            }

            public List<ViewResolver> viewResolvers() {
                return JsonSentinelGatewayBlockExceptionHandler.this.viewResolvers;
            }
        };
    };

    public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.messageWriters = serverCodecConfigurer.getWriters();
    }

    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
        log.info("执行JsonSentinelGatewayBlockExceptionHandler的writeResponse方法");
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        JSONObject responseObj = new JSONObject();
        responseObj.put("code", 444);
        responseObj.put("message", "请求过于频繁,请稍后重试");
        responseObj.put("data", null);
        byte[] datas = responseObj.toString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        if (exchange.getResponse().isCommitted()) {
            return Mono.error(ex);
        } else {
            return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
                return this.writeResponse(response, exchange);
            });
        }
    }

    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
        return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容