diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15c44dd5..28359581 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
# Change Log
---
+- [Feature: Support custom rate limit reject response info](https://github.com/Tencent/spring-cloud-tencent/pull/128)
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java
new file mode 100644
index 00000000..d51d90b9
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java
@@ -0,0 +1,71 @@
+/*
+ * 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.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.http.HttpStatus;
+
+/**
+ * The properties for rate limit.
+ *
+ * @author lepdou 2022-04-20
+ */
+@ConfigurationProperties("spring.cloud.polaris.ratelimit")
+public class PolarisRateLimitProperties {
+
+ /**
+ * custom tips when reject request.
+ */
+ private String rejectRequestTips;
+
+ /**
+ * context file path.
+ */
+ private String rejectRequestTipsFilePath;
+
+ /**
+ * custom http code when reject request.
+ */
+ private int rejectHttpCode = HttpStatus.TOO_MANY_REQUESTS.value();
+
+ public String getRejectRequestTips() {
+ return rejectRequestTips;
+ }
+
+ public void setRejectRequestTips(String rejectRequestTips) {
+ this.rejectRequestTips = rejectRequestTips;
+ }
+
+ public String getRejectRequestTipsFilePath() {
+ return rejectRequestTipsFilePath;
+ }
+
+ public void setRejectRequestTipsFilePath(String rejectRequestTipsFilePath) {
+ this.rejectRequestTipsFilePath = rejectRequestTipsFilePath;
+ }
+
+ public int getRejectHttpCode() {
+ return rejectHttpCode;
+ }
+
+ public void setRejectHttpCode(int rejectHttpCode) {
+ this.rejectHttpCode = rejectHttpCode;
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java
index 446b4041..cf4d33a9 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java
@@ -51,6 +51,11 @@ import static javax.servlet.DispatcherType.REQUEST;
matchIfMissing = true)
public class RateLimitConfiguration {
+ @Bean
+ public PolarisRateLimitProperties polarisRateLimitProperties() {
+ return new PolarisRateLimitProperties();
+ }
+
@Bean
@ConditionalOnMissingBean
public LimitAPI limitAPI(SDKContext polarisContext) {
@@ -67,8 +72,10 @@ public class RateLimitConfiguration {
@Bean
@ConditionalOnMissingBean
public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI,
- @Nullable PolarisRateLimiterLabelServletResolver labelResolver) {
- return new QuotaCheckServletFilter(limitAPI, labelResolver);
+ @Nullable PolarisRateLimiterLabelServletResolver labelResolver,
+ PolarisRateLimitProperties polarisRateLimitProperties) {
+ return new QuotaCheckServletFilter(limitAPI, labelResolver,
+ polarisRateLimitProperties);
}
@Bean
@@ -93,8 +100,10 @@ public class RateLimitConfiguration {
@Bean
public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI,
- @Nullable PolarisRateLimiterLabelReactiveResolver labelResolver) {
- return new QuotaCheckReactiveFilter(limitAPI, labelResolver);
+ @Nullable PolarisRateLimiterLabelReactiveResolver labelResolver,
+ PolarisRateLimitProperties polarisRateLimitProperties) {
+ return new QuotaCheckReactiveFilter(limitAPI, labelResolver,
+ polarisRateLimitProperties);
}
}
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 0db616f7..9a0db9a7 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
@@ -22,10 +22,14 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.PostConstruct;
+
import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
+import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
@@ -36,7 +40,6 @@ import reactor.core.publisher.Mono;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
@@ -60,10 +63,21 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
private final PolarisRateLimiterLabelReactiveResolver labelResolver;
+ private final PolarisRateLimitProperties polarisRateLimitProperties;
+
+ private String rejectTips;
+
public QuotaCheckReactiveFilter(LimitAPI limitAPI,
- PolarisRateLimiterLabelReactiveResolver labelResolver) {
+ PolarisRateLimiterLabelReactiveResolver labelResolver,
+ PolarisRateLimitProperties polarisRateLimitProperties) {
this.limitAPI = limitAPI;
this.labelResolver = labelResolver;
+ this.polarisRateLimitProperties = polarisRateLimitProperties;
+ }
+
+ @PostConstruct
+ public void init() {
+ rejectTips = RateLimitUtils.getRejectTips(polarisRateLimitProperties);
}
@Override
@@ -104,11 +118,10 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
ServerHttpResponse response = exchange.getResponse();
- response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
+ response.setRawStatusCode(polarisRateLimitProperties.getRejectHttpCode());
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer()
- .write(RateLimitConstant.QUOTA_LIMITED_INFO
- .getBytes(StandardCharsets.UTF_8));
+ .write(rejectTips.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
}
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 85706d94..ae160109 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,15 +22,18 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import javax.annotation.PostConstruct;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
+import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
@@ -43,7 +46,6 @@ import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD;
-import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
/**
* Servlet filter to check quota.
@@ -60,10 +62,21 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
private final PolarisRateLimiterLabelServletResolver labelResolver;
+ private final PolarisRateLimitProperties polarisRateLimitProperties;
+
+ private String rejectTips;
+
public QuotaCheckServletFilter(LimitAPI limitAPI,
- PolarisRateLimiterLabelServletResolver labelResolver) {
+ PolarisRateLimiterLabelServletResolver labelResolver,
+ PolarisRateLimitProperties polarisRateLimitProperties) {
this.limitAPI = limitAPI;
this.labelResolver = labelResolver;
+ this.polarisRateLimitProperties = polarisRateLimitProperties;
+ }
+
+ @PostConstruct
+ public void init() {
+ rejectTips = RateLimitUtils.getRejectTips(polarisRateLimitProperties);
}
@Override
@@ -100,8 +113,8 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
- response.setStatus(TOO_MANY_REQUESTS.value());
- response.getWriter().write(RateLimitConstant.QUOTA_LIMITED_INFO);
+ response.setStatus(polarisRateLimitProperties.getRejectHttpCode());
+ response.getWriter().write(rejectTips);
}
else {
filterChain.doFilter(request, response);
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
new file mode 100644
index 00000000..2e036909
--- /dev/null
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java
@@ -0,0 +1,70 @@
+/*
+ * Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
+ *
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ *
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed
+ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ *
+ */
+
+package com.tencent.cloud.polaris.ratelimit.utils;
+
+import com.tencent.cloud.common.util.ResourceFileUtils;
+import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
+import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
+import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * Rate limit utils.
+ *
+ * @author lepdou 2022-04-20
+ */
+public final class RateLimitUtils {
+
+ private static final Logger LOG = LoggerFactory
+ .getLogger(QuotaCheckServletFilter.class);
+
+ private RateLimitUtils() {
+
+ }
+
+ public static String getRejectTips(
+ PolarisRateLimitProperties polarisRateLimitProperties) {
+ String tips = polarisRateLimitProperties.getRejectRequestTips();
+
+ if (!StringUtils.isEmpty(tips)) {
+ return tips;
+ }
+
+ String rejectFilePath = polarisRateLimitProperties.getRejectRequestTipsFilePath();
+ if (!StringUtils.isEmpty(rejectFilePath)) {
+ try {
+ tips = ResourceFileUtils.readFile(rejectFilePath);
+ }
+ catch (Exception e) {
+ LOG.error("[RateLimit] Read custom reject tips file error. path = {}",
+ rejectFilePath, e);
+ }
+ }
+
+ if (!StringUtils.isEmpty(tips)) {
+ return tips;
+ }
+
+ return RateLimitConstant.QUOTA_LIMITED_INFO;
+ }
+
+}
diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index b71a17cc..19b37aeb 100644
--- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -5,6 +5,24 @@
"type": "java.lang.Boolean",
"defaultValue": true,
"description": "Enable polaris rate limit or not."
+ },
+ {
+ "name": "spring.cloud.polaris.ratelimit.rejectRequestTips",
+ "type": "java.lang.String",
+ "defaultValue": "",
+ "description": "Custom tips when reject request."
+ },
+ {
+ "name": "spring.cloud.polaris.ratelimit.rejectRequestTipsFilePath",
+ "type": "java.lang.String",
+ "defaultValue": "",
+ "description": "Custom tips file path when reject request."
+ },
+ {
+ "name": "spring.cloud.polaris.ratelimit.rejectHttpCode",
+ "type": "java.lang.Integer",
+ "defaultValue": "429",
+ "description": "Custom http code when reject request."
}
]
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java
new file mode 100644
index 00000000..d79bfce8
--- /dev/null
+++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java
@@ -0,0 +1,54 @@
+/*
+ * 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.common.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.core.io.ClassPathResource;
+
+/**
+ * Read file content from classpath resource.
+ *
+ * @author lepdou 2022-04-20
+ */
+public final class ResourceFileUtils {
+
+ private ResourceFileUtils() {
+ }
+
+ public static String readFile(String path) throws IOException {
+ StringBuilder sb = new StringBuilder();
+
+ ClassPathResource classPathResource = new ClassPathResource(path);
+
+ if (classPathResource.exists() && classPathResource.isReadable()) {
+ try (InputStream inputStream = classPathResource.getInputStream()) {
+ byte[] buffer = new byte[1024 * 10];
+ int len;
+ while ((len = inputStream.read(buffer)) != -1) {
+ sb.append(new String(buffer, 0, len, StandardCharsets.UTF_8));
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml
index 62af8391..dd28040b 100644
--- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml
@@ -1,27 +1,36 @@