From 6138b094c21a12feda8079c8ae4f595ffe8f610b Mon Sep 17 00:00:00 2001 From: SkyeBeFreeman <928016560@qq.com> Date: Tue, 24 Aug 2021 11:59:15 +0800 Subject: [PATCH] feat:add RateLimit filter in SCG --- .../PolarisGatewayAutoConfiguration.java | 6 + .../scg/filter/Metadata2HeaderScgFilter.java | 4 +- .../scg/filter/MetadataFirstScgFilter.java | 4 +- .../core/scg/filter/RateLimitScgFilter.java | 103 ++++++++++++++++++ 4 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/RateLimitScgFilter.java diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/config/PolarisGatewayAutoConfiguration.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/config/PolarisGatewayAutoConfiguration.java index 1ecbc478..cf8c1743 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/config/PolarisGatewayAutoConfiguration.java +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/config/PolarisGatewayAutoConfiguration.java @@ -21,6 +21,7 @@ import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.http.ZuulServlet; import com.tencent.cloud.polaris.gateway.core.scg.filter.Metadata2HeaderScgFilter; import com.tencent.cloud.polaris.gateway.core.scg.filter.MetadataFirstScgFilter; +import com.tencent.cloud.polaris.gateway.core.scg.filter.RateLimitScgFilter; import com.tencent.cloud.polaris.gateway.core.zuul.filter.Metadata2HeaderZuulFilter; import com.tencent.cloud.polaris.gateway.core.zuul.filter.MetadataFirstZuulFilter; import com.tencent.cloud.polaris.gateway.core.zuul.filter.RateLimitZuulFilter; @@ -63,6 +64,11 @@ public class PolarisGatewayAutoConfiguration { return new MetadataFirstScgFilter(); } + @Bean + public GlobalFilter rateLimitScgFilter() { + return new RateLimitScgFilter(); + } + @Bean public GlobalFilter metadata2HeaderScgFilter() { return new Metadata2HeaderScgFilter(); diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java index 654c1f18..7ac146bc 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java @@ -31,6 +31,8 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; +import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER; + /** * Scg filter used for writing metadata in HTTP request header. * @@ -38,7 +40,7 @@ import java.util.Map; */ public class Metadata2HeaderScgFilter extends AbstractGlobalFilter { - private static final int METADATA_SCG_FILTER_ORDER = 10152; + private static final int METADATA_SCG_FILTER_ORDER = LOAD_BALANCER_CLIENT_FILTER_ORDER + 1; @Override public int getOrder() { diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataFirstScgFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataFirstScgFilter.java index 8e061abe..cbe8f479 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataFirstScgFilter.java +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataFirstScgFilter.java @@ -34,9 +34,11 @@ import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.G */ public class MetadataFirstScgFilter extends AbstractGlobalFilter { + public static final int METADATA_FIRST_FILTER_ORDER = ROUTE_TO_URL_FILTER_ORDER + 1; + @Override public int getOrder() { - return ROUTE_TO_URL_FILTER_ORDER + 1; + return METADATA_FIRST_FILTER_ORDER; } @Override diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/RateLimitScgFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/RateLimitScgFilter.java new file mode 100644 index 00000000..b30c0cba --- /dev/null +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/RateLimitScgFilter.java @@ -0,0 +1,103 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.polaris.gateway.core.scg.filter; + +import com.tencent.cloud.metadata.constant.MetadataConstant; +import com.tencent.cloud.metadata.context.MetadataContext; +import com.tencent.cloud.polaris.ratelimit.utils.Consts; +import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +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.HashMap; +import java.util.Map; + +import static com.tencent.cloud.polaris.gateway.core.scg.filter.MetadataFirstScgFilter.METADATA_FIRST_FILTER_ORDER; + +/** + * Scg filter used for writing metadata in HTTP request header. + * + * @author Haotian Zhang + */ +public class RateLimitScgFilter extends AbstractGlobalFilter { + + private static final Logger LOG = LoggerFactory.getLogger(RateLimitScgFilter.class); + + public static final int RATE_LIMIT_SCG_FILTER_ORDER = METADATA_FIRST_FILTER_ORDER + 1; + + @Autowired(required = false) + private LimitAPI limitAPI; + + @Override + public int getOrder() { + return RATE_LIMIT_SCG_FILTER_ORDER; + } + + @Override + public boolean shouldFilter(ServerWebExchange exchange, GatewayFilterChain chain) { + return limitAPI != null; + } + + @Override + public Mono doFilter(ServerWebExchange exchange, GatewayFilterChain chain) { + // get metadata of current thread + MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT); + + String peerNamespace = metadataContext.getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE); + String peerService = metadataContext.getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE); + String peerPath = metadataContext.getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH); + Map labels = null; + if (StringUtils.isNotBlank(peerPath)) { + labels = new HashMap<>(); + labels.put("method", peerPath); + } + + try { + QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, peerNamespace, peerService, 1, labels, + null); + if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { + ServerHttpResponse response = exchange.getResponse(); + response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS); + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + DataBuffer dataBuffer = response.bufferFactory().allocateBuffer() + .write((Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo()).getBytes(StandardCharsets.UTF_8)); + return response.writeWith(Mono.just(dataBuffer)); + } + } catch (Throwable throwable) { + //限流API调用出现异常,不应该影响业务流程的调用 + LOG.error("fail to rate limit with QuotaRequest[{}-{}-{}].", peerNamespace, peerService, peerPath, + throwable); + } + + return chain.filter(exchange); + } +}