Merge pull request #183 from lepdou/h/expression_rule_label

support parse ratelimit rule expression labels
pull/190/head
Haotian Zhang 3 years ago committed by GitHub
commit cb103080fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,6 @@
# Change Log # Change Log
--- ---
- [fix:Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165)
- [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183)
- [fix:Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165)

@ -0,0 +1,72 @@
/*
* 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.ExpressionLabelUtils;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.polaris.client.pb.ModelProto;
import com.tencent.polaris.client.pb.RateLimitProto;
import org.springframework.util.CollectionUtils;
/**
* resolve labels from rate limit rule.
*
*@author lepdou 2022-05-13
*/
public class RateLimitRuleLabelResolver {
private final ServiceRuleManager serviceRuleManager;
public RateLimitRuleLabelResolver(ServiceRuleManager serviceRuleManager) {
this.serviceRuleManager = serviceRuleManager;
}
public Set<String> getExpressionLabelKeys(String namespace, String service) {
RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(namespace, service);
if (rateLimitRule == null) {
return Collections.emptySet();
}
List<RateLimitProto.Rule> rules = rateLimitRule.getRulesList();
if (CollectionUtils.isEmpty(rules)) {
return Collections.emptySet();
}
Set<String> expressionLabels = new HashSet<>();
for (RateLimitProto.Rule rule : rules) {
Map<String, ModelProto.MatchString> labels = rule.getLabelsMap();
if (CollectionUtils.isEmpty(labels)) {
return Collections.emptySet();
}
for (String key : labels.keySet()) {
if (ExpressionLabelUtils.isExpressionLabel(key)) {
expressionLabels.add(key);
}
}
}
return expressionLabels;
}
}

@ -19,6 +19,8 @@
package com.tencent.cloud.polaris.ratelimit.config; package com.tencent.cloud.polaris.ratelimit.config;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter;
import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter;
@ -62,6 +64,11 @@ public class RateLimitConfiguration {
return LimitAPIFactory.createLimitAPIByContext(polarisContext); return LimitAPIFactory.createLimitAPIByContext(polarisContext);
} }
@Bean
public RateLimitRuleLabelResolver rateLimitRuleLabelService(ServiceRuleManager serviceRuleManager) {
return new RateLimitRuleLabelResolver(serviceRuleManager);
}
/** /**
* Create when web application type is SERVLET. * Create when web application type is SERVLET.
*/ */
@ -73,9 +80,10 @@ public class RateLimitConfiguration {
@ConditionalOnMissingBean @ConditionalOnMissingBean
public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI, public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI,
@Nullable PolarisRateLimiterLabelServletResolver labelResolver, @Nullable PolarisRateLimiterLabelServletResolver labelResolver,
PolarisRateLimitProperties polarisRateLimitProperties) { PolarisRateLimitProperties polarisRateLimitProperties,
RateLimitRuleLabelResolver rateLimitRuleLabelResolver) {
return new QuotaCheckServletFilter(limitAPI, labelResolver, return new QuotaCheckServletFilter(limitAPI, labelResolver,
polarisRateLimitProperties); polarisRateLimitProperties, rateLimitRuleLabelResolver);
} }
@Bean @Bean
@ -101,9 +109,10 @@ public class RateLimitConfiguration {
@Bean @Bean
public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI, public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI,
@Nullable PolarisRateLimiterLabelReactiveResolver labelResolver, @Nullable PolarisRateLimiterLabelReactiveResolver labelResolver,
PolarisRateLimitProperties polarisRateLimitProperties) { PolarisRateLimitProperties polarisRateLimitProperties,
RateLimitRuleLabelResolver rateLimitRuleLabelResolver) {
return new QuotaCheckReactiveFilter(limitAPI, labelResolver, return new QuotaCheckReactiveFilter(limitAPI, labelResolver,
polarisRateLimitProperties); polarisRateLimitProperties, rateLimitRuleLabelResolver);
} }
} }

