Sentinel限流实战

一,应用场景:开放平台提供url给第三方调用时,针对调用方请求进行限流,实现对ip和参数进行限流

两种实现方式

1,对SentinelGatewayBlockExceptionHandler进行改造,新创建一个该类,重写里面的返回方法

可参考:http://www.cppcns.com/ruanjian/java/257764.html

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
    // Register the block exception handler for Spring Cloud Gateway.
    return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}

2,平台实现,自定义响应处理类,实现BlockRequestHandler接口,包装返回信息,初始化调用自定义响应处理类

参考:https://blog.csdn.net/developlee/article/details/100987345

您可以在 GatewayCallbackManager 注册回调进行定制:

  • setBlockHandler:注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为 BlockRequestHandler。默认实现为 DefaultBlockRequestHandler,当被限流时会返回类似于下面的错误信息:Blocked by Sentinel: FlowException

二,代码实现,改造SentinelGatewayBlockExceptionHandler

1,Sentinel配置类

/**
 * sentinel配置
 *
 * @author lr
 * @date 2019-09-12 13:31
 */
@Configuration
public class SentinelGatewayConfiguration {

    @Autowired
    private InetUtils inetUtils;

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

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

//    @Bean
//    @Order(Ordered.HIGHEST_PRECEDENCE)
//    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
//        // Register the block exception handler for Spring Cloud Gateway.
//        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
//    }

    /**
     * 自定义响应参数
     *
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
        return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initGatewayRules();
        //GatewayCallbackManager.setBlockHandler(new OpenBlockRequestHandler());
        //设置监控ip(多网卡时默认获取有问题,所以需要采用springCloud网卡工具类)
        SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, inetUtils.findFirstNonLoopbackAddress().getHostAddress());
    }

    /**
     * 初始化路由规则
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        //限制每个ip每秒只能调用5次,
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
                )
        );

        //限制一个应用appId每秒只能调用5次
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("appId")
                )
        );

        //限制一个应用appId每秒只能调用5次
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("appId").setPattern("testApp")
                )
        );

        GatewayRuleManager.loadRules(rules);
    }
}

2,自定义响应类

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) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        JSONObject responseObj = new JSONObject();
        responseObj.put("code", 403);
        responseObj.put("msg", "请求过于频繁,请稍后重试");
        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);
    }

}

 三,代码实现,GatewayCallbackManager回调

1,Sentinel配置类

/**
 * sentinel配置
 *
 * @author lr
 * @date 2019-09-12 13:31
 */
@Configuration
public class SentinelGatewayConfiguration {

    @Autowired
    private InetUtils inetUtils;

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

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

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 自定义响应参数
     *
     * @return
     */
//    @Bean
//    @Order(Ordered.HIGHEST_PRECEDENCE)
//    public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
//        return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
//    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initGatewayRules();
        GatewayCallbackManager.setBlockHandler(new OpenBlockRequestHandler());
        //设置监控ip(多网卡时默认获取有问题,所以需要采用springCloud网卡工具类)
        SentinelConfig.setConfig(TransportConfig.HEARTBEAT_CLIENT_IP, inetUtils.findFirstNonLoopbackAddress().getHostAddress());
    }

    /**
     * 初始化路由规则
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        //限制每个ip每秒只能调用5次,
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
                )
        );

        //限制一个应用appId每秒只能调用5次
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("appId")
                )
        );

        //限制一个应用appId每秒只能调用5次
        //setBurst突发请求额外增加5个
        rules.add(new GatewayFlowRule("open_gateway")
                .setCount(5)
                .setIntervalSec(1)
                .setBurst(5)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("appId").setPattern("testApp")
                )
        );

        GatewayRuleManager.loadRules(rules);
    }
}

2,自定义响应消息类

/**
 * @author lr
 * @date 2019-09-12 15:15
 */
public class OpenBlockRequestHandler implements BlockRequestHandler {

    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {
        // JSON result by default.
        return ServerResponse.status(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(fromObject(buildErrorResult(ex)));
    }


    private ResponseBean buildErrorResult(Throwable ex) {
        return ResponseBeanUtils.getResponseBean(ErrorCode.REQUEST_QPS_LIMIT_ERROR);
    }
}

四,postman调用结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值