官网地址 https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81
Sentinel 1.6.0发布了Sentinel API网关适配器通用模块,此模块中包含网关限流的规则和自定义API的实体和管理逻辑:
-
GatewayFlowRule:网关限流规则,针对API Gateway的场景定制的限流规则,可以针对不同的路由或自定义的API分组进行限流,支持针对请求中的参数,标头,来源IP等进行定制化的限流。
-
ApiDefinition:用户自定义的API定义分组,可以看做是一些URL匹配的组合。就像我们可以定义一个API一样my_api,请求路径模式为/foo/**和/baz/**的都归到my_api这个API分组。限流的时候可以针对这个自定义的API分组维度进行限流。
其中网关限流规则GatewayFlowRule的细分解释如下:
-
resource:资源名称,可以是网关中的路线名称或用户自定义的API分组名称。
-
resourceMode:规则是针对API网关的路由(RESOURCE_MODE_ROUTE_ID)还是用户在Sentinel中定义的API分组(RESOURCE_MODE_CUSTOM_API_NAME),而是路由。
-
grade:限流指标维度,同限流规则的grade细分。
-
count:限流阈值
-
intervalSec:统计时间窗口,单位是秒,至少是1秒。
-
controlBehavior:流量整形的控制效果,同限流规则的controlBehavior细分,目前支持快速失败和匀速排队两种模式,而是是快速失败。
-
burst:应对突发请求时额外允许的请求数量。
-
maxQueueingTimeoutMs:匀速排队模式下的最大排队时间,单位是几分钟,仅在匀速排队模式下生效。
-
paramItem:参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。
-
parseStrategy:从请求中提取参数的策略,当前支持提取源IP(PARAM_PARSE_STRATEGY_CLIENT_IP),主机(PARAM_PARSE_STRATEGY_HOST),任意头(PARAM_PARSE_STRATEGY_HEADER)和任意URL参数(PARAM_PARSE_STRATEGY_URL_PARAM)模式。
-
fieldName:如果提取策略选择Header模式或URL参数模式,则需要指定对应的header名称或URL参数名。
-
pattern:参数值的匹配模式,只有匹配该模式的请求属性值会重新统计和流控;若为空则统计该请求属性的所有值。(1.6.2版本开始支持)
-
matchStrategy:参数值的匹配策略,当前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT),子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2版本开始支持)
用户可以通过GatewayRuleManager.loadRules(rules)手动加载网关规则,或通过GatewayRuleManager.register2Property(property)注册动态规则源动态推送(推荐方式)
SpringCloudGateway
引入Maven依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例
sentinel提供了两种方式用来设置资源
1.通过配置文件进行路由设置
server: port: 8085 spring: main: allow-bean-definition-overriding: true application: name: live-gateway profiles: active: loc cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: enabled: true discovery: locator: lower-case-service-id: true routes: # 会被标识为 Sentinel 的资源 - id:cloud_route uri: lb://spring/cloud/test predicates: - Path=/spring/cloud/test/**
网关设置路由:
对应的服务:
2.在代码中进行设置
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem; import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler; import com.live.gateway.handler.GatewayHandler; import org.springframework.beans.factory.ObjectProvider; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.result.view.ViewResolver; import javax.annotation.PostConstruct; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author wjc * @description * @date 2020/10/9 */ @Configuration public class GatewayConfig { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfig(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); } @Bean @Order(-1) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } @PostConstruct public void doInit() { initCustomizedApis(); initGatewayRules(); //自定义处理被限流的请求 GatewayCallbackManager.setBlockHandler(new GatewayHandler()); } //详细配置参考官方文档 private void initCustomizedApis() { Set<ApiDefinition> definitions = new HashSet<>(); ApiDefinition api1 = new ApiDefinition("some_customized_api") .setPredicateItems(new HashSet<ApiPredicateItem>() {{ add(new ApiPathPredicateItem().setPattern("/spring/cloud/test/**") .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); }}); definitions.add(api1); GatewayApiDefinitionManager.loadApiDefinitions(definitions); } private void initGatewayRules() { Set<GatewayFlowRule> rules = new HashSet<>(); rules.add(new GatewayFlowRule("some_customized_api") .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) .setCount(1) .setIntervalSec(1) ); GatewayRuleManager.loadRules(rules); } }
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * @author wjc * @description * @date 2020/10/9 */ public class GatewayHandler implements BlockRequestHandler { private static final String DEFAULT_BLOCK_MSG_PREFIX = "Blocked by Sentinel: "; @Override public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { ServerHttpRequest request = serverWebExchange.getRequest(); System.out.println(request.getPath()); return ServerResponse.status(200) .contentType(MediaType.APPLICATION_JSON) // .body(BodyInserters.fromValue(buildErrorResult(throwable))); .body(BodyInserters.fromValue(buildErrorResult(request.getPath().value()))); } private ErrorResult buildErrorResult(Throwable ex) { // return new ErrorResult(200, DEFAULT_BLOCK_MSG_PREFIX + ex.getClass().getSimpleName()); return new ErrorResult(500, "限流"); } private ErrorResult buildErrorResult(String path) { if(path.equals("/spring/cloud/test/block/handler")){ return new ErrorResult(200, "hhahahhahah限流"); } return new ErrorResult(500, "限流"); } private static class ErrorResult { private final int code; private final String message; ErrorResult(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } }
通过网关请求接口
达到阈值限流