@ -21,10 +21,14 @@ package com.tencent.cloud.polaris.ratelimit.filter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import com.google.common.collect.Maps;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver;
import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver;
@ -42,7 +46,6 @@ import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain; import org.springframework.web.server.WebFilterChain;
@ -52,7 +55,7 @@ import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LAB
/** /**
* Reactive filter to check quota. * Reactive filter to check quota.
* *
* @author Haotian Zhang * @author Haotian Zhang, lepdou
*/ */
public class QuotaCheckReactiveFilter implements WebFilter, Ordered { public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
@ -65,14 +68,18 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
private final PolarisRateLimitProperties polarisRateLimitProperties; private final PolarisRateLimitProperties polarisRateLimitProperties;
private final RateLimitRuleLabelResolver rateLimitRuleLabelResolver;
private String rejectTips; private String rejectTips;
public QuotaCheckReactiveFilter(LimitAPI limitAPI, public QuotaCheckReactiveFilter(LimitAPI limitAPI,
PolarisRateLimiterLabelReactiveResolver labelResolver, PolarisRateLimiterLabelReactiveResolver labelResolver,
PolarisRateLimitProperties polarisRateLimitProperties) { PolarisRateLimitProperties polarisRateLimitProperties,
RateLimitRuleLabelResolver rateLimitRuleLabelResolver) {
this.limitAPI = limitAPI; this.limitAPI = limitAPI;
this.labelResolver = labelResolver; this.labelResolver = labelResolver;
this.polarisRateLimitProperties = polarisRateLimitProperties; this.polarisRateLimitProperties = polarisRateLimitProperties;
this.rateLimitRuleLabelResolver = rateLimitRuleLabelResolver;
} }
@PostConstruct @PostConstruct
@ -90,27 +97,7 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localNamespace = MetadataContext.LOCAL_NAMESPACE;
String localService = MetadataContext.LOCAL_SERVICE; String localService = MetadataContext.LOCAL_SERVICE;
Map<String, String> labels = new HashMap<>(); Map<String, String> labels = getRequestLabels(exchange, localNamespace, localService);
// 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 { try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
@ -134,4 +121,42 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
return chain.filter(exchange); return chain.filter(exchange);
} }
private Map<String, String> getRequestLabels(ServerWebExchange exchange, String localNamespace, String localService) {
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 rule expression labels
Map<String, String> expressionLabels = getRuleExpressionLabels(exchange, localNamespace, localService);
labels.putAll(expressionLabels);
// add custom labels
Map<String, String> customResolvedLabels = getCustomResolvedLabels(exchange);
labels.putAll(customResolvedLabels);
return labels;
}
private Map<String, String> getCustomResolvedLabels(ServerWebExchange exchange) {
if (labelResolver != null) {
try {
return labelResolver.resolve(exchange);
}
catch (Throwable e) {
LOG.error("resolve custom label failed. resolver = {}",
labelResolver.getClass().getName(), e);
}
}
return Maps.newHashMap();
}
private Map<String, String> getRuleExpressionLabels(ServerWebExchange exchange, String namespace, String service) {
Set<String> expressionLabels = rateLimitRuleLabelResolver.getExpressionLabelKeys(namespace, service);
return ExpressionLabelUtils.resolve(exchange, expressionLabels);
}
} }

