diff --git a/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitConfiguration.java b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitConfiguration.java index 4ad99a82..ea32c470 100644 --- a/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitConfiguration.java +++ b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitConfiguration.java @@ -38,6 +38,7 @@ import org.springframework.context.annotation.Configuration; * @author Haotian Zhang */ @Configuration +@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true) public class RateLimitConfiguration { @Bean @@ -51,7 +52,6 @@ public class RateLimitConfiguration { */ @Configuration @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) - @ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true) static class QuotaCheckFilterConfig { @Bean diff --git a/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/callee/QuotaCheckFilter.java b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/callee/QuotaCheckFilter.java index e4b9d755..e1b1ee8c 100644 --- a/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/callee/QuotaCheckFilter.java +++ b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/callee/QuotaCheckFilter.java @@ -22,8 +22,8 @@ import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey; import com.tencent.cloud.metadata.context.MetadataContextHolder; 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.QuotaRequest; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; import java.io.IOException; @@ -59,21 +59,18 @@ public class QuotaCheckFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - QuotaRequest quotaRequest = new QuotaRequest(); String localNamespace = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE); String localService = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE); - quotaRequest.setNamespace(localNamespace); - quotaRequest.setService(localService); - quotaRequest.setCount(1); - String method = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_PATH); + Map labels = null; if (StringUtils.isNotBlank(method)) { - Map labels = new HashMap<>(); + labels = new HashMap<>(); labels.put("method", method); - quotaRequest.setLabels(labels); } + try { - QuotaResponse quotaResponse = limitAPI.getQuota(quotaRequest); + QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, labels, + null); if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { response.setStatus(TOO_MANY_REQUESTS.value()); response.getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo()); diff --git a/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java new file mode 100644 index 00000000..df257077 --- /dev/null +++ b/spring-cloud-tencent-starters/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java @@ -0,0 +1,55 @@ +/* + * 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.ratelimit.utils; + +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * Utils for quota checking + * + * @author Haotian Zhang + */ +public class QuotaCheckUtils { + + private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckUtils.class); + + public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace, String service, int count, + Map labels, String method) { + // build quota request + QuotaRequest quotaRequest = new QuotaRequest(); + quotaRequest.setNamespace(namespace); + quotaRequest.setService(service); + quotaRequest.setCount(count); + quotaRequest.setLabels(labels); + quotaRequest.setMethod(method); + + try { + return limitAPI.getQuota(quotaRequest); + } catch (Throwable throwable) { + LOG.error("fail to invoke getQuota of LimitAPI with QuotaRequest[{}].", quotaRequest, throwable); + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, "get quota failed")); + } + } + +} diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/pom.xml b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/pom.xml index c08a48ef..261bd215 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/pom.xml +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/pom.xml @@ -24,6 +24,11 @@ com.tencent.cloud spring-cloud-tencent-metadata + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-ratelimit + 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 63e54b8e..da5be365 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 @@ -19,8 +19,10 @@ package com.tencent.cloud.polaris.gateway.config; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.http.ZuulServlet; -import com.tencent.cloud.polaris.gateway.core.scg.filter.MetadataScgFilter; -import com.tencent.cloud.polaris.gateway.core.zuul.filter.MetadataZuulFilter; +import com.tencent.cloud.polaris.gateway.core.scg.filter.Metadata2HeaderScgFilter; +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; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; @@ -38,8 +40,17 @@ public class PolarisGatewayAutoConfiguration { @ConditionalOnClass(ZuulServlet.class) static class PolarisGatewayZuulAutoConfiguration { @Bean - public ZuulFilter metadataZuulFilter() { - return new MetadataZuulFilter(); + public ZuulFilter metadataFirstZuulFilter() { + return new MetadataFirstZuulFilter(); + } + + @Bean + public ZuulFilter rateLimitZuulFilter() { + return new RateLimitZuulFilter(); + } + @Bean + public ZuulFilter metadata2HeaderZuulFilter() { + return new Metadata2HeaderZuulFilter(); } } @@ -47,8 +58,8 @@ public class PolarisGatewayAutoConfiguration { @ConditionalOnClass(GlobalFilter.class) static class PolarisGatewayScgAutoConfiguration { @Bean - public GlobalFilter metadataScgFilter() { - return new MetadataScgFilter(); + 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/MetadataScgFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java similarity index 95% rename from spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataScgFilter.java rename to spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java index 08c48420..679fa517 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/MetadataScgFilter.java +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/scg/filter/Metadata2HeaderScgFilter.java @@ -36,9 +36,9 @@ import java.util.Map; * * @author Haotian Zhang */ -public class MetadataScgFilter extends AbstractGlobalFilter { +public class Metadata2HeaderScgFilter extends AbstractGlobalFilter { - private static final int METADATA_SCG_FILTER_ORDER = 10151; + private static final int METADATA_SCG_FILTER_ORDER = 10152; @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/zuul/filter/MetadataZuulFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/Metadata2HeaderZuulFilter.java similarity index 97% rename from spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/MetadataZuulFilter.java rename to spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/Metadata2HeaderZuulFilter.java index 727f7a58..53972c6c 100644 --- a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/MetadataZuulFilter.java +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/Metadata2HeaderZuulFilter.java @@ -38,7 +38,7 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst * * @author skyehtzhang */ -public class MetadataZuulFilter extends ZuulFilter { +public class Metadata2HeaderZuulFilter extends ZuulFilter { @Override public String filterType() { diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/MetadataFirstZuulFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/MetadataFirstZuulFilter.java new file mode 100644 index 00000000..2d158d0e --- /dev/null +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/MetadataFirstZuulFilter.java @@ -0,0 +1,64 @@ +/* + * 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.zuul.filter; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import com.tencent.cloud.metadata.constant.MetadataConstant; +import com.tencent.cloud.metadata.context.MetadataContextHolder; + +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER; +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_URI_KEY; +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY; + +/** + * Zuul filter used for setting peer info in context. + * + * @author Haotian Zhang + */ +public class MetadataFirstZuulFilter extends ZuulFilter { + @Override + public String filterType() { + return PRE_TYPE; + } + + @Override + public int filterOrder() { + return PRE_DECORATION_FILTER_ORDER + 1; + } + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() { + // get request context + RequestContext requestContext = RequestContext.getCurrentContext(); + + // TODO 对端命名空间暂时与本地命名空间相同 + MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE, + MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_NAMESPACE)); + MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE, + (String) requestContext.get(SERVICE_ID_KEY)); + MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH, + (String) requestContext.get(REQUEST_URI_KEY)); + return null; + } +} diff --git a/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/RateLimitZuulFilter.java b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/RateLimitZuulFilter.java new file mode 100644 index 00000000..89cf7896 --- /dev/null +++ b/spring-cloud-tencent-starters/spring-cloud-tencent-polaris-gateway/src/main/java/com/tencent/cloud/polaris/gateway/core/zuul/filter/RateLimitZuulFilter.java @@ -0,0 +1,99 @@ +/* + * 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.zuul.filter; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import com.tencent.cloud.metadata.constant.MetadataConstant; +import com.tencent.cloud.metadata.context.MetadataContextHolder; +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 java.util.HashMap; +import java.util.Map; + +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER; +import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; + +/** + * Zuul filter used for rate limit. + * + * @author Haotian Zhang + */ +public class RateLimitZuulFilter extends ZuulFilter { + + private static final Logger LOG = LoggerFactory.getLogger(RateLimitZuulFilter.class); + + @Autowired(required = false) + private LimitAPI limitAPI; + + @Override + public String filterType() { + return PRE_TYPE; + } + + @Override + public int filterOrder() { + return RIBBON_ROUTING_FILTER_ORDER - 1; + } + + @Override + public boolean shouldFilter() { + return limitAPI != null; + } + + @Override + public Object run() { + // get request context + RequestContext requestContext = RequestContext.getCurrentContext(); + + String peerNamespace = + MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE); + String peerService = + MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE); + String peerPath = MetadataContextHolder.get().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) { + requestContext.setSendZuulResponse(false); + requestContext.setResponseStatusCode(TOO_MANY_REQUESTS.value()); + requestContext.getResponse().getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo()); + } + } catch (Throwable throwable) { + //限流API调用出现异常,不应该影响业务流程的调用 + LOG.error("fail to rate limit with QuotaRequest[{}-{}-{}].", peerNamespace, peerService, peerPath, + throwable); + } + + return null; + } +}