diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fbb7484d..ec8957944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,3 +38,4 @@ - [feat: support lossless config from console & support warmup.](https://github.com/Tencent/spring-cloud-tencent/pull/1445) - [feat:add admin http handler.](https://github.com/Tencent/spring-cloud-tencent/pull/1450) - [feat:upgrade spring cloud 2023 version.](https://github.com/Tencent/spring-cloud-tencent/pull/1451) +- [feat:support concurrency rate limit.](https://github.com/Tencent/spring-cloud-tencent/pull/1455) diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java index a2431755c..e4fea8f8a 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java @@ -25,7 +25,6 @@ import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin; import com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin; import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin; -import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -66,8 +65,8 @@ public FilterRegistrationBean metadataServl } @Bean - public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisContextProperties polarisContextProperties) { - return new DecodeTransferMetadataServletFilter(polarisContextProperties); + public DecodeTransferMetadataServletFilter metadataServletFilter() { + return new DecodeTransferMetadataServletFilter(); } } @@ -79,8 +78,8 @@ public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisContextP protected static class MetadataReactiveFilterConfig { @Bean - public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisContextProperties polarisContextProperties) { - return new DecodeTransferMetadataReactiveFilter(polarisContextProperties); + public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() { + return new DecodeTransferMetadataReactiveFilter(); } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java index d279aa0c5..f462f67fe 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java @@ -27,7 +27,6 @@ import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.UrlUtils; import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider; -import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.api.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,11 +53,6 @@ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered { private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class); - private PolarisContextProperties polarisContextProperties; - - public DecodeTransferMetadataReactiveFilter(PolarisContextProperties polarisContextProperties) { - this.polarisContextProperties = polarisContextProperties; - } @Override public int getOrder() { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java index f7b97d0e2..d0942279c 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java @@ -27,7 +27,6 @@ import com.tencent.cloud.common.util.JacksonUtils; import com.tencent.cloud.common.util.UrlUtils; import com.tencent.cloud.metadata.provider.ServletMetadataProvider; -import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.api.utils.StringUtils; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -56,12 +55,6 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class); - private PolarisContextProperties polarisContextProperties; - - public DecodeTransferMetadataServletFilter(PolarisContextProperties polarisContextProperties) { - this.polarisContextProperties = polarisContextProperties; - } - @Override protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, FilterChain filterChain) diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java index ff27c0c4d..bbee696c9 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java @@ -20,7 +20,6 @@ import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; -import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -55,7 +54,7 @@ public class DecodeTransferMetadataReactiveFilterTest { @BeforeEach public void setUp() { - this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(new PolarisContextProperties()); + this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(); } @Test diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java deleted file mode 100644 index f51b6f0b8..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.polaris.specification.api.v1.model.ModelProto; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; - -import org.springframework.util.CollectionUtils; - -/** - * resolve labels from rate limit rule. - * - *@author lepdou 2022-05-13 - */ -@Deprecated -public class RateLimitRuleLabelResolver { - - private final ServiceRuleManager serviceRuleManager; - - public RateLimitRuleLabelResolver(ServiceRuleManager serviceRuleManager) { - this.serviceRuleManager = serviceRuleManager; - } - - public Set getExpressionLabelKeys(String namespace, String service) { - RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(namespace, service); - if (rateLimitRule == null) { - return Collections.emptySet(); - } - - List rules = rateLimitRule.getRulesList(); - if (CollectionUtils.isEmpty(rules)) { - return Collections.emptySet(); - } - - Set expressionLabels = new HashSet<>(); - for (RateLimitProto.Rule rule : rules) { - Map labels = rule.getLabelsMap(); - if (CollectionUtils.isEmpty(labels)) { - return Collections.emptySet(); - } - for (String key : labels.keySet()) { - if (ExpressionLabelUtils.isExpressionLabel(key)) { - expressionLabels.add(key); - } - } - } - return expressionLabels; - } -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java index a825c1cfe..9651950fd 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java @@ -20,14 +20,9 @@ import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.polaris.context.PolarisSDKContextManager; -import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentReactiveResolver; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentServletResolver; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import org.springframework.beans.factory.annotation.Autowired; @@ -63,20 +58,13 @@ public class PolarisRateLimitAutoConfiguration { @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) protected static class QuotaCheckFilterConfig { - @Bean - public RateLimitRuleArgumentServletResolver rateLimitRuleArgumentResolver(ServiceRuleManager serviceRuleManager, - @Autowired(required = false) PolarisRateLimiterLabelServletResolver labelResolver) { - return new RateLimitRuleArgumentServletResolver(serviceRuleManager, labelResolver); - } - @Bean @ConditionalOnMissingBean public QuotaCheckServletFilter quotaCheckFilter(PolarisSDKContextManager polarisSDKContextManager, PolarisRateLimitProperties polarisRateLimitProperties, - RateLimitRuleArgumentServletResolver rateLimitRuleArgumentResolver, @Autowired(required = false) PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback) { - return new QuotaCheckServletFilter(polarisSDKContextManager.getLimitAPI(), polarisRateLimitProperties, - rateLimitRuleArgumentResolver, polarisRateLimiterLimitedFallback); + return new QuotaCheckServletFilter(polarisSDKContextManager.getLimitAPI(), polarisSDKContextManager.getAssemblyAPI(), + polarisRateLimitProperties, polarisRateLimiterLimitedFallback); } @Bean @@ -97,21 +85,14 @@ public FilterRegistrationBean quotaFilterRegistrationBe */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) - protected static class MetadataReactiveFilterConfig { - - @Bean - public RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentResolver(ServiceRuleManager serviceRuleManager, - @Autowired(required = false) PolarisRateLimiterLabelReactiveResolver labelResolver) { - return new RateLimitRuleArgumentReactiveResolver(serviceRuleManager, labelResolver); - } + protected static class QuotaCheckReactiveFilterConfig { @Bean public QuotaCheckReactiveFilter quotaCheckReactiveFilter(PolarisSDKContextManager polarisSDKContextManager, PolarisRateLimitProperties polarisRateLimitProperties, - RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentResolver, @Nullable PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback) { - return new QuotaCheckReactiveFilter(polarisSDKContextManager.getLimitAPI(), polarisRateLimitProperties, - rateLimitRuleArgumentResolver, polarisRateLimiterLimitedFallback); + return new QuotaCheckReactiveFilter(polarisSDKContextManager.getLimitAPI(), polarisSDKContextManager.getAssemblyAPI(), + polarisRateLimitProperties, polarisRateLimiterLimitedFallback); } } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java index 014f91f72..c8d921521 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java @@ -23,19 +23,18 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Objects; -import java.util.Set; import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentReactiveResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils; import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils; import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.assembly.api.AssemblyAPI; import com.tencent.polaris.ratelimit.api.core.LimitAPI; -import com.tencent.polaris.ratelimit.api.rpc.Argument; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; import jakarta.annotation.PostConstruct; @@ -53,6 +52,7 @@ import org.springframework.web.server.WebFilterChain; import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; +import static org.springframework.core.io.buffer.DefaultDataBufferFactory.DEFAULT_INITIAL_CAPACITY; /** * Reactive filter to check quota. @@ -65,22 +65,20 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { private final LimitAPI limitAPI; - private final PolarisRateLimitProperties polarisRateLimitProperties; + private final AssemblyAPI assemblyAPI; - private final RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentResolver; + private final PolarisRateLimitProperties polarisRateLimitProperties; private final PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback; - private String rejectTips; - public QuotaCheckReactiveFilter(LimitAPI limitAPI, + public QuotaCheckReactiveFilter(LimitAPI limitAPI, AssemblyAPI assemblyAPI, PolarisRateLimitProperties polarisRateLimitProperties, - RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentResolver, @Nullable PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback) { this.limitAPI = limitAPI; + this.assemblyAPI = assemblyAPI; this.polarisRateLimitProperties = polarisRateLimitProperties; - this.rateLimitRuleArgumentResolver = rateLimitRuleArgumentResolver; this.polarisRateLimiterLimitedFallback = polarisRateLimiterLimitedFallback; } @@ -99,31 +97,41 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localService = MetadataContext.LOCAL_SERVICE; - Set arguments = rateLimitRuleArgumentResolver.getArguments(exchange, localNamespace, localService); long waitMs = -1; + QuotaResponse quotaResponse = null; try { String path = exchange.getRequest().getURI().getPath(); - QuotaResponse quotaResponse = QuotaCheckUtils.getQuota( - limitAPI, localNamespace, localService, 1, arguments, path); + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, path); if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { ServerHttpResponse response = exchange.getResponse(); DataBuffer dataBuffer; - if (!Objects.isNull(polarisRateLimiterLimitedFallback)) { + if (Objects.nonNull(quotaResponse.getActiveRule()) + && StringUtils.isNotBlank(quotaResponse.getActiveRule().getCustomResponse().getBody())) { + response.setRawStatusCode(polarisRateLimitProperties.getRejectHttpCode()); + response.getHeaders().setContentType(MediaType.TEXT_PLAIN); + dataBuffer = response.bufferFactory().allocateBuffer(DEFAULT_INITIAL_CAPACITY) + .write(quotaResponse.getActiveRule().getCustomResponse().getBody() + .getBytes(StandardCharsets.UTF_8)); + } + else if (!Objects.isNull(polarisRateLimiterLimitedFallback)) { response.setRawStatusCode(polarisRateLimiterLimitedFallback.rejectHttpCode()); response.getHeaders().setContentType(polarisRateLimiterLimitedFallback.mediaType()); - dataBuffer = response.bufferFactory().allocateBuffer() + dataBuffer = response.bufferFactory().allocateBuffer(DEFAULT_INITIAL_CAPACITY) .write(polarisRateLimiterLimitedFallback.rejectTips() .getBytes(polarisRateLimiterLimitedFallback.charset())); } else { response.setRawStatusCode(polarisRateLimitProperties.getRejectHttpCode()); response.getHeaders().setContentType(MediaType.TEXT_HTML); - dataBuffer = response.bufferFactory().allocateBuffer() + dataBuffer = response.bufferFactory().allocateBuffer(DEFAULT_INITIAL_CAPACITY) .write(rejectTips.getBytes(StandardCharsets.UTF_8)); } + // set flow control to header response.getHeaders() .add(HeaderConstant.INTERNAL_CALLEE_RET_STATUS, RetStatus.RetFlowControl.getDesc()); + // set trace span + RateLimitUtils.reportTrace(assemblyAPI, quotaResponse.getActiveRule().getId().getValue()); if (Objects.nonNull(quotaResponse.getActiveRule())) { try { String encodedActiveRuleName = URLEncoder.encode( @@ -135,6 +143,7 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { quotaResponse.getActiveRuleName(), e); } } + RateLimitUtils.release(quotaResponse); return response.writeWith(Mono.just(dataBuffer)); } // Unirate @@ -149,11 +158,13 @@ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { LOG.error("fail to invoke getQuota, service is " + localService, t); } + QuotaResponse finalQuotaResponse = quotaResponse; if (waitMs > 0) { - return Mono.delay(Duration.ofMillis(waitMs)).flatMap(e -> chain.filter(exchange)); + return Mono.delay(Duration.ofMillis(waitMs)) + .flatMap(e -> chain.filter(exchange).doFinally((v) -> RateLimitUtils.release(finalQuotaResponse))); } else { - return chain.filter(exchange); + return chain.filter(exchange).doFinally((v) -> RateLimitUtils.release(finalQuotaResponse)); } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java index ee6441f12..3c5940223 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java @@ -22,19 +22,18 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Objects; -import java.util.Set; import com.tencent.cloud.common.constant.HeaderConstant; import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentServletResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils; import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils; import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.assembly.api.AssemblyAPI; import com.tencent.polaris.ratelimit.api.core.LimitAPI; -import com.tencent.polaris.ratelimit.api.rpc.Argument; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; import jakarta.annotation.PostConstruct; @@ -68,21 +67,20 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter { private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckServletFilter.class); private final LimitAPI limitAPI; - private final PolarisRateLimitProperties polarisRateLimitProperties; + private final AssemblyAPI assemblyAPI; - private final RateLimitRuleArgumentServletResolver rateLimitRuleArgumentResolver; + private final PolarisRateLimitProperties polarisRateLimitProperties; private final PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback; private String rejectTips; - public QuotaCheckServletFilter(LimitAPI limitAPI, + public QuotaCheckServletFilter(LimitAPI limitAPI, AssemblyAPI assemblyAPI, PolarisRateLimitProperties polarisRateLimitProperties, - RateLimitRuleArgumentServletResolver rateLimitRuleArgumentResolver, @Nullable PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback) { this.limitAPI = limitAPI; + this.assemblyAPI = assemblyAPI; this.polarisRateLimitProperties = polarisRateLimitProperties; - this.rateLimitRuleArgumentResolver = rateLimitRuleArgumentResolver; this.polarisRateLimiterLimitedFallback = polarisRateLimiterLimitedFallback; } @@ -96,15 +94,17 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht @NonNull FilterChain filterChain) throws ServletException, IOException { String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localService = MetadataContext.LOCAL_SERVICE; - - Set arguments = rateLimitRuleArgumentResolver.getArguments(request, localNamespace, localService); - + QuotaResponse quotaResponse = null; try { - QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, - localNamespace, localService, 1, arguments, request.getRequestURI()); - + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, request.getRequestURI()); if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { - if (!Objects.isNull(polarisRateLimiterLimitedFallback)) { + if (Objects.nonNull(quotaResponse.getActiveRule()) + && StringUtils.isNotBlank(quotaResponse.getActiveRule().getCustomResponse().getBody())) { + response.setStatus(polarisRateLimitProperties.getRejectHttpCode()); + response.setContentType("text/plain;charset=UTF-8"); + response.getWriter().write(quotaResponse.getActiveRule().getCustomResponse().getBody()); + } + else if (!Objects.isNull(polarisRateLimiterLimitedFallback)) { response.setStatus(polarisRateLimiterLimitedFallback.rejectHttpCode()); String contentType = new MediaType(polarisRateLimiterLimitedFallback.mediaType(), polarisRateLimiterLimitedFallback.charset()).toString(); response.setContentType(contentType); @@ -115,7 +115,10 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht response.setContentType("text/html;charset=UTF-8"); response.getWriter().write(rejectTips); } + // set flow control to header response.addHeader(HeaderConstant.INTERNAL_CALLEE_RET_STATUS, RetStatus.RetFlowControl.getDesc()); + // set trace span + RateLimitUtils.reportTrace(assemblyAPI, quotaResponse.getActiveRule().getId().getValue()); if (Objects.nonNull(quotaResponse.getActiveRule())) { try { String encodedActiveRuleName = URLEncoder.encode( @@ -127,6 +130,7 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht quotaResponse.getActiveRuleName(), e); } } + RateLimitUtils.release(quotaResponse); return; } // Unirate @@ -142,7 +146,12 @@ protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull Ht LOG.error("fail to invoke getQuota, service is " + localService, t); } - filterChain.doFilter(request, response); + try { + filterChain.doFilter(request, response); + } + finally { + RateLimitUtils.release(quotaResponse); + } } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolver.java deleted file mode 100644 index 78cb2ecc9..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolver.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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.resolver; - -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; -import com.tencent.polaris.ratelimit.api.rpc.Argument; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.util.CollectionUtils; -import org.springframework.web.server.ServerWebExchange; - -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAME; -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE; - -/** - * resolve arguments from rate limit rule for Reactive. - * - * @author seansyyu 2023-03-09 - */ -public class RateLimitRuleArgumentReactiveResolver { - - private static final Logger LOG = LoggerFactory.getLogger(RateLimitRuleArgumentReactiveResolver.class); - - private final ServiceRuleManager serviceRuleManager; - - private final PolarisRateLimiterLabelReactiveResolver labelResolver; - - public RateLimitRuleArgumentReactiveResolver(ServiceRuleManager serviceRuleManager, PolarisRateLimiterLabelReactiveResolver labelResolver) { - this.serviceRuleManager = serviceRuleManager; - this.labelResolver = labelResolver; - } - - public Set getArguments(ServerWebExchange request, String namespace, String service) { - RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(namespace, service); - if (rateLimitRule == null) { - return Collections.emptySet(); - } - List rules = rateLimitRule.getRulesList(); - if (CollectionUtils.isEmpty(rules)) { - return Collections.emptySet(); - } - return rules.stream() - .flatMap(rule -> rule.getArgumentsList().stream()) - .map(matchArgument -> { - String matchKey = matchArgument.getKey(); - Argument argument = null; - switch (matchArgument.getType()) { - case CUSTOM: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildCustom(matchKey, Optional.ofNullable(getCustomResolvedLabels(request).get(matchKey)) - .orElse(StringUtils.EMPTY)); - break; - case METHOD: - argument = Argument.buildMethod(request.getRequest().getMethod().name()); - break; - case HEADER: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildHeader(matchKey, Optional.ofNullable(request.getRequest().getHeaders() - .getFirst(matchKey)).orElse(StringUtils.EMPTY)); - break; - case QUERY: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildQuery(matchKey, Optional.ofNullable(request.getRequest().getQueryParams() - .getFirst(matchKey)).orElse(StringUtils.EMPTY)); - break; - case CALLER_SERVICE: - String sourceServiceNamespace = MetadataContextHolder.getDisposableMetadata(DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE, true) - .orElse(StringUtils.EMPTY); - String sourceServiceName = MetadataContextHolder.getDisposableMetadata(DEFAULT_METADATA_SOURCE_SERVICE_NAME, true) - .orElse(StringUtils.EMPTY); - if (!StringUtils.isEmpty(sourceServiceNamespace) && !StringUtils.isEmpty(sourceServiceName)) { - argument = Argument.buildCallerService(sourceServiceNamespace, sourceServiceName); - } - break; - case CALLER_IP: - InetSocketAddress remoteAddress = request.getRequest().getRemoteAddress(); - argument = Argument.buildCallerIP(remoteAddress != null ? remoteAddress.getAddress() - .getHostAddress() : StringUtils.EMPTY); - break; - default: - break; - } - return argument; - }).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - private Map getCustomResolvedLabels(ServerWebExchange request) { - if (labelResolver != null) { - try { - return labelResolver.resolve(request); - } - catch (Throwable e) { - LOG.error("resolve custom label failed. resolver = {}", labelResolver.getClass().getName(), e); - } - } - return Collections.emptyMap(); - } -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolver.java deleted file mode 100644 index ef6bc692f..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolver.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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.resolver; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; -import com.tencent.polaris.ratelimit.api.rpc.Argument; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; -import jakarta.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.util.CollectionUtils; - -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAME; -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE; - -/** - * resolve arguments from rate limit rule for Servlet. - * - * @author seansyyu 2023-03-09 - */ -public class RateLimitRuleArgumentServletResolver { - - private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckServletFilter.class); - - private final ServiceRuleManager serviceRuleManager; - - private final PolarisRateLimiterLabelServletResolver labelResolver; - - public RateLimitRuleArgumentServletResolver(ServiceRuleManager serviceRuleManager, PolarisRateLimiterLabelServletResolver labelResolver) { - this.serviceRuleManager = serviceRuleManager; - this.labelResolver = labelResolver; - } - - public Set getArguments(HttpServletRequest request, String namespace, String service) { - RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(namespace, service); - if (rateLimitRule == null) { - return Collections.emptySet(); - } - List rules = rateLimitRule.getRulesList(); - if (CollectionUtils.isEmpty(rules)) { - return Collections.emptySet(); - } - return rules.stream() - .flatMap(rule -> rule.getArgumentsList().stream()) - .map(matchArgument -> { - String matchKey = matchArgument.getKey(); - Argument argument = null; - switch (matchArgument.getType()) { - case CUSTOM: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildCustom(matchKey, Optional.ofNullable(getCustomResolvedLabels(request).get(matchKey)).orElse(StringUtils.EMPTY)); - break; - case METHOD: - argument = Argument.buildMethod(request.getMethod()); - break; - case HEADER: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildHeader(matchKey, Optional.ofNullable(request.getHeader(matchKey)).orElse(StringUtils.EMPTY)); - break; - case QUERY: - argument = StringUtils.isBlank(matchKey) ? null : - Argument.buildQuery(matchKey, Optional.ofNullable(request.getParameter(matchKey)).orElse(StringUtils.EMPTY)); - break; - case CALLER_SERVICE: - String sourceServiceNamespace = MetadataContextHolder.getDisposableMetadata(DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE, true).orElse(StringUtils.EMPTY); - String sourceServiceName = MetadataContextHolder.getDisposableMetadata(DEFAULT_METADATA_SOURCE_SERVICE_NAME, true).orElse(StringUtils.EMPTY); - if (!StringUtils.isEmpty(sourceServiceNamespace) && !StringUtils.isEmpty(sourceServiceName)) { - argument = Argument.buildCallerService(sourceServiceNamespace, sourceServiceName); - } - break; - case CALLER_IP: - argument = Argument.buildCallerIP(Optional.ofNullable(request.getRemoteAddr()).orElse(StringUtils.EMPTY)); - break; - default: - break; - } - return argument; - }).filter(Objects::nonNull).collect(Collectors.toSet()); - } - - private Map getCustomResolvedLabels(HttpServletRequest request) { - if (labelResolver != null) { - try { - return labelResolver.resolve(request); - } - catch (Throwable e) { - LOG.error("resolve custom label failed. resolver = {}", labelResolver.getClass().getName(), e); - } - } - return Collections.emptyMap(); - } - -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java deleted file mode 100644 index 6f63cbc93..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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.spi; - -import java.util.Map; - -import org.springframework.web.server.ServerWebExchange; - -/** - * Resolve custom label from request. The label used for rate limit params. - * - * @author lepdou 2022-03-31 - */ -public interface PolarisRateLimiterLabelReactiveResolver { - - /** - * Resolve custom label from request. - * @param exchange the http request - * @return resolved labels - */ - Map resolve(ServerWebExchange exchange); -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitAutoConfiguration.java new file mode 100644 index 000000000..c4a4aaebe --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * 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.tsf; + +import com.tencent.cloud.common.tsf.ConditionalOnTsfConsulEnabled; +import com.tencent.cloud.polaris.context.config.extend.consul.ConsulProperties; +import com.tencent.cloud.polaris.context.config.extend.tsf.TsfCoreProperties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * Auto configuration of TSF rate limit. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfConsulEnabled +public class TsfRateLimitAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public TsfRateLimitConfigModifier tsfRateLimitConfigModifier(TsfCoreProperties tsfCoreProperties, + ConsulProperties consulProperties, Environment environment) { + return new TsfRateLimitConfigModifier(tsfCoreProperties, consulProperties, environment); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitBootstrapConfiguration.java similarity index 61% rename from spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java rename to spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitBootstrapConfiguration.java index 3031a065f..f71c1d0e4 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitBootstrapConfiguration.java @@ -13,26 +13,22 @@ * 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.spi; +package com.tencent.cloud.polaris.ratelimit.tsf; -import java.util.Map; +import com.tencent.cloud.common.tsf.ConditionalOnTsfConsulEnabled; -import jakarta.servlet.http.HttpServletRequest; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** - * Resolve custom label from request. The label used for rate limit params. + * Bootstrap configuration for TSF rate limit. * - * @author lepdou 2022-03-31 + * @author Haotian Zhang */ -public interface PolarisRateLimiterLabelServletResolver { - - /** - * Resolve custom label from request. - * @param request the http request - * @return resolved labels - */ - Map resolve(HttpServletRequest request); +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfConsulEnabled +@Import(TsfRateLimitAutoConfiguration.class) +public class TsfRateLimitBootstrapConfiguration { } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitConfigModifier.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitConfigModifier.java new file mode 100644 index 000000000..682559c23 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/tsf/TsfRateLimitConfigModifier.java @@ -0,0 +1,68 @@ +/* + * 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.tsf; + +import java.util.Map; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.cloud.polaris.context.config.extend.consul.ConsulProperties; +import com.tencent.cloud.polaris.context.config.extend.tsf.TsfContextUtils; +import com.tencent.cloud.polaris.context.config.extend.tsf.TsfCoreProperties; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.ratelimit.client.sync.tsf.TsfRateLimitConstants; + +import org.springframework.core.env.Environment; + +/** + * Config modifier for TSF rate limit. + * + * @author Haotian Zhang + */ +public class TsfRateLimitConfigModifier implements PolarisConfigModifier { + + private final TsfCoreProperties tsfCoreProperties; + + private final ConsulProperties consulProperties; + + private final Environment environment; + + public TsfRateLimitConfigModifier(TsfCoreProperties tsfCoreProperties, ConsulProperties consulProperties, + Environment environment) { + this.tsfCoreProperties = tsfCoreProperties; + this.consulProperties = consulProperties; + this.environment = environment; + } + + @Override + public void modify(ConfigurationImpl configuration) { + if (TsfContextUtils.isTsfConsulEnabled(environment)) { + Map metadata = configuration.getProvider().getRateLimit().getMetadata(); + metadata.put(TsfRateLimitConstants.RATE_LIMIT_MASTER_IP_KEY, tsfCoreProperties.getRatelimitMasterIp()); + metadata.put(TsfRateLimitConstants.RATE_LIMIT_MASTER_PORT_KEY, String.valueOf(tsfCoreProperties.getRatelimitMasterPort())); + metadata.put(TsfRateLimitConstants.SERVICE_NAME_KEY, tsfCoreProperties.getServiceName()); + metadata.put(TsfRateLimitConstants.INSTANCE_ID_KEY, tsfCoreProperties.getInstanceId()); + metadata.put(TsfRateLimitConstants.TOKEN_KEY, consulProperties.getAclToken()); + } + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.RATE_LIMIT_ORDER; + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java index 3adcee5e4..a3ea94446 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtils.java @@ -17,12 +17,9 @@ package com.tencent.cloud.polaris.ratelimit.utils; -import java.util.Map; -import java.util.Set; - +import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; import com.tencent.polaris.ratelimit.api.core.LimitAPI; -import com.tencent.polaris.ratelimit.api.rpc.Argument; import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import org.slf4j.Logger; @@ -40,34 +37,13 @@ public final class QuotaCheckUtils { private QuotaCheckUtils() { } - @Deprecated - 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")); - } - } - - public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace, String service, int count, - Set arguments, String method) { + public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace, String service, int count, String method) { // build quota request QuotaRequest quotaRequest = new QuotaRequest(); quotaRequest.setNamespace(namespace); quotaRequest.setService(service); quotaRequest.setCount(count); - quotaRequest.setArguments(arguments); + quotaRequest.setMetadataContext(MetadataContextHolder.get()); quotaRequest.setMethod(method); try { diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java index 07ee0633a..1b90b1beb 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java @@ -18,9 +18,17 @@ package com.tencent.cloud.polaris.ratelimit.utils; +import java.util.HashMap; +import java.util.Map; + import com.tencent.cloud.common.util.ResourceFileUtils; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; +import com.tencent.polaris.api.plugin.stat.TraceConstants; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.assembly.api.AssemblyAPI; +import com.tencent.polaris.assembly.api.pojo.TraceAttributes; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,4 +71,33 @@ public static String getRejectTips(PolarisRateLimitProperties polarisRateLimitPr return RateLimitConstant.QUOTA_LIMITED_INFO; } + + public static void reportTrace(AssemblyAPI assemblyAPI, String ruleId) { + try { + if (assemblyAPI != null) { + Map attributes = new HashMap<>(); + attributes.put(TraceConstants.RateLimitRuleId, ruleId); + TraceAttributes traceAttributes = new TraceAttributes(); + traceAttributes.setAttributes(attributes); + traceAttributes.setAttributeLocation(TraceAttributes.AttributeLocation.SPAN); + assemblyAPI.updateTraceAttributes(traceAttributes); + } + } + catch (Throwable throwable) { + LOG.warn("[RateLimit] Report rule id {} to trace error.", ruleId, throwable); + } + } + + public static void release(QuotaResponse quotaResponse) { + if (quotaResponse != null && CollectionUtils.isNotEmpty(quotaResponse.getReleaseList())) { + for (Runnable release : quotaResponse.getReleaseList()) { + try { + release.run(); + } + catch (Throwable throwable) { + LOG.warn("[RateLimit] Release error.", throwable); + } + } + } + } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories index 29d7d06c4..4441291d2 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitPropertiesBootstrapConfiguration + com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitPropertiesBootstrapConfiguration,\ + com.tencent.cloud.polaris.ratelimit.tsf.TsfRateLimitBootstrapConfiguration diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index e0ec0b806..29406b95c 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitAutoConfiguration com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitPropertiesAutoConfiguration com.tencent.cloud.polaris.ratelimit.endpoint.PolarisRateLimitRuleEndpointAutoConfiguration +com.tencent.cloud.polaris.ratelimit.tsf.TsfRateLimitAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/context/CalleeControllerTests.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/context/CalleeControllerTests.java index 3a3cc76ea..594b98230 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/context/CalleeControllerTests.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/context/CalleeControllerTests.java @@ -17,15 +17,16 @@ package com.tencent.cloud.polaris.context; +import com.google.protobuf.StringValue; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentServletResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import com.tencent.polaris.api.pojo.ServiceKey; 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 com.tencent.polaris.ratelimit.factory.LimitAPIFactory; +import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; import com.tencent.polaris.test.mock.discovery.NamingServer; import com.tencent.polaris.test.mock.discovery.NamingService; import org.junit.jupiter.api.AfterAll; @@ -118,6 +119,13 @@ public void test1() { QuotaResponse quotaResponse = mock(QuotaResponse.class); when(quotaResponse.getCode()).thenReturn(QuotaResultCode.QuotaResultLimited); when(quotaResponse.getInfo()).thenReturn("Testing rate limit after 10 times success."); + RateLimitProto.Rule rule = mock(RateLimitProto.Rule.class); + when(rule.getId()).thenReturn(StringValue.of("rate-test")); + RateLimitProto.CustomResponse customResponse = mock(RateLimitProto.CustomResponse.class); + when(customResponse.getBody()).thenReturn("limited"); + when(rule.getCustomResponse()).thenReturn(customResponse); + when(quotaResponse.getActiveRule()).thenReturn(rule); + when(quotaResponse.getActiveRuleName()).thenReturn("rate-test"); when(limitAPI.getQuota(any())).thenReturn(quotaResponse); } String result = restTemplate.getForObject(url, String.class); @@ -126,7 +134,7 @@ public void test1() { } catch (RestClientException e) { if (e instanceof TooManyRequests) { - System.out.println(((TooManyRequests) e).getResponseBodyAsString()); + assertThat(((TooManyRequests) e).getResponseBodyAsString()).isEqualTo("limited"); hasLimited = true; } else { @@ -157,10 +165,8 @@ public LimitAPI limitAPI(PolarisSDKContextManager polarisSDKContextManager) { @Primary public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI, PolarisRateLimitProperties polarisRateLimitProperties, - RateLimitRuleArgumentServletResolver rateLimitRuleArgumentResolver, @Autowired(required = false) PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback) { - return new QuotaCheckServletFilter(limitAPI, polarisRateLimitProperties, - rateLimitRuleArgumentResolver, polarisRateLimiterLimitedFallback); + return new QuotaCheckServletFilter(limitAPI, null, polarisRateLimitProperties, polarisRateLimiterLimitedFallback); } } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java deleted file mode 100644 index 4d39e0c39..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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; - -import java.util.Set; - -import com.google.protobuf.StringValue; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.polaris.specification.api.v1.model.ModelProto; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Test for {@link RateLimitRuleLabelResolver}. - * - * @author Haotian Zhang - */ -@ExtendWith(MockitoExtension.class) -public class RateLimitRuleLabelResolverTest { - - private RateLimitRuleLabelResolver rateLimitRuleLabelResolver; - - @BeforeEach - void setUp() { - ServiceRuleManager serviceRuleManager = mock(ServiceRuleManager.class); - when(serviceRuleManager.getServiceRateLimitRule(any(), anyString())).thenAnswer(invocationOnMock -> { - String serviceName = invocationOnMock.getArgument(1).toString(); - if (serviceName.equals("TestApp1")) { - return null; - } - else if (serviceName.equals("TestApp2")) { - return RateLimitProto.RateLimit.newBuilder().build(); - } - else if (serviceName.equals("TestApp3")) { - RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder().build(); - return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); - } - else { - ModelProto.MatchString matchString = ModelProto.MatchString.newBuilder() - .setType(ModelProto.MatchString.MatchStringType.EXACT) - .setValue(StringValue.of("value")) - .setValueType(ModelProto.MatchString.ValueType.TEXT).build(); - RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder() - .putLabels("${http.method}", matchString).build(); - return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); - } - }); - - rateLimitRuleLabelResolver = new RateLimitRuleLabelResolver(serviceRuleManager); - } - - @Test - public void testGetExpressionLabelKeys() { - // rateLimitRule == null - String serviceName = "TestApp1"; - Set labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); - assertThat(labelKeys).isEmpty(); - - // CollectionUtils.isEmpty(rules) - serviceName = "TestApp2"; - labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); - assertThat(labelKeys).isEmpty(); - - // CollectionUtils.isEmpty(labels) - serviceName = "TestApp3"; - labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); - assertThat(labelKeys).isEmpty(); - - // Has labels - serviceName = "TestApp4"; - labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); - assertThat(labelKeys).isNotEmpty(); - assertThat(labelKeys).contains("${http.method}"); - } -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java index 019b69af8..668df07b3 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java @@ -20,8 +20,6 @@ import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentReactiveResolver; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentServletResolver; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -53,12 +51,10 @@ public void testNoWebApplication() { PolarisRateLimitProperties.class, PolarisRateLimitAutoConfiguration.class)) .run(context -> { - assertThat(context).doesNotHaveBean(RateLimitRuleArgumentServletResolver.class); - assertThat(context).doesNotHaveBean(RateLimitRuleArgumentReactiveResolver.class); assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); assertThat(context).doesNotHaveBean(QuotaCheckServletFilter.class); assertThat(context).doesNotHaveBean(FilterRegistrationBean.class); - assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckReactiveFilterConfig.class); assertThat(context).doesNotHaveBean(QuotaCheckReactiveFilter.class); }); } @@ -71,13 +67,11 @@ public void testServletWebApplication() { PolarisRateLimitProperties.class, PolarisRateLimitAutoConfiguration.class)) .run(context -> { - assertThat(context).hasSingleBean(RateLimitRuleArgumentServletResolver.class); assertThat(context).hasSingleBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); assertThat(context).hasSingleBean(QuotaCheckServletFilter.class); assertThat(context).hasSingleBean(FilterRegistrationBean.class); - assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckReactiveFilterConfig.class); assertThat(context).doesNotHaveBean(QuotaCheckReactiveFilter.class); - assertThat(context).doesNotHaveBean(RateLimitRuleArgumentReactiveResolver.class); }); } @@ -89,12 +83,10 @@ public void testReactiveWebApplication() { PolarisRateLimitProperties.class, PolarisRateLimitAutoConfiguration.class)) .run(context -> { - assertThat(context).doesNotHaveBean(RateLimitRuleArgumentServletResolver.class); - assertThat(context).hasSingleBean(RateLimitRuleArgumentReactiveResolver.class); assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); assertThat(context).doesNotHaveBean(QuotaCheckServletFilter.class); assertThat(context).doesNotHaveBean(FilterRegistrationBean.class); - assertThat(context).hasSingleBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).hasSingleBean(PolarisRateLimitAutoConfiguration.QuotaCheckReactiveFilterConfig.class); assertThat(context).hasSingleBean(QuotaCheckReactiveFilter.class); }); } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java index 7696b1eb5..f99e5d0af 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java @@ -34,8 +34,6 @@ import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentReactiveResolver; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; import com.tencent.polaris.ratelimit.api.core.LimitAPI; @@ -76,8 +74,6 @@ @SpringBootTest(classes = QuotaCheckReactiveFilterTest.TestApplication.class, properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"}) public class QuotaCheckReactiveFilterTest { - private final PolarisRateLimiterLabelReactiveResolver labelResolver = - exchange -> Collections.singletonMap("xxx", "xxx"); private QuotaCheckReactiveFilter quotaCheckReactiveFilter; private QuotaCheckReactiveFilter quotaCheckWithRateLimiterLimitedFallbackReactiveFilter; private PolarisRateLimiterLimitedFallback polarisRateLimiterLimitedFallback; @@ -126,10 +122,10 @@ else if (serviceName.equals("TestApp3")) { RateLimitProto.RateLimit rateLimit = RateLimitProto.RateLimit.newBuilder().addRules(rateLimitRule).build(); when(serviceRuleManager.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit); - RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentReactiveResolver = new RateLimitRuleArgumentReactiveResolver(serviceRuleManager, labelResolver); - this.quotaCheckReactiveFilter = new QuotaCheckReactiveFilter(limitAPI, polarisRateLimitProperties, rateLimitRuleArgumentReactiveResolver, null); + this.quotaCheckReactiveFilter = new QuotaCheckReactiveFilter(limitAPI, null, polarisRateLimitProperties, null); this.polarisRateLimiterLimitedFallback = new JsonPolarisRateLimiterLimitedFallback(); - this.quotaCheckWithRateLimiterLimitedFallbackReactiveFilter = new QuotaCheckReactiveFilter(limitAPI, polarisRateLimitWithHtmlRejectTipsProperties, rateLimitRuleArgumentReactiveResolver, polarisRateLimiterLimitedFallback); + this.quotaCheckWithRateLimiterLimitedFallbackReactiveFilter = new QuotaCheckReactiveFilter(limitAPI, null, + polarisRateLimitWithHtmlRejectTipsProperties, polarisRateLimiterLimitedFallback); } @Test diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java index 5b5b20165..78a378c5f 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java @@ -23,7 +23,6 @@ import java.io.InputStreamReader; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; -import java.util.Collections; import java.util.stream.Collectors; import com.google.protobuf.InvalidProtocolBufferException; @@ -33,8 +32,6 @@ import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; -import com.tencent.cloud.polaris.ratelimit.resolver.RateLimitRuleArgumentServletResolver; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLimitedFallback; import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; import com.tencent.polaris.ratelimit.api.core.LimitAPI; @@ -70,12 +67,10 @@ @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = QuotaCheckServletFilterTest.TestApplication.class, properties = { - "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" -}) + "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" + }) public class QuotaCheckServletFilterTest { - private final PolarisRateLimiterLabelServletResolver labelResolver = - exchange -> Collections.singletonMap("xxx", "xxx"); private QuotaCheckServletFilter quotaCheckServletFilter; private QuotaCheckServletFilter quotaCheckWithHtmlRejectTipsServletFilter; private QuotaCheckServletFilter quotaCheckWithRateLimiterLimitedFallbackFilter; @@ -96,7 +91,8 @@ else if (serviceName.equals("TestApp2")) { } else if (serviceName.equals("TestApp3")) { QuotaResponse response = new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultLimited, 0, "QuotaResultLimited")); - response.setActiveRule(RateLimitProto.Rule.newBuilder().setName(StringValue.newBuilder().setValue("MOCK_RULE").build()).build()); + response.setActiveRule(RateLimitProto.Rule.newBuilder() + .setName(StringValue.newBuilder().setValue("MOCK_RULE").build()).build()); return response; } else { @@ -114,19 +110,22 @@ else if (serviceName.equals("TestApp3")) { ServiceRuleManager serviceRuleManager = mock(ServiceRuleManager.class); - RateLimitProto.Rule.Builder ratelimitRuleBuilder = RateLimitProto.Rule.newBuilder(); - InputStream inputStream = QuotaCheckServletFilterTest.class.getClassLoader().getResourceAsStream("ratelimit.json"); - String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("")); + RateLimitProto.Rule.Builder ratelimitRuleBuilder = RateLimitProto.Rule.newBuilder(); + InputStream inputStream = QuotaCheckServletFilterTest.class.getClassLoader() + .getResourceAsStream("ratelimit.json"); + String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines() + .collect(Collectors.joining("")); JsonFormat.parser().ignoringUnknownFields().merge(json, ratelimitRuleBuilder); RateLimitProto.Rule rateLimitRule = ratelimitRuleBuilder.build(); RateLimitProto.RateLimit rateLimit = RateLimitProto.RateLimit.newBuilder().addRules(rateLimitRule).build(); when(serviceRuleManager.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit); - RateLimitRuleArgumentServletResolver rateLimitRuleArgumentServletResolver = new RateLimitRuleArgumentServletResolver(serviceRuleManager, labelResolver); - this.quotaCheckServletFilter = new QuotaCheckServletFilter(limitAPI, polarisRateLimitProperties, rateLimitRuleArgumentServletResolver, null); - this.quotaCheckWithHtmlRejectTipsServletFilter = new QuotaCheckServletFilter(limitAPI, polarisRateLimitWithHtmlRejectTipsProperties, rateLimitRuleArgumentServletResolver, null); + this.quotaCheckServletFilter = new QuotaCheckServletFilter(limitAPI, null, polarisRateLimitProperties, null); + this.quotaCheckWithHtmlRejectTipsServletFilter = new QuotaCheckServletFilter(limitAPI, null, + polarisRateLimitWithHtmlRejectTipsProperties, null); this.polarisRateLimiterLimitedFallback = new JsonPolarisRateLimiterLimitedFallback(); - this.quotaCheckWithRateLimiterLimitedFallbackFilter = new QuotaCheckServletFilter(limitAPI, polarisRateLimitWithHtmlRejectTipsProperties, rateLimitRuleArgumentServletResolver, polarisRateLimiterLimitedFallback); + this.quotaCheckWithRateLimiterLimitedFallbackFilter = new QuotaCheckServletFilter(limitAPI, null, + polarisRateLimitWithHtmlRejectTipsProperties, polarisRateLimiterLimitedFallback); } @Test diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolverTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolverTest.java deleted file mode 100644 index 1648a9618..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentReactiveResolverTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.resolver; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.util.JsonFormat; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilterTest; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; -import com.tencent.polaris.ratelimit.api.rpc.Argument; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.http.server.reactive.MockServerHttpRequest; -import org.springframework.mock.web.server.MockServerWebExchange; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.server.ServerWebExchange; - -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAME; -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - classes = RateLimitRuleArgumentReactiveResolverTest.TestApplication.class, - properties = { - "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" - }) -public class RateLimitRuleArgumentReactiveResolverTest { - - private final PolarisRateLimiterLabelReactiveResolver labelResolver = - exchange -> Collections.singletonMap("xxx", "xxx"); - - private final PolarisRateLimiterLabelReactiveResolver labelResolverEx = - exchange -> { - throw new RuntimeException(); - }; - - private RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentReactiveResolver1; - private RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentReactiveResolver2; - private RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentReactiveResolver3; - private RateLimitRuleArgumentReactiveResolver rateLimitRuleArgumentReactiveResolver4; - - @BeforeEach - void setUp() throws InvalidProtocolBufferException { - MetadataContext.LOCAL_NAMESPACE = "TEST"; - - ServiceRuleManager serviceRuleManager = mock(ServiceRuleManager.class); - - RateLimitProto.Rule.Builder ratelimitRuleBuilder = RateLimitProto.Rule.newBuilder(); - InputStream inputStream = QuotaCheckServletFilterTest.class.getClassLoader().getResourceAsStream("ratelimit.json"); - String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("")); - JsonFormat.parser().ignoringUnknownFields().merge(json, ratelimitRuleBuilder); - RateLimitProto.Rule rateLimitRule = ratelimitRuleBuilder.build(); - RateLimitProto.RateLimit rateLimit = RateLimitProto.RateLimit.newBuilder().addRules(rateLimitRule).build(); - when(serviceRuleManager.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit); - - // normal - this.rateLimitRuleArgumentReactiveResolver1 = new RateLimitRuleArgumentReactiveResolver(serviceRuleManager, labelResolver); - // ex - this.rateLimitRuleArgumentReactiveResolver2 = new RateLimitRuleArgumentReactiveResolver(serviceRuleManager, labelResolverEx); - // null - ServiceRuleManager serviceRuleManager1 = mock(ServiceRuleManager.class); - when(serviceRuleManager1.getServiceRateLimitRule(anyString(), anyString())).thenReturn(null); - this.rateLimitRuleArgumentReactiveResolver3 = new RateLimitRuleArgumentReactiveResolver(serviceRuleManager1, labelResolver); - // null 2 - ServiceRuleManager serviceRuleManager2 = mock(ServiceRuleManager.class); - RateLimitProto.RateLimit rateLimit2 = RateLimitProto.RateLimit.newBuilder().build(); - when(serviceRuleManager2.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit2); - this.rateLimitRuleArgumentReactiveResolver4 = new RateLimitRuleArgumentReactiveResolver(serviceRuleManager2, labelResolver); - } - - @Test - public void testGetRuleArguments() { - // Mock request - MetadataContext.LOCAL_SERVICE = "Test"; - // Mock request - MockServerHttpRequest request = MockServerHttpRequest.get("http://127.0.0.1:8080/test") - .remoteAddress(new InetSocketAddress("127.0.0.1", 8080)) - .header("xxx", "xxx") - .queryParam("yyy", "yyy") - .build(); - ServerWebExchange exchange = MockServerWebExchange.from(request); - MetadataContext metadataContext = new MetadataContext(); - metadataContext.setUpstreamDisposableMetadata(new HashMap() {{ - put(DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE, MetadataContext.LOCAL_NAMESPACE); - put(DEFAULT_METADATA_SOURCE_SERVICE_NAME, MetadataContext.LOCAL_SERVICE); - }}); - MetadataContextHolder.set(metadataContext); - Set arguments = rateLimitRuleArgumentReactiveResolver1.getArguments(exchange, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - Set exceptRes = new HashSet<>(); - exceptRes.add(Argument.buildMethod("GET")); - exceptRes.add(Argument.buildHeader("xxx", "xxx")); - exceptRes.add(Argument.buildQuery("yyy", "yyy")); - exceptRes.add(Argument.buildCallerIP("127.0.0.1")); - exceptRes.add(Argument.buildCustom("xxx", "xxx")); - exceptRes.add(Argument.buildCallerService(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE)); - assertThat(arguments).isEqualTo(exceptRes); - - rateLimitRuleArgumentReactiveResolver2.getArguments(exchange, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - rateLimitRuleArgumentReactiveResolver3.getArguments(exchange, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - rateLimitRuleArgumentReactiveResolver4.getArguments(exchange, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - } - - @SpringBootApplication - protected static class TestApplication { - } - -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolverTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolverTest.java deleted file mode 100644 index 1a8b49ed7..000000000 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/resolver/RateLimitRuleArgumentServletResolverTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.resolver; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import com.google.protobuf.InvalidProtocolBufferException; -import com.google.protobuf.util.JsonFormat; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.polaris.context.ServiceRuleManager; -import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilterTest; -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; -import com.tencent.polaris.ratelimit.api.rpc.Argument; -import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAME; -import static com.tencent.cloud.common.constant.MetadataConstant.DefaultMetadata.DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - classes = RateLimitRuleArgumentServletResolverTest.TestApplication.class, - properties = { - "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" - }) -public class RateLimitRuleArgumentServletResolverTest { - - private final PolarisRateLimiterLabelServletResolver labelResolver = - exchange -> Collections.singletonMap("xxx", "xxx"); - private final PolarisRateLimiterLabelServletResolver labelResolverEx = - exchange -> { - throw new RuntimeException(); - }; - - private RateLimitRuleArgumentServletResolver rateLimitRuleArgumentServletResolver1; - private RateLimitRuleArgumentServletResolver rateLimitRuleArgumentServletResolver2; - private RateLimitRuleArgumentServletResolver rateLimitRuleArgumentServletResolver3; - private RateLimitRuleArgumentServletResolver rateLimitRuleArgumentServletResolver4; - - @BeforeEach - void setUp() throws InvalidProtocolBufferException { - MetadataContext.LOCAL_NAMESPACE = "TEST"; - - ServiceRuleManager serviceRuleManager = mock(ServiceRuleManager.class); - - RateLimitProto.Rule.Builder ratelimitRuleBuilder = RateLimitProto.Rule.newBuilder(); - InputStream inputStream = QuotaCheckServletFilterTest.class.getClassLoader().getResourceAsStream("ratelimit.json"); - String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining("")); - JsonFormat.parser().ignoringUnknownFields().merge(json, ratelimitRuleBuilder); - RateLimitProto.Rule rateLimitRule = ratelimitRuleBuilder.build(); - RateLimitProto.RateLimit rateLimit = RateLimitProto.RateLimit.newBuilder().addRules(rateLimitRule).build(); - when(serviceRuleManager.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit); - - // normal - this.rateLimitRuleArgumentServletResolver1 = new RateLimitRuleArgumentServletResolver(serviceRuleManager, labelResolver); - // ex - this.rateLimitRuleArgumentServletResolver2 = new RateLimitRuleArgumentServletResolver(serviceRuleManager, labelResolverEx); - // null - ServiceRuleManager serviceRuleManager1 = mock(ServiceRuleManager.class); - when(serviceRuleManager1.getServiceRateLimitRule(anyString(), anyString())).thenReturn(null); - this.rateLimitRuleArgumentServletResolver3 = new RateLimitRuleArgumentServletResolver(serviceRuleManager1, labelResolver); - // null 2 - ServiceRuleManager serviceRuleManager2 = mock(ServiceRuleManager.class); - RateLimitProto.RateLimit rateLimit2 = RateLimitProto.RateLimit.newBuilder().build(); - when(serviceRuleManager2.getServiceRateLimitRule(anyString(), anyString())).thenReturn(rateLimit2); - this.rateLimitRuleArgumentServletResolver4 = new RateLimitRuleArgumentServletResolver(serviceRuleManager2, labelResolver); - } - - @Test - public void testGetRuleArguments() { - // Mock request - MetadataContext.LOCAL_SERVICE = "Test"; - MockHttpServletRequest request = new MockHttpServletRequest(null, "GET", "/xxx"); - request.setParameter("yyy", "yyy"); - request.addHeader("xxx", "xxx"); - MetadataContext metadataContext = new MetadataContext(); - metadataContext.setUpstreamDisposableMetadata(new HashMap() {{ - put(DEFAULT_METADATA_SOURCE_SERVICE_NAMESPACE, MetadataContext.LOCAL_NAMESPACE); - put(DEFAULT_METADATA_SOURCE_SERVICE_NAME, MetadataContext.LOCAL_SERVICE); - }}); - MetadataContextHolder.set(metadataContext); - Set arguments = rateLimitRuleArgumentServletResolver1.getArguments(request, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - Set exceptRes = new HashSet<>(); - exceptRes.add(Argument.buildMethod("GET")); - exceptRes.add(Argument.buildHeader("xxx", "xxx")); - exceptRes.add(Argument.buildQuery("yyy", "yyy")); - exceptRes.add(Argument.buildCallerIP("127.0.0.1")); - exceptRes.add(Argument.buildCustom("xxx", "xxx")); - exceptRes.add(Argument.buildCallerService(MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE)); - assertThat(arguments).isEqualTo(exceptRes); - - rateLimitRuleArgumentServletResolver2.getArguments(request, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - rateLimitRuleArgumentServletResolver3.getArguments(request, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - rateLimitRuleArgumentServletResolver4.getArguments(request, MetadataContext.LOCAL_NAMESPACE, MetadataContext.LOCAL_SERVICE); - } - - @SpringBootApplication - protected static class TestApplication { - } -} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java index ad2691f4d..792948c88 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java @@ -17,9 +17,6 @@ package com.tencent.cloud.polaris.ratelimit.utils; -import java.util.HashMap; -import java.util.HashSet; - import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; @@ -69,59 +66,28 @@ else if (serviceName.equals("TestApp3")) { public void testGetQuota() { // Pass String serviceName = "TestApp1"; - QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashMap<>(), null); - assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); - assertThat(quotaResponse.getWaitMs()).isEqualTo(0); - assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); - - // Unirate waiting 1000ms - serviceName = "TestApp2"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashMap<>(), null); - assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); - assertThat(quotaResponse.getWaitMs()).isEqualTo(1000); - assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); - - // Rate limited - serviceName = "TestApp3"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashMap<>(), null); - assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultLimited); - assertThat(quotaResponse.getWaitMs()).isEqualTo(0); - assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultLimited"); - - // Exception - serviceName = "TestApp4"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashMap<>(), null); - assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); - assertThat(quotaResponse.getWaitMs()).isEqualTo(0); - assertThat(quotaResponse.getInfo()).isEqualTo("get quota failed"); - } - - @Test - public void testGetQuota2() { - // Pass - String serviceName = "TestApp1"; - QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashSet<>(), null); + QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null); assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); assertThat(quotaResponse.getWaitMs()).isEqualTo(0); assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); // Unirate waiting 1000ms serviceName = "TestApp2"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashSet<>(), null); + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null); assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); assertThat(quotaResponse.getWaitMs()).isEqualTo(1000); assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); // Rate limited serviceName = "TestApp3"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashSet<>(), null); + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null); assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultLimited); assertThat(quotaResponse.getWaitMs()).isEqualTo(0); assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultLimited"); // Exception serviceName = "TestApp4"; - quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, new HashSet<>(), null); + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null); assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); assertThat(quotaResponse.getWaitMs()).isEqualTo(0); assertThat(quotaResponse.getInfo()).isEqualTo("get quota failed"); diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java index 2a1ec1e3e..1f220769d 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java @@ -28,6 +28,8 @@ import com.tencent.polaris.metadata.core.MetadataProvider; import com.tencent.polaris.metadata.core.MetadataType; import com.tencent.polaris.metadata.core.TransitiveType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -42,6 +44,8 @@ */ public final class MetadataContextHolder { + private static final Logger LOG = LoggerFactory.getLogger(MetadataContextHolder.class); + private static StaticMetadataManager staticMetadataManager; static { @@ -58,8 +62,14 @@ public static MetadataContext get() { private static MetadataContext createMetadataManager() { MetadataContext metadataManager = new MetadataContext(); if (staticMetadataManager == null) { - staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext() - .getBean(StaticMetadataManager.class); + if (ApplicationContextAwareUtils.getApplicationContext() != null) { + staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext() + .getBean(StaticMetadataManager.class); + } + else { + // for junit test. + return metadataManager; + } } // local custom metadata MetadataContainer metadataContainer = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false); diff --git a/spring-cloud-tencent-commons/src/main/java/org/springframework/tsf/core/context/TsfContext.java b/spring-cloud-tencent-commons/src/main/java/org/springframework/tsf/core/context/TsfContext.java index 0e7c98a8f..a97f2635c 100644 --- a/spring-cloud-tencent-commons/src/main/java/org/springframework/tsf/core/context/TsfContext.java +++ b/spring-cloud-tencent-commons/src/main/java/org/springframework/tsf/core/context/TsfContext.java @@ -43,7 +43,7 @@ public static void putTags(Map tagMap, Tag.ControlFlag... flags) return; } MetadataContext tsfCoreContext = MetadataContextHolder.get(); - TransitiveType transitive = TransitiveType.NONE; + TransitiveType transitive = TransitiveType.DISPOSABLE; if (null != flags) { for (Tag.ControlFlag flag : flags) { if (flag == Tag.ControlFlag.TRANSITIVE) { diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java index 4690489b2..9ceadc0c9 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java @@ -36,6 +36,7 @@ public class CustomMetadata implements InstanceMetadataProvider { public Map getMetadata() { Map metadata = new HashMap<>(); metadata.put("k1", "v1"); + metadata.put("lane", "lane1"); return metadata; } diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolver.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolver.java deleted file mode 100644 index 3f9676d4d..000000000 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolver.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.quickstart.callee.ratelimit; - -import java.util.HashMap; -import java.util.Map; - -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -/** - * resolver custom label from request. - * - * @author lepdou 2022-03-31 - */ -@Component -public class CustomLabelResolver implements PolarisRateLimiterLabelServletResolver { - private static final Logger LOG = LoggerFactory.getLogger(CustomLabelResolver.class); - - @Value("${label.key-value:}") - private String[] keyValues; - - @Override - public Map resolve(HttpServletRequest request) { - // rate limit by some request params. such as query params, headers .. - - return getLabels(keyValues); - } - - private Map getLabels(String[] keyValues) { - Map labels = new HashMap<>(); - for (String kv : keyValues) { - String key = kv.substring(0, kv.indexOf(":")); - String value = kv.substring(kv.indexOf(":") + 1); - labels.put(key, value); - } - - LOG.info("Current labels:{}", labels); - return labels; - } -} diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/resources/application.yml index 47c90a2ea..973625425 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/resources/application.yml +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-a/src/main/resources/application.yml @@ -49,3 +49,8 @@ management: - polaris-config label: key-value: user:zhangsan +logging: + file: + name: /sct-demo-logs/${spring.application.name}/root.log + level: + root: INFO diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java index 4cbacb2d6..94f239a7f 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/CustomMetadata.java @@ -36,6 +36,7 @@ public class CustomMetadata implements InstanceMetadataProvider { public Map getMetadata() { Map metadata = new HashMap<>(); metadata.put("k1", "v2"); + metadata.put("lane", "lane2"); return metadata; } diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolverReactive.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolverReactive.java deleted file mode 100644 index 4a270fc62..000000000 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/java/com/tencent/cloud/quickstart/callee/ratelimit/CustomLabelResolverReactive.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.quickstart.callee.ratelimit; - -import java.util.HashMap; -import java.util.Map; - -import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.web.server.ServerWebExchange; - -/** - * resolver custom label from request. - * - * @author sean yu - */ -@Component -public class CustomLabelResolverReactive implements PolarisRateLimiterLabelReactiveResolver { - private static final Logger LOG = LoggerFactory.getLogger(CustomLabelResolverReactive.class); - - @Value("${label.key-value:}") - private String[] keyValues; - - @Override - public Map resolve(ServerWebExchange exchange) { - // rate limit by some request params. such as query params, headers .. - return getLabels(keyValues); - } - - private Map getLabels(String[] keyValues) { - Map labels = new HashMap<>(); - for (String kv : keyValues) { - String key = kv.substring(0, kv.indexOf(":")); - String value = kv.substring(kv.indexOf(":") + 1); - labels.put(key, value); - } - - LOG.info("Current labels:{}", labels); - return labels; - } -} diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/resources/application.yml index 544f70796..401ef2aef 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/resources/application.yml +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-callee-service-b/src/main/resources/application.yml @@ -48,3 +48,8 @@ management: - polaris-config label: key-value: user2:lisi +logging: + file: + name: /sct-demo-logs/${spring.application.name}/root.log + level: + root: INFO diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/CustomMetadataProvider.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/CustomMetadataProvider.java index f49315220..2e4326be7 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/CustomMetadataProvider.java +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/CustomMetadataProvider.java @@ -18,6 +18,9 @@ package com.tencent.cloud.quickstart.caller; +import java.util.HashMap; +import java.util.Map; + import com.tencent.cloud.common.spi.InstanceMetadataProvider; import org.springframework.stereotype.Component; @@ -28,6 +31,12 @@ @Component public class CustomMetadataProvider implements InstanceMetadataProvider { + @Override + public Map getMetadata() { + Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + return metadata; + } // @Override // public String getRegion() { // return "huadong"; diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java index 25764d052..7ec58858a 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/java/com/tencent/cloud/quickstart/caller/QuickstartCallerController.java @@ -40,6 +40,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; @@ -100,7 +101,12 @@ public ResponseEntity rest(@RequestHeader Map headerMap) HttpEntity entity = new HttpEntity<>(headers); // 使用 exchange 方法发送 GET 请求,并获取响应 - return restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + try { + return restTemplate.exchange(url, HttpMethod.GET, entity, String.class); + } + catch (HttpClientErrorException | HttpServerErrorException httpClientErrorException) { + return new ResponseEntity<>(httpClientErrorException.getResponseBodyAsString(), httpClientErrorException.getStatusCode()); + } } /** @@ -121,32 +127,33 @@ public Mono webclient() { * Get information 30 times per 1 second. * * @return result of 30 calls. - * @throws InterruptedException exception */ @GetMapping("/ratelimit") - public String invokeInfo() throws InterruptedException { - StringBuffer builder = new StringBuffer(); - CountDownLatch count = new CountDownLatch(30); + public String invokeInfo() { + StringBuilder builder = new StringBuilder(); AtomicInteger index = new AtomicInteger(0); for (int i = 0; i < 30; i++) { - new Thread(() -> { + try { + ResponseEntity entity = restTemplate.getForEntity( + "http://QuickstartCalleeService/quickstart/callee/info", String.class); + builder.append(entity.getBody() + "\n"); try { - ResponseEntity entity = restTemplate.getForEntity( - "http://QuickstartCalleeService/quickstart/callee/info", String.class); - builder.append(entity.getBody() + "\n"); + Thread.sleep(30); } - catch (RestClientException e) { - if (e instanceof HttpClientErrorException.TooManyRequests) { - builder.append("TooManyRequests " + index.incrementAndGet() + "\n"); - } - else { - throw e; - } + catch (InterruptedException e) { + throw new RuntimeException(e); } - count.countDown(); - }).start(); + } + catch (RestClientException e) { + if (e instanceof HttpClientErrorException.TooManyRequests) { + builder.append("TooManyRequests " + index.incrementAndGet() + "\n"); + } + else { + throw e; + } + } } - count.await(); + return builder.toString(); } diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml index a03d7b071..a35213d43 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-caller-service/src/main/resources/application.yml @@ -50,3 +50,8 @@ management: include: - polaris-discovery - polaris-circuit-breaker +logging: + file: + name: /sct-demo-logs/${spring.application.name}/root.log + level: + root: INFO diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml index 39de0709b..d40fd6bf2 100644 --- a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml +++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml @@ -57,3 +57,8 @@ spring: - Path=/QuickstartCallerService/** filters: - StripPrefix=1 +logging: + file: + name: /sct-demo-logs/${spring.application.name}/root.log + level: + root: INFO diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml b/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml index 108f456ec..aa82baace 100644 --- a/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml @@ -27,6 +27,11 @@ spring-cloud-starter-tencent-polaris-contract + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-ratelimit + + org.springframework.boot spring-boot-starter-web diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfContextConfigModifier.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfContextConfigModifier.java index bf681054f..18c55bcb7 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfContextConfigModifier.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfContextConfigModifier.java @@ -53,6 +53,7 @@ public void modify(ConfigurationImpl configuration) { tsfEventReporterConfig.setTsfNamespaceId(tsfCoreProperties.getTsfNamespaceId()); tsfEventReporterConfig.setServiceName(tsfCoreProperties.getServiceName()); tsfEventReporterConfig.setToken(consulProperties.getAclToken()); + tsfEventReporterConfig.setApplicationId(tsfCoreProperties.getTsfApplicationId()); configuration.getGlobal().getEventReporter() .setPluginConfig(DefaultPlugins.TSF_EVENT_REPORTER_TYPE, tsfEventReporterConfig); } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreProperties.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreProperties.java index c60220a9f..39c29baaf 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreProperties.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/extend/tsf/TsfCoreProperties.java @@ -129,6 +129,12 @@ public class TsfCoreProperties { @Value("${tsf_event_master_port:15200}") private Integer eventMasterPort; + @Value("${tsf_ratelimit_master_ip:}") + private String ratelimitMasterIp; + + @Value("${tsf_ratelimit_master_port:7000}") + private Integer ratelimitMasterPort; + public String getAppId() { return appId; } @@ -270,6 +276,22 @@ public void setEventMasterPort(Integer eventMasterPort) { this.eventMasterPort = eventMasterPort; } + public String getRatelimitMasterIp() { + return ratelimitMasterIp; + } + + public void setRatelimitMasterIp(String ratelimitMasterIp) { + this.ratelimitMasterIp = ratelimitMasterIp; + } + + public Integer getRatelimitMasterPort() { + return ratelimitMasterPort; + } + + public void setRatelimitMasterPort(Integer ratelimitMasterPort) { + this.ratelimitMasterPort = ratelimitMasterPort; + } + @Override public String toString() { return "TsfCoreProperties{" + @@ -289,6 +311,8 @@ public String toString() { ", scheme='" + scheme + '\'' + ", eventMasterIp='" + eventMasterIp + '\'' + ", eventMasterPort=" + eventMasterPort + + ", ratelimitMasterIp='" + ratelimitMasterIp + '\'' + + ", ratelimitMasterPort=" + ratelimitMasterPort + '}'; } }