@ -19,8 +19,10 @@
package com.tencent.cloud.polaris.ratelimit.filter; package com.tencent.cloud.polaris.ratelimit.filter;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
@ -29,6 +31,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver;
import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties;
import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant;
import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver;
@ -42,7 +46,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD; import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD;
@ -50,13 +53,12 @@ import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LAB
/** /**
* Servlet filter to check quota. * Servlet filter to check quota.
* *
* @author Haotian Zhang * @author Haotian Zhang, lepdou
*/ */
@Order(RateLimitConstant.FILTER_ORDER) @Order(RateLimitConstant.FILTER_ORDER)
public class QuotaCheckServletFilter extends OncePerRequestFilter { public class QuotaCheckServletFilter extends OncePerRequestFilter {
private static final Logger LOG = LoggerFactory private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckServletFilter.class);
.getLogger(QuotaCheckServletFilter.class);
private final LimitAPI limitAPI; private final LimitAPI limitAPI;
@ -64,14 +66,18 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
private final PolarisRateLimitProperties polarisRateLimitProperties; private final PolarisRateLimitProperties polarisRateLimitProperties;
private final RateLimitRuleLabelResolver rateLimitRuleLabelResolver;
private String rejectTips; private String rejectTips;
public QuotaCheckServletFilter(LimitAPI limitAPI, public QuotaCheckServletFilter(LimitAPI limitAPI,
PolarisRateLimiterLabelServletResolver labelResolver, PolarisRateLimiterLabelServletResolver labelResolver,
PolarisRateLimitProperties polarisRateLimitProperties) { PolarisRateLimitProperties polarisRateLimitProperties,
RateLimitRuleLabelResolver rateLimitRuleLabelResolver) {
this.limitAPI = limitAPI; this.limitAPI = limitAPI;
this.labelResolver = labelResolver; this.labelResolver = labelResolver;
this.polarisRateLimitProperties = polarisRateLimitProperties; this.polarisRateLimitProperties = polarisRateLimitProperties;
this.rateLimitRuleLabelResolver = rateLimitRuleLabelResolver;
} }
@PostConstruct @PostConstruct
@ -80,52 +86,68 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter {
} }
@Override @Override
protected void doFilterInternal(HttpServletRequest request, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localNamespace = MetadataContext.LOCAL_NAMESPACE;
String localService = MetadataContext.LOCAL_SERVICE; String localService = MetadataContext.LOCAL_SERVICE;
Map<String, String> labels = getRequestLabels(request, localNamespace, localService);
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
response.setStatus(polarisRateLimitProperties.getRejectHttpCode());
response.getWriter().write(rejectTips);
return;
}
filterChain.doFilter(request, response);
}
catch (Throwable t) {
// An exception occurs in the rate limiting API call,
// which should not affect the call of the business process.
LOG.error("fail to invoke getQuota, service is " + localService, t);
filterChain.doFilter(request, response);
}
}
private Map<String, String> getRequestLabels(HttpServletRequest request, String localNamespace, String localService) {
Map<String, String> labels = new HashMap<>(); Map<String, String> labels = new HashMap<>();
// add build in labels // add build in labels
String path = request.getRequestURI(); String path = request.getRequestURI();
if (StringUtils.isNotBlank(path)) { if (StringUtils.isNotBlank(path)) {
labels.put(LABEL_METHOD, path); labels.put(LABEL_METHOD, path);
} }
// add custom labels // add rule expression labels
Map<String, String> expressionLabels = getRuleExpressionLabels(request, localNamespace, localService);
labels.putAll(expressionLabels);
// add custom resolved labels
Map<String, String> customLabels = getCustomResolvedLabels(request);
labels.putAll(customLabels);
return labels;
}
private Map<String, String> getCustomResolvedLabels(HttpServletRequest request) {
if (labelResolver != null) { if (labelResolver != null) {
try { try {
Map<String, String> customLabels = labelResolver.resolve(request); return labelResolver.resolve(request);
if (!CollectionUtils.isEmpty(customLabels)) {
labels.putAll(customLabels);
}
} }
catch (Throwable e) { catch (Throwable e) {
LOG.error("resolve custom label failed. resolver = {}", LOG.error("resolve custom label failed. resolver = {}",
labelResolver.getClass().getName(), e); labelResolver.getClass().getName(), e);
} }
} }
return Collections.emptyMap();
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI,
localNamespace, localService, 1, labels, null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
response.setStatus(polarisRateLimitProperties.getRejectHttpCode());
response.getWriter().write(rejectTips);
}
else {
filterChain.doFilter(request, response);
}
}
catch (Throwable t) {
// An exception occurs in the rate limiting API call,
// which should not affect the call of the business process.
LOG.error("fail to invoke getQuota, service is " + localService, t);
filterChain.doFilter(request, response);
}
} }
private Map<String, String> getRuleExpressionLabels(HttpServletRequest request, String namespace, String service) {
Set<String> expressionLabels = rateLimitRuleLabelResolver.getExpressionLabelKeys(namespace, service);
return ExpressionLabelUtils.resolve(request, expressionLabels);
}
} }

