feat:add polaris rate limit in zuul

pull/15/head
SkyeBeFreeman 3 years ago
parent 67d30e94cb
commit b9ebbcdada

@ -38,6 +38,7 @@ import org.springframework.context.annotation.Configuration;
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@Configuration @Configuration
@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true)
public class RateLimitConfiguration { public class RateLimitConfiguration {
@Bean @Bean
@ -51,7 +52,6 @@ public class RateLimitConfiguration {
*/ */
@Configuration @Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true)
static class QuotaCheckFilterConfig { static class QuotaCheckFilterConfig {
@Bean @Bean

@ -22,8 +22,8 @@ import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey; import com.tencent.cloud.metadata.constant.MetadataConstant.SystemMetadataKey;
import com.tencent.cloud.metadata.context.MetadataContextHolder; import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.ratelimit.utils.Consts; import com.tencent.cloud.polaris.ratelimit.utils.Consts;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import java.io.IOException; import java.io.IOException;
@ -59,21 +59,18 @@ public class QuotaCheckFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { throws ServletException, IOException {
QuotaRequest quotaRequest = new QuotaRequest();
String localNamespace = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE); String localNamespace = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_NAMESPACE);
String localService = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE); String localService = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_SERVICE);
quotaRequest.setNamespace(localNamespace);
quotaRequest.setService(localService);
quotaRequest.setCount(1);
String method = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_PATH); String method = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.LOCAL_PATH);
Map<String, String> labels = null;
if (StringUtils.isNotBlank(method)) { if (StringUtils.isNotBlank(method)) {
Map<String, String> labels = new HashMap<>(); labels = new HashMap<>();
labels.put("method", method); labels.put("method", method);
quotaRequest.setLabels(labels);
} }
try { try {
QuotaResponse quotaResponse = limitAPI.getQuota(quotaRequest); QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, localNamespace, localService, 1, labels,
null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
response.setStatus(TOO_MANY_REQUESTS.value()); response.setStatus(TOO_MANY_REQUESTS.value());
response.getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo()); response.getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo());

@ -0,0 +1,55 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.ratelimit.utils;
import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* Utils for quota checking
*
* @author Haotian Zhang
*/
public class QuotaCheckUtils {
private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckUtils.class);
public static QuotaResponse getQuota(LimitAPI limitAPI, String namespace, String service, int count,
Map<String, String> 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"));
}
}
}

@ -24,6 +24,11 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-metadata</artifactId> <artifactId>spring-cloud-tencent-metadata</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end --> <!-- Spring Cloud Tencent dependencies end -->
<dependency> <dependency>

@ -19,8 +19,10 @@ package com.tencent.cloud.polaris.gateway.config;
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.http.ZuulServlet; import com.netflix.zuul.http.ZuulServlet;
import com.tencent.cloud.polaris.gateway.core.scg.filter.MetadataScgFilter; import com.tencent.cloud.polaris.gateway.core.scg.filter.Metadata2HeaderScgFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.MetadataZuulFilter; import com.tencent.cloud.polaris.gateway.core.zuul.filter.Metadata2HeaderZuulFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.MetadataFirstZuulFilter;
import com.tencent.cloud.polaris.gateway.core.zuul.filter.RateLimitZuulFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -38,8 +40,17 @@ public class PolarisGatewayAutoConfiguration {
@ConditionalOnClass(ZuulServlet.class) @ConditionalOnClass(ZuulServlet.class)
static class PolarisGatewayZuulAutoConfiguration { static class PolarisGatewayZuulAutoConfiguration {
@Bean @Bean
public ZuulFilter metadataZuulFilter() { public ZuulFilter metadataFirstZuulFilter() {
return new MetadataZuulFilter(); return new MetadataFirstZuulFilter();
}
@Bean
public ZuulFilter rateLimitZuulFilter() {
return new RateLimitZuulFilter();
}
@Bean
public ZuulFilter metadata2HeaderZuulFilter() {
return new Metadata2HeaderZuulFilter();
} }
} }
@ -47,8 +58,8 @@ public class PolarisGatewayAutoConfiguration {
@ConditionalOnClass(GlobalFilter.class) @ConditionalOnClass(GlobalFilter.class)
static class PolarisGatewayScgAutoConfiguration { static class PolarisGatewayScgAutoConfiguration {
@Bean @Bean
public GlobalFilter metadataScgFilter() { public GlobalFilter metadata2HeaderScgFilter() {
return new MetadataScgFilter(); return new Metadata2HeaderScgFilter();
} }
} }

@ -36,9 +36,9 @@ import java.util.Map;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class MetadataScgFilter extends AbstractGlobalFilter { public class Metadata2HeaderScgFilter extends AbstractGlobalFilter {
private static final int METADATA_SCG_FILTER_ORDER = 10151; private static final int METADATA_SCG_FILTER_ORDER = 10152;
@Override @Override
public int getOrder() { public int getOrder() {

@ -38,7 +38,7 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst
* *
* @author skyehtzhang * @author skyehtzhang
*/ */
public class MetadataZuulFilter extends ZuulFilter { public class Metadata2HeaderZuulFilter extends ZuulFilter {
@Override @Override
public String filterType() { public String filterType() {

@ -0,0 +1,64 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.core.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_URI_KEY;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY;
/**
* Zuul filter used for setting peer info in context.
*
* @author Haotian Zhang
*/
public class MetadataFirstZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return PRE_DECORATION_FILTER_ORDER + 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
// get request context
RequestContext requestContext = RequestContext.getCurrentContext();
// TODO 对端命名空间暂时与本地命名空间相同
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE,
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.LOCAL_NAMESPACE));
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE,
(String) requestContext.get(SERVICE_ID_KEY));
MetadataContextHolder.get().putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH,
(String) requestContext.get(REQUEST_URI_KEY));
return null;
}
}

@ -0,0 +1,99 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.core.zuul.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.tencent.cloud.metadata.constant.MetadataConstant;
import com.tencent.cloud.metadata.context.MetadataContextHolder;
import com.tencent.cloud.polaris.ratelimit.utils.Consts;
import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils;
import com.tencent.polaris.ratelimit.api.core.LimitAPI;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse;
import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.RIBBON_ROUTING_FILTER_ORDER;
import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS;
/**
* Zuul filter used for rate limit.
*
* @author Haotian Zhang
*/
public class RateLimitZuulFilter extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(RateLimitZuulFilter.class);
@Autowired(required = false)
private LimitAPI limitAPI;
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return RIBBON_ROUTING_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return limitAPI != null;
}
@Override
public Object run() {
// get request context
RequestContext requestContext = RequestContext.getCurrentContext();
String peerNamespace =
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_NAMESPACE);
String peerService =
MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE);
String peerPath = MetadataContextHolder.get().getSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH);
Map<String, String> labels = null;
if (StringUtils.isNotBlank(peerPath)) {
labels = new HashMap<>();
labels.put("method", peerPath);
}
try {
QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, peerNamespace, peerService, 1, labels,
null);
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(TOO_MANY_REQUESTS.value());
requestContext.getResponse().getWriter().write(Consts.QUOTA_LIMITED_INFO + quotaResponse.getInfo());
}
} catch (Throwable throwable) {
//限流API调用出现异常不应该影响业务流程的调用
LOG.error("fail to rate limit with QuotaRequest[{}-{}-{}].", peerNamespace, peerService, peerPath,
throwable);
}
return null;
}
}
Loading…
Cancel
Save