add custom label resolver spi for rate limit

pull/107/head
lepdou 3 years ago
parent ce20863792
commit 7988a982c1

@ -1,19 +1,5 @@
# Change Log
---
- [the server address only support one ip](https://github.com/Tencent/spring-cloud-tencent/pull/36)
- [feature: get service list by given namespace](https://github.com/Tencent/spring-cloud-tencent/pull/38)
- [feat:Support multi-discovery server.](https://github.com/Tencent/spring-cloud-tencent/pull/42)
- [fix:fix [Deserialization of Untrusted Data in logback]](https://github.com/Tencent/spring-cloud-tencent/pull/44/files)
- [feat:support customize registered ip address.](https://github.com/Tencent/spring-cloud-tencent/pull/47)
- [fix:fix import default log implement error](https://github.com/Tencent/spring-cloud-tencent/pull/53)
- [send heartbeat if healthcheck url not configured](https://github.com/Tencent/spring-cloud-tencent/pull/54)
- [feat:optimize project structure and checkstyle](https://github.com/Tencent/spring-cloud-tencent/pull/56)
- [feat:divide storage and transfer of metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/63)
- [feat:support polaris config center.](https://github.com/Tencent/spring-cloud-tencent/pull/69)
- [feat:optimize metadata module.](https://github.com/Tencent/spring-cloud-tencent/pull/70)
- [feat: optimize pom dependency and demo](https://github.com/Tencent/spring-cloud-tencent/pull/71)
- [feat:upgrade polaris version to 1.3.0.](https://github.com/Tencent/spring-cloud-tencent/pull/72)
- [feat:rollback init polaris-context to application phase](https://github.com/Tencent/spring-cloud-tencent/pull/73)
- [fix:fix junit not working bug.](https://github.com/Tencent/spring-cloud-tencent/pull/96)
- [feat:add custom label resolver spi for rate limit](https://github.com/Tencent/spring-cloud-tencent/pull/107)

@ -13,6 +13,7 @@
* 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;
@ -20,6 +21,8 @@ package com.tencent.cloud.polaris.ratelimit.config;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter;
import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.factory.LimitAPIFactory;
@ -30,6 +33,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import static javax.servlet.DispatcherType.ASYNC;
import static javax.servlet.DispatcherType.ERROR;
@ -59,8 +63,9 @@ public class RateLimitConfiguration {
@Bean
@ConditionalOnMissingBean
public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI) {
return new QuotaCheckServletFilter(limitAPI);
public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI,
@Nullable PolarisRateLimiterLabelServletResolver labelResolver) {
return new QuotaCheckServletFilter(limitAPI, labelResolver);
}
@Bean
@ -84,8 +89,9 @@ public class RateLimitConfiguration {
static class MetadataReactiveFilterConfig {
@Bean
public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI) {
return new QuotaCheckReactiveFilter(limitAPI);
public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI,
@Nullable PolarisRateLimiterLabelReactiveResolver labelResolver) {
return new QuotaCheckReactiveFilter(limitAPI, labelResolver);
}
}

@ -37,6 +37,11 @@ public final class RateLimitConstant {
*/
public static String QUOTA_LIMITED_INFO = "The request is deny by rate limit because the throttling threshold is reached";
/**
* The build in label method.
*/
public static String LABEL_METHOD = "method";
private RateLimitConstant() {
}

@ -13,6 +13,7 @@
* 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.filter;
@ -23,6 +24,7 @@ import java.util.Map;
import com.tencent.cloud.common.metadata.MetadataContext;
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.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
@ -37,10 +39,13 @@ 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;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD;
/**
* Reactive filter to check quota.
*
@ -52,8 +57,12 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
private final LimitAPI limitAPI;
public QuotaCheckReactiveFilter(LimitAPI limitAPI) {
private final PolarisRateLimiterLabelReactiveResolver labelResolver;
public QuotaCheckReactiveFilter(LimitAPI limitAPI,
PolarisRateLimiterLabelReactiveResolver labelResolver) {
this.limitAPI = limitAPI;
this.labelResolver = labelResolver;
}
@Override
@ -65,23 +74,37 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String localNamespace = MetadataContext.LOCAL_NAMESPACE;
String localService = MetadataContext.LOCAL_SERVICE;
String method = exchange.getRequest().getURI().getPath();
Map<String, String> labels = null;
if (StringUtils.isNotBlank(method)) {
labels = new HashMap<>();
labels.put("method", method);
Map<String, String> labels = new HashMap<>();
// add build in labels
String path = exchange.getRequest().getURI().getPath();
if (StringUtils.isNotBlank(path)) {
labels.put(LABEL_METHOD, path);
}
// add custom labels
if (labelResolver != null) {
try {
Map<String, String> customLabels = labelResolver.resolve(exchange);
if (!CollectionUtils.isEmpty(customLabels)) {
labels.putAll(customLabels);
}
} catch (Throwable e) {
LOG.error("resolve custom label failed. resolver = {}", labelResolver.getClass().getName(), e);
}
}
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, labels,
null);
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer()
.write((RateLimitConstant.QUOTA_LIMITED_INFO + quotaResponse.getInfo())
.getBytes(StandardCharsets.UTF_8));
DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write(
RateLimitConstant.QUOTA_LIMITED_INFO.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(dataBuffer));
}
}

@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.common.metadata.MetadataContext;
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.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
@ -38,8 +39,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
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;
/**
@ -53,9 +56,11 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckServletFilter.class);
private final LimitAPI limitAPI;
private final PolarisRateLimiterLabelServletResolver labelResolver;
public QuotaCheckServletFilter(LimitAPI limitAPI) {
public QuotaCheckServletFilter(LimitAPI limitAPI, PolarisRateLimiterLabelServletResolver labelResolver) {
this.limitAPI = limitAPI;
this.labelResolver = labelResolver;
}
@Override
@ -63,11 +68,26 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
throws ServletException, IOException {
String localNamespace = MetadataContext.LOCAL_NAMESPACE;
String localService = MetadataContext.LOCAL_SERVICE;
String method = request.getRequestURI();
Map<String, String> labels = null;
if (StringUtils.isNotBlank(method)) {
labels = new HashMap<>();
labels.put("method", method);
Map<String, String> labels = new HashMap<>();
// add build in labels
String path = request.getRequestURI();
if (StringUtils.isNotBlank(path)) {
labels.put(LABEL_METHOD, path);
}
// add custom labels
if (labelResolver != null) {
try {
Map<String, String> customLabels = labelResolver.resolve(request);
if (!CollectionUtils.isEmpty(customLabels)) {
labels.putAll(customLabels);
}
} catch (Throwable e) {
LOG.error("resolve custom label failed. resolver = {}", labelResolver.getClass().getName(), e);
}
}
try {

@ -0,0 +1,40 @@
/*
* 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<String, String> resolve(ServerWebExchange exchange);
}

@ -0,0 +1,40 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
/**
* Resolve custom label from request. The label used for rate limit params.
*
* @author lepdou 2022-03-31
*/
public interface PolarisRateLimiterLabelServletResolver {
/**
* Resolve custom label from request.
*
* @param request the http request
* @return resolved labels
*/
Map<String, String> resolve(HttpServletRequest request);
}

@ -0,0 +1,47 @@
/*
* 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.ratelimit.example.service.callee;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver;
import org.springframework.stereotype.Component;
/**
* resolver custom label from request.
*
*@author lepdou 2022-03-31
*/
@Component
public class CustomLabelResolver implements PolarisRateLimiterLabelServletResolver {
@Override
public Map<String, String> resolve(HttpServletRequest request) {
//rate limit by some request params. such as query params, headers ..
Map<String, String> labels = new HashMap<>();
labels.put("user", "zhangsan");
return labels;
}
}
Loading…
Cancel
Save