@ -0,0 +1,187 @@
/*
* 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.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
/**
* the utils for parse label expression.
*
*@author lepdou 2022-05-13
*/
public class ExpressionLabelUtils {
private static final String LABEL_HEADER_PREFIX = "${http.header.";
private static final int LABEL_HEADER_PREFIX_LEN = LABEL_HEADER_PREFIX.length();
private static final String LABEL_QUERY_PREFIX = "${http.query.";
private static final int LABEL_QUERY_PREFIX_LEN = LABEL_QUERY_PREFIX.length();
private static final String LABEL_COOKIE_PREFIX = "${http.cookie.";
private static final int LABEL_COOKIE_PREFIX_LEN = LABEL_COOKIE_PREFIX.length();
private static final String LABEL_METHOD = "${http.method}";
private static final String LABEL_URI = "${http.uri}";
private static final String LABEL_SUFFIX = "}";
public static boolean isExpressionLabel(String labelKey) {
if (StringUtils.isEmpty(labelKey)) {
return false;
}
if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey) ||
StringUtils.startsWithIgnoreCase(LABEL_URI, labelKey)) {
return true;
}
return (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX) ||
StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX) ||
StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX))
&& StringUtils.endsWith(labelKey, LABEL_SUFFIX);
}
public static Map<String, String> resolve(HttpServletRequest request, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
Map<String, String> labels = new HashMap<>();
for (String labelKey : labelKeys) {
if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) {
String headerKey = labelKey.substring(LABEL_HEADER_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, request.getHeader(headerKey));
}
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) {
String queryKey = labelKey.substring(LABEL_QUERY_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, getQueryValue(request.getQueryString(), queryKey));
}
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) {
String cookieKey = labelKey.substring(LABEL_COOKIE_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, getCookieValue(request.getCookies(), cookieKey));
}
else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) {
labels.put(labelKey, request.getMethod());
}
else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) {
labels.put(labelKey, request.getRequestURI());
}
}
return labels;
}
public static Map<String, String> resolve(ServerWebExchange exchange, Set<String> labelKeys) {
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
Map<String, String> labels = new HashMap<>();
for (String labelKey : labelKeys) {
if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) {
String headerKey = labelKey.substring(LABEL_HEADER_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, getHeaderValue(exchange.getRequest(), headerKey));
}
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) {
String queryKey = labelKey.substring(LABEL_QUERY_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, getQueryValue(exchange.getRequest(), queryKey));
}
else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) {
String cookieKey = labelKey.substring(LABEL_COOKIE_PREFIX_LEN, labelKey.length() - 1);
labels.put(labelKey, getCookieValue(exchange.getRequest(), cookieKey));
}
else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) {
labels.put(labelKey, exchange.getRequest().getMethodValue());
}
else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) {
labels.put(labelKey, exchange.getRequest().getURI().getPath());
}
}
return labels;
}
private static String getQueryValue(String queryString, String queryKey) {
if (StringUtils.isBlank(queryString)) {
return StringUtils.EMPTY;
}
String[] queries = StringUtils.split(queryString, "&");
if (queries == null || queries.length == 0) {
return StringUtils.EMPTY;
}
for (String query : queries) {
String[] queryKV = StringUtils.split(query, "=");
if (queryKV != null && queryKV.length == 2 && StringUtils.equals(queryKV[0], queryKey)) {
return queryKV[1];
}
}
return StringUtils.EMPTY;
}
private static String getCookieValue(Cookie[] cookies, String key) {
if (cookies == null || cookies.length == 0) {
return StringUtils.EMPTY;
}
for (Cookie cookie : cookies) {
if (StringUtils.equals(cookie.getName(), key)) {
return cookie.getValue();
}
}
return StringUtils.EMPTY;
}
private static String getHeaderValue(ServerHttpRequest request, String key) {
String value = request.getHeaders().getFirst(key);
if (value == null) {
return StringUtils.EMPTY;
}
return value;
}
private static String getQueryValue(ServerHttpRequest request, String key) {
MultiValueMap<String, String> queries = request.getQueryParams();
if (CollectionUtils.isEmpty(queries)) {
return StringUtils.EMPTY;
}
String value = queries.getFirst(key);
if (value == null) {
return StringUtils.EMPTY;
}
return value;
}
private static String getCookieValue(ServerHttpRequest request, String key) {
HttpCookie cookie = request.getCookies().getFirst(key);
if (cookie == null) {
return StringUtils.EMPTY;
}
return cookie.getValue();
}
}

