Sentinel 限流

作者: 扮鬼之梦 | 来源:发表于2020-12-08 18:16 被阅读0次

    官方文档

    https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

    Feign 中 使用 Sentinel 断路器

    https://www.jianshu.com/p/8e769e365f88

    在 SpringClouldGateway 的过滤器中进行QPS限流

    引入依赖

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

    添加限流的过滤器

    LimitFilter

    import com.alibaba.csp.sentinel.Entry;
    import com.alibaba.csp.sentinel.SphU;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.RuleConstant;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
    import com.gnl.commonlib.error.SystemError;
    import com.gnl.gateway.util.HttpDataExtractor;
    import lombok.extern.log4j.Log4j2;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.ArrayList;
    import java.util.List;
    
    
    @Log4j2
    @Component
    public class LimitFilter implements GlobalFilter, Ordered {
    
        @Override
        public int getOrder() {
            // 返回值用来指定执行的顺序,数字越小,优先级越高
            return 1;
        }
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            String method = HttpDataExtractor.getMethod(exchange);
            String path = HttpDataExtractor.getPath(exchange);
            String userId = HttpDataExtractor.getHeader(exchange, "userId");
    
            // 被限流的资源名称(例如:GET /auth-service/auth/userInfo 123456)
            String resourceName = method + " " + path + " " + userId;
    
            // 不需要登录的接口,直接放行
            if (userId == null) {
                log.info(resourceName + " pass");
                return chain.filter(exchange);
            }
    
            // 初始化限流规则
            initFlowRules(resourceName);
            Entry entry = null;
            try {
                // 设置被限流的资源名称
                entry = SphU.entry(resourceName);
            } catch (BlockException e) {
                // 被限流时捕获BlockException,直接返回系统繁忙
                log.info(resourceName + " block");
                return HttpDataExtractor.returnData(SystemError.SYSTEM_IS_BUSY, exchange);
            } finally {
                if (entry != null) {
                    entry.exit();
                }
            }
    
            // 未被限流,直接访问资源
            log.info(resourceName + " pass");
            return chain.filter(exchange);
        }
    
        private static void initFlowRules(String resourceName) {
            List<FlowRule> rules = new ArrayList<>();
            FlowRule rule = new FlowRule();
            rule.setResource(resourceName);
            rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
            // 设置限流的 QPS 为 3,结合resourceName(GET /auth-service/auth/userInfo 666666),表示为用户(666666)访问接口GET /auth-service/auth/userInfo的QPS大于3时,就会被限流
            rule.setCount(3);
            rules.add(rule);
            FlowRuleManager.loadRules(rules);
        }
    
    }
    

    HttpDataExtractor

    import com.alibaba.fastjson.JSON;
    import com.gnl.commonlib.base.BaseError;
    import com.gnl.commonlib.base.BaseResponse;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    import java.util.function.Consumer;
    
    public class HttpDataExtractor {
    
        /**
         * 获取请求头
         */
        public static String getHeader(ServerWebExchange exchange, String key) {
            ServerHttpRequest request = exchange.getRequest();
            List<String> list = request.getHeaders().get(key);
            if (list == null || list.size() == 0) {
                return null;
            }
            return list.get(0);
        }
    
        /**
         * 获得访问路径
         */
        public static String getPath(ServerWebExchange exchange) {
            ServerHttpRequest request = exchange.getRequest();
            return request.getURI().getPath();
        }
    
        /**
         * 获取请求方法
         */
        public static String getMethod(ServerWebExchange exchange) {
            ServerHttpRequest request = exchange.getRequest();
            return request.getMethod().toString();
        }
    
        /**
         * 添加请求头
         */
        public static void addHeader(ServerWebExchange exchange, Consumer<HttpHeaders> httpHeaders) {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders).build();
            exchange.mutate().request(serverHttpRequest).build();
        }
    
        /**
         * 返回值的封装
         */
        public static Mono<Void> returnData(BaseError baseError, ServerWebExchange exchange) {
            ServerHttpResponse response = exchange.getResponse();
            BaseResponse data = BaseResponse.error(baseError);
            byte[] datas = JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(datas);
    
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
    
            return response.writeWith(Mono.just(buffer));
        }
    }
    

    效果

    使用jMeter进行压测,0秒发送10个请求,只有三个成功访问了资源,后面七个直接返回了系统繁忙


    image

    成功返回的信息


    image
    被限流返回的信息
    image

    相关文章

      网友评论

        本文标题:Sentinel 限流

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