美文网首页
代码配置Sentinel

代码配置Sentinel

作者: 笔记本一号 | 来源:发表于2023-12-13 23:32 被阅读0次

配置说明参考: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);
    }

}

相关文章

网友评论

      本文标题:代码配置Sentinel

      本文链接:https://www.haomeiwen.com/subject/eyeigdtx.html