Merge branch 'greenwich' into greenwich

pull/166/head
Haotian Zhang 3 years ago committed by GitHub
commit 9e296ce1da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,7 @@
# Change Log
---
- [Feature: Support custom rate limit reject response info](https://github.com/Tencent/spring-cloud-tencent/pull/154)
- [Feature: Remove spring-javaformat-maven-plugin](https://github.com/Tencent/spring-cloud-tencent/pull/152)
- [feat:optimize config server address](https://github.com/Tencent/spring-cloud-tencent/pull/150)
- [feat:refactor loadbalancer module as a basic module for router and circuit breaker.](https://github.com/Tencent/spring-cloud-tencent/pull/166)

@ -137,10 +137,6 @@
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>

@ -18,16 +18,19 @@
package com.tencent.cloud.polaris.config;
import java.util.ArrayList;
import java.util.List;
import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.common.util.AddressUtils;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.PolarisContextProperties;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
/**
* Read configuration from spring cloud's configuration file and override polaris.yaml.
@ -36,22 +39,38 @@ import org.springframework.util.StringUtils;
*/
public class ConfigurationModifier implements PolarisConfigModifier {
@Autowired
private PolarisConfigProperties polarisConfigProperties;
private final PolarisConfigProperties polarisConfigProperties;
private final PolarisContextProperties polarisContextProperties;
public ConfigurationModifier(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
this.polarisConfigProperties = polarisConfigProperties;
this.polarisContextProperties = polarisContextProperties;
}
@Override
public void modify(ConfigurationImpl configuration) {
// set connector type
configuration.getConfigFile().getServerConnector().setConnectorType("polaris");
if (StringUtils.isEmpty(polarisConfigProperties.getAddress())) {
return;
// set config server address
List<String> configAddresses;
String configAddressesStr = polarisConfigProperties.getAddress();
if (StringUtils.isNotEmpty(configAddressesStr)) {
configAddresses = AddressUtils.parseAddressList(polarisConfigProperties.getAddress());
}
else {
configAddresses = resolveConfigAddressFromPolarisAddress(polarisContextProperties.getAddress());
}
// override polaris config server address
List<String> addresses = AddressUtils
.parseAddressList(polarisConfigProperties.getAddress());
if (CollectionUtils.isEmpty(configAddresses)) {
throw new RuntimeException("Config server address is blank. Please check your config in bootstrap.yml"
+ " with spring.cloud.polaris.address or spring.cloud.polaris.config.address");
}
configuration.getConfigFile().getServerConnector().setAddresses(addresses);
configuration.getConfigFile().getServerConnector().setAddresses(configAddresses);
}
@Override
@ -59,4 +78,24 @@ public class ConfigurationModifier implements PolarisConfigModifier {
return ContextConstant.ModifierOrder.CONFIG_ORDER;
}
/**
* In most cases, the address of the configuration center is the same as that of Polaris, but the port is different.
* Therefore, the address of the configuration center can be deduced directly from the Polaris address.
*
*/
private List<String> resolveConfigAddressFromPolarisAddress(String polarisAddress) {
if (StringUtils.isEmpty(polarisAddress)) {
return null;
}
List<String> polarisAddresses = AddressUtils.parseAddressList(polarisAddress);
List<String> configAddresses = new ArrayList<>(polarisAddresses.size());
for (String address : polarisAddresses) {
String ip = StringUtils.substringBeforeLast(address, ":");
configAddresses.add(ip + ":" + polarisConfigProperties.getPort());
}
return configAddresses;
}
}

@ -70,8 +70,9 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
public ConfigurationModifier configurationModifier() {
return new ConfigurationModifier();
public ConfigurationModifier configurationModifier(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties);
}
}

@ -39,6 +39,11 @@ public class PolarisConfigProperties {
*/
private String address;
/**
* Polaris config grpc port.
*/
private int port = 8093;
/**
* Whether to automatically update to the spring context when the configuration file.
* is updated
@ -66,6 +71,14 @@ public class PolarisConfigProperties {
this.address = address;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean isAutoRefresh() {
return autoRefresh;
}

@ -14,6 +14,13 @@
"description": "The polaris configuration server address.",
"sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties"
},
{
"name": "spring.cloud.polaris.config.port",
"type": "java.lang.Integer",
"defaultValue": "8093",
"description": "The polaris configuration server port.",
"sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties"
},
{
"name": "spring.cloud.polaris.config.auto-refresh",
"type": "java.lang.Boolean",

@ -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;
}
}

@ -49,6 +49,11 @@ import static javax.servlet.DispatcherType.REQUEST;
@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true)
public class RateLimitConfiguration {
@Bean
public PolarisRateLimitProperties polarisRateLimitProperties() {
return new PolarisRateLimitProperties();
}
@Bean
@ConditionalOnMissingBean
public LimitAPI limitAPI(SDKContext polarisContext) {
@ -65,8 +70,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
@ -91,8 +98,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);
}
}

@ -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;
@ -60,10 +64,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 +119,18 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered {
if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
HttpStatus httpStatus;
try {
httpStatus = HttpStatus.valueOf(polarisRateLimitProperties.getRejectHttpCode());
}
catch (IllegalArgumentException e) {
LOG.error("Illegal custom reject http code, will fallback to default http code 429 [TOO_MANY_REQUESTS]");
httpStatus = HttpStatus.TOO_MANY_REQUESTS;
}
response.setStatusCode(httpStatus);
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));
}
}

@ -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);

@ -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;
}
}

@ -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."
}
]
}

@ -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();
}
}

@ -5,9 +5,9 @@ spring:
name: polaris-config-example
cloud:
polaris:
address: grpc://127.0.0.1:8091
namespace: default
config:
address: grpc://127.0.0.1:8093
auto-refresh: true # auto refresh when config file changed
groups:
- name: ${spring.application.name} # group name

@ -1,27 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>polaris-ratelimit-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>polaris-ratelimit-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ratelimit-callee-service</artifactId>
<artifactId>ratelimit-callee-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency>
</dependencies>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

@ -10,3 +10,4 @@ spring:
enabled: true
ratelimit:
enabled: true
rejectRequestTipsFilePath: reject-tips.html

Loading…
Cancel
Save