@ -47,4 +47,8 @@ public class PolarisContextAutoConfiguration {
return new ModifyAddress(); return new ModifyAddress();
} }
@Bean
public ServiceRuleManager serviceRuleManager(SDKContext sdkContext) {
return new ServiceRuleManager(sdkContext);
}
} }

@ -0,0 +1,114 @@
/*
* 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.context;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider;
import com.tencent.polaris.api.pojo.ServiceEventKey;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.pojo.ServiceRule;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.client.flow.BaseFlow;
import com.tencent.polaris.client.flow.DefaultFlowControlParam;
import com.tencent.polaris.client.flow.FlowControlParam;
import com.tencent.polaris.client.flow.ResourcesResponse;
import com.tencent.polaris.client.pb.RateLimitProto;
import com.tencent.polaris.client.pb.RoutingProto;
/**
* the manager of service governance rules. for example: rate limit rule, router rules.
*
*@author lepdou 2022-05-13
*/
public class ServiceRuleManager {
private final SDKContext sdkContext;
private final FlowControlParam controlParam;
public ServiceRuleManager(SDKContext sdkContext) {
this.sdkContext = sdkContext;
controlParam = new DefaultFlowControlParam();
controlParam.setTimeoutMs(sdkContext.getConfig().getGlobal().getAPI().getTimeout());
controlParam.setMaxRetry(sdkContext.getConfig().getGlobal().getAPI().getMaxRetryTimes());
controlParam.setRetryIntervalMs(sdkContext.getConfig().getGlobal().getAPI().getRetryInterval());
}
public RateLimitProto.RateLimit getServiceRateLimitRule(String namespace, String service) {
ServiceEventKey serviceEventKey = new ServiceEventKey(new ServiceKey(namespace, service),
ServiceEventKey.EventType.RATE_LIMITING);
DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider();
svcKeysProvider.setSvcEventKey(serviceEventKey);
ResourcesResponse resourcesResponse = BaseFlow
.syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam);
ServiceRule serviceRule = resourcesResponse.getServiceRule(serviceEventKey);
if (serviceRule != null) {
Object rule = serviceRule.getRule();
if (rule instanceof RateLimitProto.RateLimit) {
return (RateLimitProto.RateLimit) rule;
}
}
return null;
}
public List<RoutingProto.Routing> getServiceRouterRule(String namespace, String sourceService, String dstService) {
Set<ServiceEventKey> routerKeys = new HashSet<>();
ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, dstService),
ServiceEventKey.EventType.ROUTING);
routerKeys.add(dstSvcEventKey);
ServiceEventKey srcSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, sourceService),
ServiceEventKey.EventType.ROUTING);
DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider();
svcKeysProvider.setSvcEventKeys(routerKeys);
ResourcesResponse resourcesResponse = BaseFlow
.syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam);
List<RoutingProto.Routing> rules = new ArrayList<>();
ServiceRule sourceServiceRule = resourcesResponse.getServiceRule(srcSvcEventKey);
if (sourceServiceRule != null) {
Object rule = sourceServiceRule.getRule();
if (rule instanceof RoutingProto.Routing) {
rules.add((RoutingProto.Routing) rule);
}
}
ServiceRule dstServiceRule = resourcesResponse.getServiceRule(dstSvcEventKey);
if (dstServiceRule != null) {
Object rule = dstServiceRule.getRule();
if (rule instanceof RoutingProto.Routing) {
rules.add((RoutingProto.Routing) rule);
}
}
return rules;
}
}
Loading…
Cancel
Save