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