diff --git a/CHANGELOG.md b/CHANGELOG.md
index da76a8e80b..894650e122 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,3 +4,4 @@
- [feat: implement circuit breaker in enhance plugin, support listen config group, support refresh single config in refresh_context mode.](https://github.com/Tencent/spring-cloud-tencent/pull/1490)
- [feat:support polaris event.](https://github.com/Tencent/spring-cloud-tencent/pull/1494)
- [feat:support circuit breaker metrics reporting.](https://github.com/Tencent/spring-cloud-tencent/pull/1495)
+- [feat: support gateway context, feign eager-load support default value.](https://github.com/Tencent/spring-cloud-tencent/pull/1496)
diff --git a/spring-cloud-starter-tencent-all/pom.xml b/spring-cloud-starter-tencent-all/pom.xml
index 31c46e67a4..b4c07bf58d 100644
--- a/spring-cloud-starter-tencent-all/pom.xml
+++ b/spring-cloud-starter-tencent-all/pom.xml
@@ -55,6 +55,11 @@
spring-cloud-starter-tencent-trace-plugin
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-gateway-plugin
+
+
com.tencent.cloud
spring-cloud-starter-tencent-polaris-auth
diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
index 712a9b9c44..1d3cdf0967 100644
--- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
+++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java
@@ -22,6 +22,7 @@
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.constant.OrderConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.common.util.UrlUtils;
@@ -95,6 +96,11 @@ public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain web
MetadataConstant.HeaderName.METADATA_CONTEXT,
MetadataContextHolder.get());
+ String targetNamespace = serverWebExchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.NAMESPACE);
+ if (StringUtils.isNotBlank(targetNamespace)) {
+ MetadataContextHolder.get().putFragmentContext(MetadataContext.FRAGMENT_APPLICATION_NONE,
+ MetadataConstant.POLARIS_TARGET_NAMESPACE, targetNamespace);
+ }
TransHeadersTransfer.transfer(serverHttpRequest);
return webFilterChain.filter(serverWebExchange)
.doFinally((type) -> MetadataContextHolder.remove());
diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java
index b33295b68b..a7c10c5b9a 100644
--- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java
+++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/reporter/CircuitBreakerPlugin.java
@@ -28,6 +28,7 @@
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedRequestContext;
import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext;
import com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter;
+import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.circuitbreak.client.exception.CallAbortedException;
import com.tencent.polaris.metadata.core.MetadataType;
import org.slf4j.Logger;
@@ -69,10 +70,10 @@ public void run(EnhancedPluginContext context) throws Throwable {
EnhancedRequestContext request = context.getRequest();
EnhancedResponseContext response = context.getResponse();
- String governanceNamespace = MetadataContext.LOCAL_NAMESPACE;
-
String host = request.getServiceUrl() != null ? request.getServiceUrl().getHost() : request.getUrl().getHost();
String path = request.getServiceUrl() != null ? request.getServiceUrl().getPath() : request.getUrl().getPath();
+ String governanceNamespace = StringUtils.isNotEmpty(request.getGovernanceNamespace()) ? request.getGovernanceNamespace() : MetadataContext.LOCAL_NAMESPACE;
+
String httpMethod = request.getHttpMethod().name();
CircuitBreaker circuitBreaker = circuitBreakerFactory.create(governanceNamespace + "#" + host + "#" + path + "#http#" + httpMethod);
diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
index a903d1f999..3c8b1bf4f5 100644
--- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
+++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java
@@ -28,6 +28,7 @@
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.enums.ConfigFileFormat;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
+import com.tencent.polaris.api.utils.ClassUtils;
import com.tencent.polaris.configuration.api.core.ConfigFileMetadata;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
@@ -213,13 +214,17 @@ void initTsfConfigGroups(CompositePropertySource compositePropertySource) {
String tsfNamespaceName = environment.getProperty("tsf_namespace_name");
String tsfGroupName = environment.getProperty("tsf_group_name");
- if (StringUtils.isEmpty(tsfId) || StringUtils.isEmpty(tsfNamespaceName) || StringUtils.isEmpty(tsfGroupName)) {
+ if (StringUtils.isEmpty(tsfNamespaceName) || StringUtils.isEmpty(tsfGroupName)) {
return;
}
String namespace = polarisContextProperties.getNamespace();
- List tsfConfigGroups = Arrays.asList(
- tsfId + "." + tsfGroupName + ".application_config_group",
- tsfId + "." + tsfNamespaceName + ".global_config_group");
+ List tsfConfigGroups = new ArrayList<>();
+ tsfConfigGroups.add((StringUtils.hasText(tsfId) ? tsfId + "." : "") + tsfGroupName + ".application_config_group");
+ tsfConfigGroups.add((StringUtils.hasText(tsfId) ? tsfId + "." : "") + tsfNamespaceName + ".global_config_group");
+
+ if (ClassUtils.isClassPresent("org.springframework.cloud.gateway.filter.GlobalFilter")) {
+ tsfConfigGroups.add((StringUtils.hasText(tsfId) ? tsfId + "." : "") + tsfGroupName + ".gateway_config_group");
+ }
for (String tsfConfigGroup : tsfConfigGroups) {
PolarisPropertySource polarisPropertySource = loadGroupPolarisPropertySource(configFileService, namespace, tsfConfigGroup);
if (polarisPropertySource == null) {
diff --git a/spring-cloud-starter-tencent-polaris-discovery/pom.xml b/spring-cloud-starter-tencent-polaris-discovery/pom.xml
index 35c87ef0de..0086854c9a 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/pom.xml
+++ b/spring-cloud-starter-tencent-polaris-discovery/pom.xml
@@ -58,6 +58,18 @@
+
+ org.springframework.cloud
+ spring-cloud-openfeign-core
+ true
+
+
+
+ io.github.openfeign
+ feign-core
+ true
+
+
org.springframework.boot
spring-boot-starter-web
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java
index ee023a25c4..e1c5ac361b 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java
@@ -17,6 +17,9 @@
package com.tencent.cloud.polaris.discovery;
+import com.tencent.cloud.common.constant.MetadataConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.api.core.ConsumerAPI;
@@ -49,7 +52,9 @@ public PolarisDiscoveryHandler(PolarisDiscoveryProperties polarisDiscoveryProper
* @return list of healthy instances
*/
public InstancesResponse getHealthyInstances(String service) {
- String namespace = polarisDiscoveryProperties.getNamespace();
+ String namespace = MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_APPLICATION_NONE,
+ MetadataConstant.POLARIS_TARGET_NAMESPACE, polarisDiscoveryProperties.getNamespace());
+
GetHealthyInstancesRequest getHealthyInstancesRequest = new GetHealthyInstancesRequest();
getHealthyInstancesRequest.setNamespace(namespace);
getHealthyInstancesRequest.setService(service);
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/config/PolarisEagerLoadAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/config/PolarisEagerLoadAutoConfiguration.java
index 179833b675..7edf653e79 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/config/PolarisEagerLoadAutoConfiguration.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/config/PolarisEagerLoadAutoConfiguration.java
@@ -29,7 +29,7 @@
import org.springframework.context.annotation.Configuration;
@Configuration
-@ConditionalOnProperty(name = "spring.cloud.polaris.discovery.eager-load.enabled", havingValue = "true")
+@ConditionalOnProperty(name = "spring.cloud.polaris.discovery.eager-load.enabled", havingValue = "true", matchIfMissing = true)
public class PolarisEagerLoadAutoConfiguration {
@Bean
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/instrument/feign/FeignEagerLoadSmartLifecycle.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/instrument/feign/FeignEagerLoadSmartLifecycle.java
index ca4e0e0a9e..4ede8529cf 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/instrument/feign/FeignEagerLoadSmartLifecycle.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/eager/instrument/feign/FeignEagerLoadSmartLifecycle.java
@@ -17,13 +17,17 @@
package com.tencent.cloud.polaris.eager.instrument.feign;
-import com.tencent.cloud.common.util.FeignUtil;
+import java.lang.reflect.Field;
+import java.lang.reflect.Proxy;
+
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient;
import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClient;
import com.tencent.polaris.api.utils.StringUtils;
+import feign.Target;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.SmartLifecycle;
@@ -47,36 +51,59 @@ public FeignEagerLoadSmartLifecycle(ApplicationContext applicationContext, Polar
@Override
public void start() {
LOG.info("feign eager-load start");
- for (String name : applicationContext.getBeanDefinitionNames()) {
+ for (Object bean : applicationContext.getBeansWithAnnotation(FeignClient.class).values()) {
try {
- if (name.contains(FeignUtil.FEIGN_CLIENT_SPECIF) && !name.startsWith(FeignUtil.FEIGN_CLIENT_DEFAULT)) {
- String feignName = FeignUtil.analysisFeignName(name, applicationContext);
- if (StringUtils.isNotBlank(feignName)) {
- LOG.info("[{}] eager-load start", feignName);
- if (polarisDiscoveryClient != null) {
- polarisDiscoveryClient.getInstances(feignName);
- }
- else if (polarisReactiveDiscoveryClient != null) {
- polarisReactiveDiscoveryClient.getInstances(feignName).subscribe();
- }
- else {
- LOG.warn("[{}] no discovery client found.", feignName);
+ if (Proxy.isProxyClass(bean.getClass())) {
+ Target.HardCodedTarget> hardCodedTarget = getHardCodedTarget(bean);
+ if (hardCodedTarget != null) {
+ FeignClient feignClient = hardCodedTarget.type().getAnnotation(FeignClient.class);
+ // if feignClient contains url, it doesn't need to eager load.
+ if (StringUtils.isEmpty(feignClient.url())) {
+ // support variables and default values.
+ String feignName = hardCodedTarget.name();
+ LOG.info("[{}] eager-load start", feignName);
+ if (polarisDiscoveryClient != null) {
+ polarisDiscoveryClient.getInstances(feignName);
+ }
+ else if (polarisReactiveDiscoveryClient != null) {
+ polarisReactiveDiscoveryClient.getInstances(feignName).subscribe();
+ }
+ else {
+ LOG.warn("[{}] no discovery client found.", feignName);
+ }
+ LOG.info("[{}] eager-load end", feignName);
}
- LOG.info("[{}] eager-load end", feignName);
- }
- else {
- LOG.warn("feign name is blank.");
}
}
}
catch (Exception e) {
- LOG.error("[{}] eager-load failed.", name, e);
+ LOG.debug("[{}] eager-load failed.", bean, e);
}
}
LOG.info("feign eager-load end");
}
+ public static Target.HardCodedTarget> getHardCodedTarget(Object proxy) {
+ try {
+ Object invocationHandler = Proxy.getInvocationHandler(proxy);
+
+ for (Field field : invocationHandler.getClass().getDeclaredFields()) {
+ field.setAccessible(true);
+ Object fieldValue = field.get(invocationHandler);
+ if (fieldValue instanceof Target.HardCodedTarget) {
+ return (Target.HardCodedTarget>) fieldValue;
+ }
+ }
+ }
+ catch (Exception e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("proxy:{}, getTarget failed.", proxy, e);
+ }
+ }
+ return null;
+ }
+
@Override
public void stop() {
diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/tsf/TsfServletRegistrationCustomizer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/tsf/TsfServletRegistrationCustomizer.java
index 08ca276ee5..7296e52c50 100644
--- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/tsf/TsfServletRegistrationCustomizer.java
+++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/tsf/TsfServletRegistrationCustomizer.java
@@ -29,7 +29,6 @@
import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer;
import com.tencent.polaris.plugins.connector.common.constant.ConsulConstant;
-
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.StringUtils;
diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterUtils.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterUtils.java
index cb295da8c5..0e5f2b5b04 100644
--- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterUtils.java
+++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterUtils.java
@@ -25,7 +25,9 @@
import java.util.function.Function;
import java.util.stream.Collectors;
+import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.rpc.enhancement.transformer.InstanceTransformer;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;
@@ -72,7 +74,10 @@ public static ServiceInstances transferServersToServiceInstances(Flux getMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean caller) {
MetadataContainer metadataContainer = getMetadataContainer(metadataType, caller);
Map values = new HashMap<>();
@@ -266,6 +274,8 @@ public Map getFragmentContext(String fragment) {
return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true);
case FRAGMENT_APPLICATION:
return getMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, false);
+ case FRAGMENT_APPLICATION_NONE:
+ return getMetadataAsMap(MetadataType.APPLICATION, TransitiveType.NONE, false);
case FRAGMENT_UPSTREAM_APPLICATION:
return getMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, true);
case FRAGMENT_RAW_TRANSHEADERS:
@@ -277,6 +287,10 @@ public Map getFragmentContext(String fragment) {
}
}
+ public String getContext(String fragment, String key, String defaultValue) {
+ return getFragmentContext(fragment).getOrDefault(key, defaultValue);
+ }
+
public String getContext(String fragment, String key) {
Map fragmentContext = getFragmentContext(fragment);
if (fragmentContext == null) {
@@ -305,6 +319,9 @@ public void putFragmentContext(String fragment, Map context) {
case FRAGMENT_APPLICATION:
putMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, false, context);
break;
+ case FRAGMENT_APPLICATION_NONE:
+ putMetadataAsMap(MetadataType.APPLICATION, TransitiveType.NONE, false, context);
+ break;
case FRAGMENT_UPSTREAM_APPLICATION:
putMetadataAsMap(MetadataType.APPLICATION, TransitiveType.DISPOSABLE, true, context);
break;
@@ -320,7 +337,9 @@ public void putFragmentContext(String fragment, Map context) {
}
}
- public static void setLocalService(String service) {
- LOCAL_SERVICE = service;
+ public void putFragmentContext(String fragment, String key, String value) {
+ Map context = new HashMap<>(1);
+ context.put(key, value);
+ putFragmentContext(fragment, context);
}
}
diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/FeignUtil.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/FeignUtil.java
deleted file mode 100644
index 4306e087ef..0000000000
--- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/FeignUtil.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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 org.springframework.context.ApplicationContext;
-import org.springframework.util.StringUtils;
-
-/**
- * @author heihuliliu
- */
-public final class FeignUtil {
-
- /**
- * Feign client spec.
- */
- public static final String FEIGN_CLIENT_SPECIF = ".FeignClientSpecification";
-
- /**
- * Default Feign client spec.
- */
- public static final String FEIGN_CLIENT_DEFAULT = "default.";
-
- /**
- * regular expression that parses ${xxx} .
- */
- public static final String REGEX = "^[$][{](.*)[}]$";
-
- /**
- * replacement of ${xxx}.
- */
- public static final String REPLACEMENT = "$1";
-
- private FeignUtil() {
-
- }
-
- /**
- * TODO If @FeignClient specifies contextId, the service name will not be obtained correctly, but the contextId will be obtained.
- *
- * @param name feign name.
- * @param context application context.
- * @return service name.
- */
- public static String analysisFeignName(String name, ApplicationContext context) {
- String feignName = "";
- String feignPath = name.substring(0, name.indexOf(FEIGN_CLIENT_SPECIF));
- // Handle the case where the service name is a variable
- if (feignPath.matches(REGEX)) {
- feignPath = context.getEnvironment().getProperty(feignPath.replaceAll(REGEX, REPLACEMENT));
- }
- if (StringUtils.hasText(feignPath)) {
- // The case of multi-level paths
- String[] feignNames = feignPath.split("/");
- if (feignNames.length > 1) {
- for (int i = 0; i < feignNames.length; i++) {
- if (StringUtils.hasText(feignNames[i])) {
- feignName = feignNames[i];
- break;
- }
- }
- }
- else {
- feignName = feignNames[0];
- }
- }
- return feignName;
- }
-}
diff --git a/spring-cloud-tencent-coverage/pom.xml b/spring-cloud-tencent-coverage/pom.xml
index f3fa076f1f..7ec3c91a40 100644
--- a/spring-cloud-tencent-coverage/pom.xml
+++ b/spring-cloud-tencent-coverage/pom.xml
@@ -86,7 +86,7 @@
com.tencent.cloud
- spring-cloud-tencent-gateway-plugin
+ spring-cloud-starter-tencent-gateway-plugin
diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml
index 15da36e3cb..62284f878a 100644
--- a/spring-cloud-tencent-dependencies/pom.xml
+++ b/spring-cloud-tencent-dependencies/pom.xml
@@ -194,7 +194,7 @@
com.tencent.cloud
- spring-cloud-tencent-gateway-plugin
+ spring-cloud-starter-tencent-gateway-plugin
${revision}
diff --git a/spring-cloud-tencent-examples/polaris-router-featureenv-example/featureenv-gateway/pom.xml b/spring-cloud-tencent-examples/polaris-router-featureenv-example/featureenv-gateway/pom.xml
index d8752b721d..72578faf82 100644
--- a/spring-cloud-tencent-examples/polaris-router-featureenv-example/featureenv-gateway/pom.xml
+++ b/spring-cloud-tencent-examples/polaris-router-featureenv-example/featureenv-gateway/pom.xml
@@ -20,7 +20,7 @@
com.tencent.cloud
- spring-cloud-tencent-gateway-plugin
+ spring-cloud-starter-tencent-gateway-plugin
com.tencent.cloud
diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/pom.xml b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/pom.xml
index 9bfb76041a..4428cc570d 100644
--- a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/pom.xml
+++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/pom.xml
@@ -21,7 +21,7 @@
com.tencent.cloud
- spring-cloud-tencent-gateway-plugin
+ spring-cloud-starter-tencent-gateway-plugin
diff --git a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml
index d40fd6bf2c..d45c52e7bd 100644
--- a/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml
+++ b/spring-cloud-tencent-examples/quickstart-example/quickstart-gateway-service/src/main/resources/application.yml
@@ -43,13 +43,6 @@ spring:
args:
regexp: '''/'' + serviceId + ''/(?.*)'''
replacement: '''/$\{remaining}'''
- 'filters[1]':
- name: CircuitBreaker
- args:
- # statusCodes 缺省时会自动识别 "5**" 为错误
- # statusCodes: '''404,5**'''
- # fallbackUri 缺省时会在熔断触发后拉取 plaris server 配置的降级作为 response
- fallbackUri: '''forward:/polaris-fallback'''
routes:
- id: QuickstartCallerService
uri: lb://QuickstartCallerService
diff --git a/spring-cloud-tencent-plugin-starters/pom.xml b/spring-cloud-tencent-plugin-starters/pom.xml
index 4159c7843e..67ad734d84 100644
--- a/spring-cloud-tencent-plugin-starters/pom.xml
+++ b/spring-cloud-tencent-plugin-starters/pom.xml
@@ -16,7 +16,7 @@
spring-cloud-tencent-featureenv-plugin
- spring-cloud-tencent-gateway-plugin
+ spring-cloud-starter-tencent-gateway-plugin
spring-cloud-starter-tencent-discovery-adapter-plugin
spring-cloud-tencent-lossless-plugin
spring-cloud-starter-tencent-threadlocal-plugin
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/pom.xml
similarity index 80%
rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/pom.xml
rename to spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/pom.xml
index 264dc78b55..49536e26c0 100644
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/pom.xml
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/pom.xml
@@ -10,8 +10,8 @@
4.0.0
- spring-cloud-tencent-gateway-plugin
- Spring Cloud Tencent Gateway Plugin
+ spring-cloud-starter-tencent-gateway-plugin
+ Spring Cloud Starter Tencent Gateway Plugin
@@ -19,6 +19,12 @@
spring-cloud-starter-tencent-polaris-config
+
+ com.tencent.cloud
+ spring-cloud-starter-tencent-polaris-discovery
+ true
+
+
org.springframework.cloud
spring-cloud-gateway-server
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayPluginAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayPluginAutoConfiguration.java
new file mode 100644
index 0000000000..98a7a3636b
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayPluginAutoConfiguration.java
@@ -0,0 +1,109 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway;
+
+import com.tencent.cloud.plugin.gateway.context.ContextGatewayFilterFactory;
+import com.tencent.cloud.plugin.gateway.context.ContextGatewayProperties;
+import com.tencent.cloud.plugin.gateway.context.ContextGatewayPropertiesManager;
+import com.tencent.cloud.plugin.gateway.context.ContextPropertiesRouteDefinitionLocator;
+import com.tencent.cloud.plugin.gateway.context.ContextRoutePredicateFactory;
+import com.tencent.cloud.plugin.gateway.context.GatewayConfigChangeListener;
+import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled;
+import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
+import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient;
+import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClient;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.env.Environment;
+
+/**
+ * Auto configuration for spring cloud gateway plugins.
+ * @author lepdou 2022-07-06
+ */
+@Configuration
+@ConditionalOnPolarisEnabled
+@ConditionalOnProperty(value = "spring.cloud.tencent.plugin.scg.enabled", matchIfMissing = true)
+public class GatewayPluginAutoConfiguration {
+
+ @Configuration
+ @ConditionalOnProperty(value = "spring.cloud.tencent.plugin.scg.context.enabled", matchIfMissing = true)
+ @ConditionalOnPolarisConfigEnabled
+ @ConditionalOnClass(GlobalFilter.class)
+ @Import(ContextGatewayProperties.class)
+ public static class ContextPluginConfiguration {
+
+ @Value("${spring.cloud.polaris.discovery.eager-load.enabled:#{'true'}}")
+ private boolean commonEagerLoadEnabled;
+
+ @Value("${spring.cloud.polaris.discovery.eager-load.gateway.enabled:#{'true'}}")
+ private boolean gatewayEagerLoadEnabled;
+
+ @Bean
+ public ContextGatewayFilterFactory contextGatewayFilterFactory(ContextGatewayPropertiesManager contextGatewayPropertiesManager) {
+ return new ContextGatewayFilterFactory(contextGatewayPropertiesManager);
+ }
+
+ @Bean
+ public ContextPropertiesRouteDefinitionLocator contextPropertiesRouteDefinitionLocator(ContextGatewayProperties properties) {
+ return new ContextPropertiesRouteDefinitionLocator(properties);
+ }
+
+ @Bean
+ public ContextRoutePredicateFactory contextServiceRoutePredicateFactory() {
+ return new ContextRoutePredicateFactory();
+ }
+
+ @Bean
+ public ContextGatewayPropertiesManager contextGatewayPropertiesManager(ContextGatewayProperties properties,
+ @Autowired(required = false) PolarisDiscoveryClient polarisDiscoveryClient,
+ @Autowired(required = false) PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient) {
+ ContextGatewayPropertiesManager contextGatewayPropertiesManager = new ContextGatewayPropertiesManager();
+ contextGatewayPropertiesManager.setGroupRouteMap(properties.getGroups());
+ if (commonEagerLoadEnabled && gatewayEagerLoadEnabled) {
+ contextGatewayPropertiesManager.eagerLoad(polarisDiscoveryClient, polarisReactiveDiscoveryClient);
+ }
+ return contextGatewayPropertiesManager;
+ }
+
+ @Bean
+ public GatewayRegistrationCustomizer gatewayRegistrationCustomizer() {
+ return new GatewayRegistrationCustomizer();
+ }
+
+ @Bean
+ public GatewayConfigChangeListener gatewayConfigChangeListener(ContextGatewayPropertiesManager manager,
+ ApplicationEventPublisher publisher, Environment environment) {
+ return new GatewayConfigChangeListener(manager, publisher, environment);
+ }
+
+ @Bean
+ public PolarisReactiveLoadBalancerClientFilterBeanPostProcessor polarisReactiveLoadBalancerClientFilterBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new PolarisReactiveLoadBalancerClientFilterBeanPostProcessor(applicationContext);
+ }
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayRegistrationCustomizer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayRegistrationCustomizer.java
new file mode 100644
index 0000000000..9888991272
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/GatewayRegistrationCustomizer.java
@@ -0,0 +1,28 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway;
+
+import com.tencent.cloud.polaris.registry.PolarisRegistration;
+import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer;
+
+public class GatewayRegistrationCustomizer implements PolarisRegistrationCustomizer {
+ @Override
+ public void customize(PolarisRegistration registration) {
+ registration.getMetadata().put("internal-service-type", "spring-cloud-gateway");
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilter.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilter.java
new file mode 100644
index 0000000000..538f0e4594
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilter.java
@@ -0,0 +1,56 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway;
+
+import com.tencent.cloud.common.constant.MetadataConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.web.server.ServerWebExchange;
+
+public class PolarisReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
+
+ private final ReactiveLoadBalancerClientFilter clientFilter;
+
+ public PolarisReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory,
+ GatewayLoadBalancerProperties properties, ReactiveLoadBalancerClientFilter clientFilter) {
+ super(clientFactory, properties);
+ this.clientFilter = clientFilter;
+ }
+
+ @Override
+ public int getOrder() {
+ return clientFilter.getOrder();
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ // restore context from exchange
+ MetadataContext metadataContext = (MetadataContext) exchange.getAttributes().get(
+ MetadataConstant.HeaderName.METADATA_CONTEXT);
+ if (metadataContext != null) {
+ MetadataContextHolder.set(metadataContext);
+ }
+ return clientFilter.filter(exchange, chain);
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilterBeanPostProcessor.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilterBeanPostProcessor.java
new file mode 100644
index 0000000000..3055984f2a
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/PolarisReactiveLoadBalancerClientFilterBeanPostProcessor.java
@@ -0,0 +1,51 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
+import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
+import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.Ordered;
+
+public class PolarisReactiveLoadBalancerClientFilterBeanPostProcessor implements BeanPostProcessor, Ordered {
+
+ private ApplicationContext applicationContext;
+
+ public PolarisReactiveLoadBalancerClientFilterBeanPostProcessor(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof ReactiveLoadBalancerClientFilter && !(bean instanceof PolarisReactiveLoadBalancerClientFilter)) {
+ LoadBalancerClientFactory clientFactory = applicationContext.getBean(LoadBalancerClientFactory.class);
+ GatewayLoadBalancerProperties properties = applicationContext.getBean(GatewayLoadBalancerProperties.class);
+
+ return new PolarisReactiveLoadBalancerClientFilter(clientFactory, properties, (ReactiveLoadBalancerClientFilter) bean);
+ }
+ return bean;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ApiType.java
similarity index 58%
rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainer.java
rename to spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ApiType.java
index d927f362c6..4679e804c7 100644
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainer.java
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ApiType.java
@@ -15,23 +15,15 @@
* specific language governing permissions and limitations under the License.
*/
-package com.tencent.cloud.plugin.gateway.staining;
-
-import java.util.Map;
-
-import org.springframework.core.Ordered;
-import org.springframework.web.server.ServerWebExchange;
-
-/**
- * Staining according to request parameters. for example, when the request parameter uid=0, staining env=blue.
- * @author lepdou 2022-07-06
- */
-public interface TrafficStainer extends Ordered {
+package com.tencent.cloud.plugin.gateway.context;
+public enum ApiType {
+ /**
+ * Ms api type.
+ */
+ MS,
/**
- * get stained labels from request.
- * @param exchange the request.
- * @return stained labels.
+ * External api type.
*/
- Map apply(ServerWebExchange exchange);
+ EXTERNAL,
}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilter.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilter.java
new file mode 100644
index 0000000000..261e45c049
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilter.java
@@ -0,0 +1,199 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.lang.reflect.Constructor;
+import java.net.URI;
+import java.util.HashMap;
+
+import com.tencent.cloud.common.constant.MetadataConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.polaris.api.utils.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import reactor.core.publisher.Mono;
+
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
+import org.springframework.cloud.gateway.route.Route;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
+
+public class ContextGatewayFilter implements GatewayFilter, Ordered {
+
+ private static final Logger logger = LoggerFactory.getLogger(ContextGatewayFilter.class);
+
+ private ContextGatewayPropertiesManager manager;
+
+ private ContextGatewayFilterFactory.Config config;
+
+ public ContextGatewayFilter(ContextGatewayPropertiesManager manager, ContextGatewayFilterFactory.Config config) {
+ this.manager = manager;
+ this.config = config;
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ GroupContext groupContext = manager.getGroups().get(config.getGroup());
+
+ if (ApiType.MS.equals(groupContext.getPredicate().getApiType())) {
+ return msFilter(exchange, chain, groupContext);
+ }
+ else {
+ return externalFilter(exchange, chain, groupContext);
+ }
+ }
+
+ private Mono externalFilter(ServerWebExchange exchange, GatewayFilterChain chain, GroupContext groupContext) {
+ ServerHttpRequest request = exchange.getRequest();
+ String[] apis = rebuildExternalApi(request, request.getPath().value());
+ GroupContext.ContextRoute contextRoute = manager.getGroupPathRoute(config.getGroup(), apis[0]);
+ if (contextRoute == null) {
+ throw new RuntimeException(String.format("Can't find context route for group: %s, path: %s, origin path: %s", config.getGroup(), apis[0], request.getPath()));
+ }
+ updateRouteMetadata(exchange, contextRoute);
+
+ URI requestUri = URI.create(contextRoute.getHost() + apis[1]);
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri);
+ // 调整为正确路径
+ ServerHttpRequest newRequest = request.mutate().path(apis[1]).build();
+ return chain.filter(exchange.mutate().request(newRequest).build());
+ }
+
+ private Mono msFilter(ServerWebExchange exchange, GatewayFilterChain chain, GroupContext groupContext) {
+ ServerHttpRequest request = exchange.getRequest();
+ String[] apis = rebuildMsApi(request, groupContext, request.getPath().value());
+ // 判断 api 是否匹配
+ GroupContext.ContextRoute contextRoute = manager.getGroupPathRoute(config.getGroup(), apis[0]);
+ if (contextRoute == null) {
+ throw new RuntimeException(String.format("Can't find context route for group: %s, path: %s, origin path: %s", config.getGroup(), apis[0], request.getPath()));
+ }
+ updateRouteMetadata(exchange, contextRoute);
+
+ MetadataContext metadataContext = (MetadataContext) exchange.getAttributes().get(
+ MetadataConstant.HeaderName.METADATA_CONTEXT);
+
+ metadataContext.putFragmentContext(MetadataContext.FRAGMENT_APPLICATION_NONE,
+ MetadataConstant.POLARIS_TARGET_NAMESPACE, contextRoute.getNamespace());
+
+ URI requestUri = URI.create("lb://" + contextRoute.getService() + apis[1]);
+ exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUri);
+ // 调整为正确路径
+ ServerHttpRequest newRequest = request.mutate().path(apis[1]).build();
+ return chain.filter(exchange.mutate().request(newRequest).build());
+ }
+
+ /**
+ * e.g. "/context/api/test" → [ "GET|/api/test", "/api/test"]
+ */
+ private String[] rebuildExternalApi(ServerHttpRequest request, String path) {
+ String[] pathSegments = path.split("/");
+ StringBuilder matchPath = new StringBuilder();
+ StringBuilder realPath = new StringBuilder();
+ int index = 2;
+ matchPath.append(request.getMethodValue()).append("|");
+ for (int i = index; i < pathSegments.length; i++) {
+ matchPath.append("/").append(pathSegments[i]);
+ realPath.append("/").append(pathSegments[i]);
+ }
+ if (path.endsWith("/")) {
+ matchPath.append("/");
+ realPath.append("/");
+ }
+ return new String[] {matchPath.toString(), realPath.toString()};
+ }
+
+ /**
+ * returns an array of two strings, the first is the match path, the second is the real path.
+ * e.g. "/context/namespace/svc/api/test" → [ "GET|/namespace/svc/api/test", "/api/test"]
+ */
+ private String[] rebuildMsApi(ServerHttpRequest request, GroupContext groupContext, String path) {
+ String[] pathSegments = path.split("/");
+ StringBuilder matchPath = new StringBuilder();
+ int index = 2;
+ matchPath.append(request.getMethodValue()).append("|");
+
+ Position namespacePosition = groupContext.getPredicate().getNamespace().getPosition();
+ switch (namespacePosition) {
+ case QUERY:
+ matchPath.append("/").append(request.getQueryParams().getFirst(groupContext.getPredicate().getNamespace().getKey()));
+ break;
+ case HEADER:
+ matchPath.append("/").append(request.getHeaders().getFirst(groupContext.getPredicate().getNamespace().getKey()));
+ break;
+ case PATH:
+ default:
+ matchPath.append("/").append(pathSegments[index++]);
+ break;
+ }
+ Position servicePosition = groupContext.getPredicate().getService().getPosition();
+ switch (servicePosition) {
+ case QUERY:
+ matchPath.append("/").append(request.getQueryParams().getFirst(groupContext.getPredicate().getService().getKey()));
+ break;
+ case HEADER:
+ matchPath.append("/").append(request.getHeaders().getFirst(groupContext.getPredicate().getService().getKey()));
+ break;
+ case PATH:
+ default:
+ matchPath.append("/").append(pathSegments[index++]);
+ }
+ StringBuilder realPath = new StringBuilder();
+ for (int i = index; i < pathSegments.length; i++) {
+ matchPath.append("/").append(pathSegments[i]);
+ realPath.append("/").append(pathSegments[i]);
+ }
+ if (path.endsWith("/")) {
+ matchPath.append("/");
+ realPath.append("/");
+ }
+
+ return new String[] {matchPath.toString(), realPath.toString()};
+ }
+
+ private void updateRouteMetadata(ServerWebExchange exchange, GroupContext.ContextRoute contextRoute) {
+ if (CollectionUtils.isEmpty(contextRoute.getMetadata())) {
+ return;
+ }
+
+ Route route = (Route) exchange.getAttributes().get(GATEWAY_ROUTE_ATTR);
+ Constructor constructor = Route.class.getDeclaredConstructors()[1];
+ constructor.setAccessible(true);
+ try {
+ HashMap metadata = new HashMap<>(route.getMetadata());
+ metadata.putAll(contextRoute.getMetadata());
+ Route newRoute = (Route) constructor.newInstance(route.getId(), route.getUri(),
+ route.getOrder(), route.getPredicate(), route.getFilters(), metadata);
+ exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, newRoute);
+ }
+ catch (Exception e) {
+ logger.debug("[updateRouteMetadata] update route metadata failed", e);
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ // after RouteToRequestUrlFilter, DecodeTransferMetadataReactiveFilter
+ return RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 12;
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilterFactory.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilterFactory.java
new file mode 100644
index 0000000000..2293bee6fc
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayFilterFactory.java
@@ -0,0 +1,58 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
+
+public class ContextGatewayFilterFactory extends AbstractGatewayFilterFactory {
+
+ private ContextGatewayPropertiesManager manager;
+
+ public ContextGatewayFilterFactory(ContextGatewayPropertiesManager manager) {
+ super(Config.class);
+ this.manager = manager;
+ }
+
+ @Override
+ public List shortcutFieldOrder() {
+ return Arrays.asList("group");
+ }
+
+ @Override
+ public GatewayFilter apply(Config config) {
+ return new ContextGatewayFilter(manager, config);
+ }
+
+ public static class Config {
+ private String group;
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+ }
+
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayProperties.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayProperties.java
new file mode 100644
index 0000000000..f4d07541a5
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayProperties.java
@@ -0,0 +1,70 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.core.style.ToStringCreator;
+
+@ConfigurationProperties(ContextGatewayProperties.PREFIX)
+public class ContextGatewayProperties {
+
+ /**
+ * Properties prefix.
+ */
+ public static final String PREFIX = "spring.cloud.tencent.gateway";
+
+ private final Log logger = LogFactory.getLog(getClass());
+
+ private Map routes = new HashMap<>();
+
+ private Map groups = new HashMap<>();
+
+ public Map getRoutes() {
+ return routes;
+ }
+
+ public void setRoutes(Map routes) {
+ this.routes = routes;
+ if (routes != null && routes.size() > 0 && logger.isDebugEnabled()) {
+ logger.debug("Routes supplied from Gateway Properties: " + routes);
+ }
+ }
+
+ public Map getGroups() {
+ return groups;
+ }
+
+ public void setGroups(Map groups) {
+ this.groups = groups;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringCreator(this).append("routes", routes)
+ .append("groups", groups).toString();
+
+ }
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayPropertiesManager.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayPropertiesManager.java
new file mode 100644
index 0000000000..ae491587e3
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextGatewayPropertiesManager.java
@@ -0,0 +1,150 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.tencent.cloud.common.constant.MetadataConstant;
+import com.tencent.cloud.common.metadata.MetadataContext;
+import com.tencent.cloud.common.metadata.MetadataContextHolder;
+import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient;
+import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClient;
+import com.tencent.polaris.api.utils.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.util.AntPathMatcher;
+
+public class ContextGatewayPropertiesManager {
+
+ private static final Logger logger = LoggerFactory.getLogger(ContextGatewayPropertiesManager.class);
+ /**
+ * context -> {path key -> route}.
+ */
+ private volatile ConcurrentHashMap> groupPathRouteMap = new ConcurrentHashMap<>();
+ /**
+ * context -> {wildcard path key -> route}.
+ */
+ private volatile ConcurrentHashMap> groupWildcardPathRouteMap = new ConcurrentHashMap<>();
+
+ private Map groups = new HashMap<>();
+
+ private AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+ public Map> getGroupPathRouteMap() {
+ return groupPathRouteMap;
+ }
+
+ public void setGroupRouteMap(Map groups) {
+
+ ConcurrentHashMap> newGroupPathRouteMap = new ConcurrentHashMap<>();
+ ConcurrentHashMap> newGroupWildcardPathRouteMap = new ConcurrentHashMap<>();
+ if (groups != null) {
+ for (Map.Entry entry : groups.entrySet()) {
+ Map newGroupPathRoute = new HashMap<>();
+ Map newGroupWildcardPathRoute = new HashMap<>();
+ for (GroupContext.ContextRoute route : entry.getValue().getRoutes()) {
+ String path = route.getPath();
+ // convert path parameter to group wildcard path
+ if (path.contains("{") && path.contains("}") || path.contains("*")) {
+ newGroupWildcardPathRoute.put(buildPathKey(entry.getValue(), route), route);
+ }
+ else {
+ newGroupPathRoute.put(buildPathKey(entry.getValue(), route), route);
+ }
+ }
+ newGroupWildcardPathRouteMap.put(entry.getKey(), newGroupWildcardPathRoute);
+ newGroupPathRouteMap.put(entry.getKey(), newGroupPathRoute);
+ }
+ }
+ this.groupPathRouteMap = newGroupPathRouteMap;
+ this.groupWildcardPathRouteMap = newGroupWildcardPathRouteMap;
+ this.groups = groups;
+ }
+
+ public Map getGroups() {
+ return groups;
+ }
+
+ public GroupContext.ContextRoute getGroupPathRoute(String group, String path) {
+ Map groupPathRouteMap = this.groupPathRouteMap.get(group);
+ if (groupPathRouteMap != null && groupPathRouteMap.containsKey(path)) {
+ return groupPathRouteMap.get(path);
+ }
+
+ Map groupWildcardPathRouteMap = this.groupWildcardPathRouteMap.get(group);
+ if (groupWildcardPathRouteMap != null) {
+ for (Map.Entry entry : groupWildcardPathRouteMap.entrySet()) {
+ boolean matched = antPathMatcher.match(entry.getKey(), path);
+ if (matched) {
+ return entry.getValue();
+ }
+ }
+ }
+ return null;
+ }
+
+ public void eagerLoad(PolarisDiscoveryClient polarisDiscoveryClient,
+ PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient) {
+ for (Map contextRouteMap : groupPathRouteMap.values()) {
+ for (GroupContext.ContextRoute contextRoute : contextRouteMap.values()) {
+ eagerLoadFromRoute(contextRoute, polarisDiscoveryClient, polarisReactiveDiscoveryClient);
+ }
+ }
+ for (Map contextRouteMap : groupWildcardPathRouteMap.values()) {
+ for (GroupContext.ContextRoute contextRoute : contextRouteMap.values()) {
+ eagerLoadFromRoute(contextRoute, polarisDiscoveryClient, polarisReactiveDiscoveryClient);
+ }
+ }
+ }
+
+ private void eagerLoadFromRoute(GroupContext.ContextRoute contextRoute, PolarisDiscoveryClient polarisDiscoveryClient,
+ PolarisReactiveDiscoveryClient polarisReactiveDiscoveryClient) {
+ String namespace = contextRoute.getNamespace();
+ String service = contextRoute.getService();
+ if (StringUtils.isNotEmpty(namespace) && StringUtils.isNotEmpty(service)) {
+ logger.info("[{},{}] eager-load start", namespace, service);
+ MetadataContextHolder.get().putFragmentContext(MetadataContext.FRAGMENT_APPLICATION_NONE,
+ MetadataConstant.POLARIS_TARGET_NAMESPACE, namespace);
+
+ if (polarisDiscoveryClient != null) {
+ polarisDiscoveryClient.getInstances(service);
+ }
+ else if (polarisReactiveDiscoveryClient != null) {
+ polarisReactiveDiscoveryClient.getInstances(service).subscribe();
+ }
+ else {
+ logger.warn("[{}] no discovery client found.", service);
+ }
+ logger.info("[{},{}] eager-load end", namespace, service);
+ }
+ }
+
+ private String buildPathKey(GroupContext groupContext, GroupContext.ContextRoute route) {
+ switch (groupContext.getPredicate().getApiType()) {
+ case MS:
+ return String.format("%s|/%s/%s%s", route.getMethod(), route.getNamespace(), route.getService(), route.getPath());
+ case EXTERNAL:
+ default:
+ return String.format("%s|%s", route.getMethod(), route.getPath());
+ }
+ }
+
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextPropertiesRouteDefinitionLocator.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextPropertiesRouteDefinitionLocator.java
new file mode 100644
index 0000000000..366b48b3af
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextPropertiesRouteDefinitionLocator.java
@@ -0,0 +1,37 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import reactor.core.publisher.Flux;
+
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
+
+public class ContextPropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
+
+ private final ContextGatewayProperties properties;
+
+ public ContextPropertiesRouteDefinitionLocator(ContextGatewayProperties properties) {
+ this.properties = properties;
+ }
+
+ @Override
+ public Flux getRouteDefinitions() {
+ return Flux.fromIterable(this.properties.getRoutes().values());
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextRoutePredicateFactory.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextRoutePredicateFactory.java
new file mode 100644
index 0000000000..cce94d665c
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/ContextRoutePredicateFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
+import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
+import org.springframework.web.server.ServerWebExchange;
+
+public class ContextRoutePredicateFactory extends AbstractRoutePredicateFactory {
+
+ public ContextRoutePredicateFactory() {
+ super(Config.class);
+ }
+
+ @Override
+ public Predicate apply(Config config) {
+ return new GatewayPredicate() {
+ @Override
+ public boolean test(ServerWebExchange exchange) {
+ // TODO: do path-rewriting , put to GATEWAY_PREDICATE_PATH_CONTAINER_ATTR
+ return true;
+ }
+
+ @Override
+ public Object getConfig() {
+ return config;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Config: %s", config);
+ }
+ };
+ }
+
+ @Override
+ public List shortcutFieldOrder() {
+ return Arrays.asList("group");
+ }
+
+ public static class Config {
+ private String group;
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ @Override
+ public String toString() {
+ return "Config{" +
+ "group='" + group + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GatewayConfigChangeListener.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GatewayConfigChangeListener.java
new file mode 100644
index 0000000000..50b299856c
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GatewayConfigChangeListener.java
@@ -0,0 +1,57 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener;
+import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent;
+
+import org.springframework.boot.context.properties.bind.BindResult;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.cloud.gateway.config.GatewayProperties;
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.core.env.Environment;
+
+public class GatewayConfigChangeListener {
+
+ private ApplicationEventPublisher publisher;
+
+ private ContextGatewayPropertiesManager manager;
+
+ private Environment environment;
+
+ public GatewayConfigChangeListener(ContextGatewayPropertiesManager manager,
+ ApplicationEventPublisher publisher, Environment environment) {
+ this.manager = manager;
+ this.publisher = publisher;
+ this.environment = environment;
+ }
+
+ @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = ContextGatewayProperties.PREFIX)
+ public void onChangeTencentGatewayProperties(ConfigChangeEvent event) {
+ Binder binder = Binder.get(environment);
+ BindResult result = binder.bind(ContextGatewayProperties.PREFIX, ContextGatewayProperties.class);
+ manager.setGroupRouteMap(result.get().getGroups());
+ this.publisher.publishEvent(new RefreshRoutesEvent(event));
+ }
+
+ @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = GatewayProperties.PREFIX)
+ public void onChangeGatewayConfigChangeListener(ConfigChangeEvent event) {
+ this.publisher.publishEvent(new RefreshRoutesEvent(event));
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GroupContext.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GroupContext.java
new file mode 100644
index 0000000000..6394cffc49
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/GroupContext.java
@@ -0,0 +1,223 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+import java.util.List;
+import java.util.Map;
+
+public class GroupContext {
+
+ private String comment;
+
+ private ApiType apiType;
+
+ private ContextPredicate predicate;
+
+ private List routes;
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public ApiType getApiType() {
+ return apiType;
+ }
+
+ public void setApiType(ApiType apiType) {
+ this.apiType = apiType;
+ }
+
+ public ContextPredicate getPredicate() {
+ return predicate;
+ }
+
+ public void setPredicate(ContextPredicate predicate) {
+ this.predicate = predicate;
+ }
+
+ public List getRoutes() {
+ return routes;
+ }
+
+ public void setRoutes(List routes) {
+ this.routes = routes;
+ }
+
+ public static class ContextPredicate {
+ private ApiType apiType;
+
+ private String context;
+
+ private ContextNamespace namespace;
+
+ private ContextService service;
+
+ public ApiType getApiType() {
+ return apiType;
+ }
+
+ public void setApiType(ApiType apiType) {
+ this.apiType = apiType;
+ }
+
+ public ContextNamespace getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(ContextNamespace namespace) {
+ this.namespace = namespace;
+ }
+
+ public ContextService getService() {
+ return service;
+ }
+
+ public void setService(ContextService service) {
+ this.service = service;
+ }
+
+ public String getContext() {
+ return context;
+ }
+
+ public void setContext(String context) {
+ this.context = context;
+ }
+
+ }
+
+ public static class ContextNamespace {
+ private Position position;
+
+ private String key;
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+ }
+
+ public static class ContextService {
+ private Position position;
+
+ private String key;
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public void setPosition(Position position) {
+ this.position = position;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+ }
+
+ public static class ContextRoute {
+ private String path;
+
+ private String pathMapping;
+
+ private String method;
+
+ private String service;
+
+ private String host;
+
+ private String namespace;
+
+ private Map metadata;
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public String getPathMapping() {
+ return pathMapping;
+ }
+
+ public void setPathMapping(String pathMapping) {
+ this.pathMapping = pathMapping;
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public String getService() {
+ return service;
+ }
+
+ public void setService(String service) {
+ this.service = service;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ public Map getMetadata() {
+ return metadata;
+ }
+
+ public void setMetadata(Map metadata) {
+ this.metadata = metadata;
+ }
+ }
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/Position.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/Position.java
new file mode 100644
index 0000000000..7277ce0373
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/context/Position.java
@@ -0,0 +1,33 @@
+/*
+ * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
+ *
+ * Copyright (C) 2021 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.plugin.gateway.context;
+
+public enum Position {
+ /**
+ * Path position.
+ */
+ PATH,
+ /**
+ * Query position.
+ */
+ QUERY,
+ /**
+ * Header position.
+ */
+ HEADER,
+}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json
similarity index 100%
rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json
rename to spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000000..1714707dc7
--- /dev/null
+++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.tencent.cloud.plugin.gateway.GatewayPluginAutoConfiguration
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/resources/application-test.yml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/test/resources/application-test.yml
similarity index 100%
rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/resources/application-test.yml
rename to spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-gateway-plugin/src/test/resources/application-test.yml
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfiguration.java
deleted file mode 100644
index 324e0dc0f6..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfiguration.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway;
-
-import java.util.List;
-
-import com.tencent.cloud.plugin.gateway.staining.TrafficStainer;
-import com.tencent.cloud.plugin.gateway.staining.TrafficStainingGatewayFilter;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingExecutor;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingProperties;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleTrafficStainer;
-import com.tencent.cloud.plugin.gateway.staining.rule.StainingRuleManager;
-import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled;
-import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Auto configuration for spring cloud gateway plugins.
- * @author lepdou 2022-07-06
- */
-@Configuration
-@ConditionalOnPolarisEnabled
-@ConditionalOnProperty(value = "spring.cloud.tencent.plugin.scg.enabled", matchIfMissing = true)
-public class SCGPluginsAutoConfiguration {
-
- @Configuration
- @ConditionalOnProperty("spring.cloud.tencent.plugin.scg.staining.rule-staining.enabled")
- @ConditionalOnPolarisConfigEnabled
- public static class RuleStainingPluginConfiguration {
-
- @Bean
- public RuleStainingProperties ruleStainingProperties() {
- return new RuleStainingProperties();
- }
-
- @Bean
- public StainingRuleManager stainingRuleManager(RuleStainingProperties stainingProperties, ConfigFileService configFileService) {
- return new StainingRuleManager(stainingProperties, configFileService);
- }
-
- @Bean
- public TrafficStainingGatewayFilter trafficStainingGatewayFilter(List trafficStainer) {
- return new TrafficStainingGatewayFilter(trafficStainer);
- }
-
- @Bean
- public RuleStainingExecutor ruleStainingExecutor() {
- return new RuleStainingExecutor();
- }
-
- @Bean
- public RuleTrafficStainer ruleTrafficStainer(StainingRuleManager stainingRuleManager, RuleStainingExecutor ruleStainingExecutor) {
- return new RuleTrafficStainer(stainingRuleManager, ruleStainingExecutor);
- }
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilter.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilter.java
deleted file mode 100644
index 184d52faee..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilter.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining;
-
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.tencent.cloud.common.constant.MetadataConstant;
-import com.tencent.cloud.common.metadata.MetadataContext;
-import com.tencent.cloud.common.metadata.MetadataContextHolder;
-import com.tencent.cloud.common.util.JacksonUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import reactor.core.publisher.Mono;
-
-import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-import org.springframework.cloud.gateway.filter.GlobalFilter;
-import org.springframework.core.Ordered;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.util.CollectionUtils;
-import org.springframework.web.server.ServerWebExchange;
-
-import static org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER;
-
-/**
- * Staining the request, and the stained labels will be passed to the link through transitive metadata.
- * @author lepdou 2022-07-06
- */
-public class TrafficStainingGatewayFilter implements GlobalFilter, Ordered {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(TrafficStainingGatewayFilter.class);
-
- private final List trafficStainers;
-
- public TrafficStainingGatewayFilter(List trafficStainers) {
- if (!CollectionUtils.isEmpty(trafficStainers)) {
- trafficStainers.sort(Comparator.comparingInt(Ordered::getOrder));
- }
- this.trafficStainers = trafficStainers;
- }
-
- @Override
- public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- if (CollectionUtils.isEmpty(trafficStainers)) {
- return chain.filter(exchange);
- }
-
- // 1. get stained labels from request
- Map stainedLabels = getStainedLabels(exchange);
-
- if (CollectionUtils.isEmpty(stainedLabels)) {
- return chain.filter(exchange);
- }
-
- // 2. put stained labels to metadata context
- ServerHttpRequest request = exchange.getRequest().mutate().headers((httpHeaders) -> {
- MetadataContext metadataContext = exchange.getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT);
- if (metadataContext == null) {
- metadataContext = MetadataContextHolder.get();
- }
-
- Map oldTransitiveMetadata = metadataContext.getTransitiveMetadata();
-
- // append new transitive metadata
- Map newTransitiveMetadata = new HashMap<>(oldTransitiveMetadata);
- newTransitiveMetadata.putAll(stainedLabels);
-
- metadataContext.setTransitiveMetadata(newTransitiveMetadata);
- }).build();
-
- return chain.filter(exchange.mutate().request(request).build());
- }
-
- Map getStainedLabels(ServerWebExchange exchange) {
- Map stainedLabels = new HashMap<>();
- int size = trafficStainers.size();
- TrafficStainer stainer = null;
- for (int i = size - 1; i >= 0; i--) {
- try {
- stainer = trafficStainers.get(i);
- Map labels = stainer.apply(exchange);
- if (!CollectionUtils.isEmpty(labels)) {
- stainedLabels.putAll(labels);
- }
- }
- catch (Exception e) {
- if (stainer != null) {
- LOGGER.error("[SCT] traffic stained error. stainer = {}", stainer.getClass().getName(), e);
- }
- }
- }
- LOGGER.debug("[SCT] traffic stained labels. {}", JacksonUtils.serialize2Json(stainedLabels));
-
- return stainedLabels;
- }
-
- @Override
- public int getOrder() {
- return ROUTE_TO_URL_FILTER_ORDER + 1;
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutor.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutor.java
deleted file mode 100644
index 90f08fbea7..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutor.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.tencent.cloud.common.rule.Condition;
-import com.tencent.cloud.common.rule.ConditionUtils;
-import com.tencent.cloud.common.rule.KVPairUtils;
-import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils;
-
-import org.springframework.util.CollectionUtils;
-import org.springframework.web.server.ServerWebExchange;
-
-/**
- * Resolve labels from request by staining rule.
- * @author lepdou 2022-07-11
- */
-public class RuleStainingExecutor {
-
- Map execute(ServerWebExchange exchange, StainingRule stainingRule) {
- if (stainingRule == null) {
- return Collections.emptyMap();
- }
-
- List rules = stainingRule.getRules();
- if (CollectionUtils.isEmpty(rules)) {
- return Collections.emptyMap();
- }
-
- Map parsedLabels = new HashMap<>();
-
- for (StainingRule.Rule rule : rules) {
- List conditions = rule.getConditions();
-
- Set keys = new HashSet<>();
- conditions.forEach(condition -> keys.add(condition.getKey()));
- Map actualValues = SpringWebExpressionLabelUtils.resolve(exchange, keys);
-
- if (!ConditionUtils.match(actualValues, conditions)) {
- continue;
- }
-
- parsedLabels.putAll(KVPairUtils.toMap(rule.getLabels()));
- }
-
- return parsedLabels;
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingProperties.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingProperties.java
deleted file mode 100644
index 782cd2ecd4..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingProperties.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-/**
- * The properties for rule staining.
- * @author lepdou 2022-07-11
- */
-@ConfigurationProperties("spring.cloud.tencent.plugin.scg.staining.rule-staining")
-public class RuleStainingProperties {
-
- @Value("${spring.cloud.tencent.plugin.scg.staining.rule-staining.namespace:${spring.cloud.tencent.namespace:default}}")
- private String namespace;
-
- @Value("${spring.cloud.tencent.plugin.scg.staining.rule-staining.group:${spring.application.name:spring-cloud-gateway}}")
- private String group;
-
- @Value("${spring.cloud.tencent.plugin.scg.staining.rule-staining.fileName:rule/staining.json}")
- private String fileName;
-
- private boolean enabled = true;
-
- public String getNamespace() {
- return namespace;
- }
-
- public void setNamespace(String namespace) {
- this.namespace = namespace;
- }
-
- public String getGroup() {
- return group;
- }
-
- public void setGroup(String group) {
- this.group = group;
- }
-
- public String getFileName() {
- return fileName;
- }
-
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainer.java
deleted file mode 100644
index 4096e76386..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import java.util.Collections;
-import java.util.Map;
-
-import com.tencent.cloud.plugin.gateway.staining.TrafficStainer;
-
-import org.springframework.web.server.ServerWebExchange;
-
-/**
- * Staining the request by staining rules.
- * @author lepdou 2022-07-06
- */
-public class RuleTrafficStainer implements TrafficStainer {
-
- private final StainingRuleManager stainingRuleManager;
- private final RuleStainingExecutor ruleStainingExecutor;
-
- public RuleTrafficStainer(StainingRuleManager stainingRuleManager, RuleStainingExecutor ruleStainingExecutor) {
- this.stainingRuleManager = stainingRuleManager;
- this.ruleStainingExecutor = ruleStainingExecutor;
- }
-
- @Override
- public Map apply(ServerWebExchange exchange) {
- StainingRule stainingRule = stainingRuleManager.getStainingRule();
-
- if (stainingRule == null) {
- return Collections.emptyMap();
- }
-
- return ruleStainingExecutor.execute(exchange, stainingRule);
- }
-
- @Override
- public int getOrder() {
- return 0;
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRule.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRule.java
deleted file mode 100644
index 8a1df7a2bf..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRule.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import java.util.List;
-
-import com.tencent.cloud.common.rule.Condition;
-import com.tencent.cloud.common.rule.KVPair;
-
-/**
- * The rules for staining.
- * @author lepdou 2022-07-07
- */
-public class StainingRule {
-
- private List rules;
-
- public List getRules() {
- return rules;
- }
-
- public void setRules(List rules) {
- this.rules = rules;
- }
-
- @Override
- public String toString() {
- return "StainingRule{" +
- "rules=" + rules +
- '}';
- }
-
- public static class Rule {
- private List conditions;
- private List labels;
-
- public List getConditions() {
- return conditions;
- }
-
- public void setConditions(List conditions) {
- this.conditions = conditions;
- }
-
- public List getLabels() {
- return labels;
- }
-
- public void setLabels(List labels) {
- this.labels = labels;
- }
-
- @Override
- public String toString() {
- return "Rule{" +
- "conditions=" + conditions +
- ", labels=" + labels +
- '}';
- }
- }
-
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManager.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManager.java
deleted file mode 100644
index 87823f540e..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManager.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import com.tencent.cloud.common.util.JacksonUtils;
-import com.tencent.polaris.configuration.api.core.ConfigFile;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-import org.apache.commons.lang.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Fetch staining rule from polaris, and deserialize to {@link StainingRule}.
- * @author lepdou 2022-07-07
- */
-public class StainingRuleManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(StainingRuleManager.class);
-
- private final RuleStainingProperties stainingProperties;
- private final ConfigFileService configFileService;
-
- private StainingRule stainingRule;
-
- public StainingRuleManager(RuleStainingProperties stainingProperties, ConfigFileService configFileService) {
- this.stainingProperties = stainingProperties;
- this.configFileService = configFileService;
-
- initStainingRule();
- }
-
- private void initStainingRule() {
- ConfigFile rulesFile = configFileService.getConfigFile(stainingProperties.getNamespace(), stainingProperties.getGroup(),
- stainingProperties.getFileName());
-
- rulesFile.addChangeListener(event -> {
- LOGGER.info("[SCT] update scg staining rules. {}", event);
- deserialize(event.getNewValue());
- });
-
- String ruleJson = rulesFile.getContent();
- LOGGER.info("[SCT] init scg staining rules. {}", ruleJson);
-
- deserialize(ruleJson);
- }
-
- private void deserialize(String ruleJsonStr) {
- if (StringUtils.isBlank(ruleJsonStr)) {
- stainingRule = null;
- return;
- }
-
- try {
- stainingRule = JacksonUtils.deserialize(ruleJsonStr, StainingRule.class);
- }
- catch (Exception e) {
- LOGGER.error("[SCT] deserialize staining rule error.", e);
- throw e;
- }
- }
-
- public StainingRule getStainingRule() {
- return stainingRule;
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories
deleted file mode 100644
index 70c95961b9..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/main/resources/META-INF/spring.factories
+++ /dev/null
@@ -1,2 +0,0 @@
-org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
- com.tencent.cloud.plugin.gateway.SCGPluginsAutoConfiguration
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfigurationTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfigurationTest.java
deleted file mode 100644
index 04f43644eb..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/SCGPluginsAutoConfigurationTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway;
-
-import com.tencent.cloud.plugin.gateway.staining.TrafficStainingGatewayFilter;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingExecutor;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingProperties;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleTrafficStainer;
-import com.tencent.cloud.plugin.gateway.staining.rule.StainingRuleManager;
-import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-import com.tencent.polaris.configuration.factory.ConfigFileServiceFactory;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
-
-/**
- * Test for {@link SCGPluginsAutoConfiguration}.
- * @author derek.yi 2022-11-03
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(webEnvironment = DEFINED_PORT, classes = SCGPluginsAutoConfigurationTest.TestApplication.class,
- properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml",
- "spring.cloud.tencent.plugin.scg.staining.rule-staining.enabled = true"})
-public class SCGPluginsAutoConfigurationTest {
-
- @Autowired
- private ApplicationContext applicationContext;
-
- @Test
- public void testAutoConfiguration() {
- assertThat(applicationContext.getBeansOfType(RuleStainingProperties.class).size()).isEqualTo(1);
- assertThat(applicationContext.getBeansOfType(StainingRuleManager.class).size()).isEqualTo(1);
- assertThat(applicationContext.getBeansOfType(TrafficStainingGatewayFilter.class).size()).isEqualTo(1);
- assertThat(applicationContext.getBeansOfType(RuleStainingExecutor.class).size()).isEqualTo(1);
- assertThat(applicationContext.getBeansOfType(RuleTrafficStainer.class).size()).isEqualTo(1);
- }
-
- @SpringBootApplication
- public static class TestApplication {
-
- @Bean
- public ConfigFileService configFileService(PolarisSDKContextManager polarisSDKContextManager) {
- return ConfigFileServiceFactory.createConfigFileService(polarisSDKContextManager.getSDKContext());
- }
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilterTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilterTest.java
deleted file mode 100644
index 44414bf7e3..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/TrafficStainingGatewayFilterTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.tencent.cloud.common.metadata.MetadataContext;
-import com.tencent.cloud.common.metadata.MetadataContextHolder;
-import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingExecutor;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleStainingProperties;
-import com.tencent.cloud.plugin.gateway.staining.rule.RuleTrafficStainer;
-import com.tencent.cloud.plugin.gateway.staining.rule.StainingRuleManager;
-import com.tencent.polaris.configuration.api.core.ConfigFile;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import reactor.core.publisher.Mono;
-
-import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
-import org.springframework.mock.web.server.MockServerWebExchange;
-import org.springframework.web.server.ServerWebExchange;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * Test for {@link TrafficStainingGatewayFilter}.
- * @author lepdou 2022-07-12
- */
-@ExtendWith(MockitoExtension.class)
-public class TrafficStainingGatewayFilterTest {
-
- private final String testNamespace = "testNamespace";
- private final String testGroup = "testGroup";
- private final String testFileName = "rule.json";
- @Mock
- private GatewayFilterChain chain;
- @Mock
- private ServerWebExchange exchange;
- @Mock
- private ConfigFileService configFileService;
-
- @BeforeAll
- static void beforeAll() {
- Mockito.mockStatic(ApplicationContextAwareUtils.class);
- when(ApplicationContextAwareUtils
- .getProperties(any())).thenReturn("fooBar");
- }
-
- @Test
- public void testNoneTrafficStainingImplement() {
- TrafficStainingGatewayFilter filter = new TrafficStainingGatewayFilter(null);
-
- when(chain.filter(exchange)).thenReturn(Mono.empty());
-
- filter.filter(exchange, chain);
-
- verify(chain).filter(exchange);
- }
-
- @Test
- public void testMultiStaining() {
- TrafficStainer trafficStainer1 = Mockito.mock(TrafficStainer.class);
- TrafficStainer trafficStainer2 = Mockito.mock(TrafficStainer.class);
-
- when(trafficStainer1.getOrder()).thenReturn(1);
- when(trafficStainer2.getOrder()).thenReturn(2);
-
- Map labels1 = new HashMap<>();
- labels1.put("k1", "v1");
- labels1.put("k2", "v2");
- when(trafficStainer1.apply(exchange)).thenReturn(labels1);
-
- Map labels2 = new HashMap<>();
- labels2.put("k1", "v11");
- labels2.put("k3", "v3");
- when(trafficStainer2.apply(exchange)).thenReturn(labels2);
-
- TrafficStainingGatewayFilter filter = new TrafficStainingGatewayFilter(Arrays.asList(trafficStainer1, trafficStainer2));
- Map result = filter.getStainedLabels(exchange);
-
- assertThat(result).isNotEmpty();
- assertThat(result.get("k1")).isEqualTo("v1");
- assertThat(result.get("k2")).isEqualTo("v2");
- assertThat(result.get("k3")).isEqualTo("v3");
- }
-
- @Test
- public void testNoTrafficStainers() {
- MetadataContext metadataContext = new MetadataContext();
- MetadataContextHolder.set(metadataContext);
-
- TrafficStainingGatewayFilter filter = new TrafficStainingGatewayFilter(null);
- filter.filter(exchange, chain);
- Map map = metadataContext.getTransitiveMetadata();
- assertThat(map).isEmpty();
- }
-
- @Test
- public void testWithTrafficStainers() {
- MetadataContext metadataContext = new MetadataContext();
- MetadataContextHolder.set(metadataContext);
-
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn("{\n"
- + " \"rules\":[\n"
- + " {\n"
- + " \"conditions\":[\n"
- + " {\n"
- + " \"key\":\"${http.query.uid}\",\n"
- + " \"values\":[\"1000\"],\n"
- + " \"operation\":\"EQUALS\"\n"
- + " }\n"
- + " ],\n"
- + " \"labels\":[\n"
- + " {\n"
- + " \"key\":\"env\",\n"
- + " \"value\":\"blue\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + "}");
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
- RuleStainingExecutor ruleStainingExecutor = new RuleStainingExecutor();
- RuleTrafficStainer ruleTrafficStainer = new RuleTrafficStainer(stainingRuleManager, ruleStainingExecutor);
-
- TrafficStainingGatewayFilter filter = new TrafficStainingGatewayFilter(Collections.singletonList(ruleTrafficStainer));
-
- MockServerHttpRequest request = MockServerHttpRequest.get("/users")
- .queryParam("uid", "1000").build();
- MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
-
- filter.filter(exchange, chain);
- Map map = metadataContext.getTransitiveMetadata();
- assertThat(map).isNotNull();
- assertThat(map.size()).isEqualTo(1);
- assertThat(map.get("env")).isEqualTo("blue");
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutorTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutorTest.java
deleted file mode 100644
index cff02243a8..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleStainingExecutorTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Map;
-
-import com.tencent.cloud.common.rule.Condition;
-import com.tencent.cloud.common.rule.KVPair;
-import com.tencent.cloud.common.rule.Operation;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
-import org.springframework.mock.web.server.MockServerWebExchange;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-/**
- * Test for {@link RuleStainingExecutor}.
- * @author lepdou 2022-07-12
- */
-@ExtendWith(MockitoExtension.class)
-public class RuleStainingExecutorTest {
-
- @Test
- public void testMatchCondition() {
- Condition condition1 = new Condition();
- condition1.setKey("${http.header.uid}");
- condition1.setOperation(Operation.EQUALS.toString());
- condition1.setValues(Collections.singletonList("1000"));
-
- Condition condition2 = new Condition();
- condition2.setKey("${http.query.source}");
- condition2.setOperation(Operation.IN.toString());
- condition2.setValues(Collections.singletonList("wx"));
-
- StainingRule.Rule rule = new StainingRule.Rule();
- rule.setConditions(Arrays.asList(condition1, condition2));
-
- KVPair kvPair = new KVPair();
- kvPair.setKey("env");
- kvPair.setValue("blue");
- rule.setLabels(Collections.singletonList(kvPair));
-
- StainingRule stainingRule = new StainingRule();
- stainingRule.setRules(Collections.singletonList(rule));
-
- MockServerHttpRequest request = MockServerHttpRequest.get("/users")
- .queryParam("source", "wx")
- .header("uid", "1000").build();
- MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
-
- RuleStainingExecutor executor = new RuleStainingExecutor();
-
- Map stainedLabels = executor.execute(exchange, stainingRule);
-
- assertThat(stainedLabels).isNotNull();
- assertThat(stainedLabels.size()).isEqualTo(1);
- assertThat(stainedLabels.get("env")).isEqualTo("blue");
- }
-
- @Test
- public void testNotMatchCondition() {
- Condition condition1 = new Condition();
- condition1.setKey("${http.header.uid}");
- condition1.setOperation(Operation.EQUALS.toString());
- condition1.setValues(Collections.singletonList("1000"));
-
- Condition condition2 = new Condition();
- condition2.setKey("${http.query.source}");
- condition2.setOperation(Operation.IN.toString());
- condition2.setValues(Collections.singletonList("wx"));
-
- StainingRule.Rule rule = new StainingRule.Rule();
- rule.setConditions(Arrays.asList(condition1, condition2));
-
- KVPair kvPair = new KVPair();
- kvPair.setKey("env");
- kvPair.setValue("blue");
- rule.setLabels(Collections.singletonList(kvPair));
-
- StainingRule stainingRule = new StainingRule();
- stainingRule.setRules(Collections.singletonList(rule));
-
- MockServerHttpRequest request = MockServerHttpRequest.get("/users")
- .queryParam("source", "wx")
- .header("uid", "10001").build();
- MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
-
- RuleStainingExecutor executor = new RuleStainingExecutor();
-
- Map stainedLabels = executor.execute(exchange, stainingRule);
-
- assertThat(stainedLabels).isNotNull();
- assertThat(stainedLabels.size()).isEqualTo(0);
- }
-
- @Test
- public void testMatchTwoRulesAndNotMatchOneRule() {
- Condition condition1 = new Condition();
- condition1.setKey("${http.header.uid}");
- condition1.setOperation(Operation.EQUALS.toString());
- condition1.setValues(Collections.singletonList("1000"));
-
- Condition condition2 = new Condition();
- condition2.setKey("${http.query.source}");
- condition2.setOperation(Operation.IN.toString());
- condition2.setValues(Collections.singletonList("wx"));
-
- // rule1 matched
- StainingRule.Rule rule1 = new StainingRule.Rule();
- rule1.setConditions(Arrays.asList(condition1, condition2));
-
- KVPair kvPair = new KVPair();
- kvPair.setKey("env");
- kvPair.setValue("blue");
- rule1.setLabels(Collections.singletonList(kvPair));
-
- // rule2 matched
- StainingRule.Rule rule2 = new StainingRule.Rule();
- rule2.setConditions(Collections.singletonList(condition1));
-
- KVPair kvPair2 = new KVPair();
- kvPair2.setKey("label1");
- kvPair2.setValue("value1");
- KVPair kvPair3 = new KVPair();
- kvPair3.setKey("label2");
- kvPair3.setValue("value2");
- rule2.setLabels(Arrays.asList(kvPair2, kvPair3));
-
- // rule3 not matched
- Condition condition3 = new Condition();
- condition3.setKey("${http.query.type}");
- condition3.setOperation(Operation.IN.toString());
- condition3.setValues(Collections.singletonList("wx"));
-
- StainingRule.Rule rule3 = new StainingRule.Rule();
- rule3.setConditions(Collections.singletonList(condition3));
-
- KVPair kvPair4 = new KVPair();
- kvPair4.setKey("label3");
- kvPair4.setValue("value3");
- rule3.setLabels(Collections.singletonList(kvPair4));
-
- StainingRule stainingRule = new StainingRule();
- stainingRule.setRules(Arrays.asList(rule1, rule2, rule3));
-
- MockServerHttpRequest request = MockServerHttpRequest.get("/users")
- .queryParam("source", "wx")
- .header("uid", "1000").build();
- MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
-
- RuleStainingExecutor executor = new RuleStainingExecutor();
-
- Map stainedLabels = executor.execute(exchange, stainingRule);
-
- assertThat(stainedLabels).isNotNull();
- assertThat(stainedLabels.size()).isEqualTo(3);
- assertThat(stainedLabels.get("env")).isEqualTo("blue");
- assertThat(stainedLabels.get("label1")).isEqualTo("value1");
- assertThat(stainedLabels.get("label2")).isEqualTo("value2");
- }
-
- @Test
- public void testNoStainingRule() {
- RuleStainingExecutor executor = new RuleStainingExecutor();
- assertThat(executor.execute(null, null)).isEmpty();
- assertThat(executor.execute(null, new StainingRule())).isEmpty();
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainerTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainerTest.java
deleted file mode 100644
index d65a913a02..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/RuleTrafficStainerTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import java.util.Map;
-
-import com.tencent.polaris.configuration.api.core.ConfigFile;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
-import org.springframework.mock.web.server.MockServerWebExchange;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.when;
-
-/**
- * Test for {@link RuleTrafficStainer}.
- * @author derek.yi 2022-11-03
- */
-@ExtendWith(MockitoExtension.class)
-public class RuleTrafficStainerTest {
-
- private final String testNamespace = "testNamespace";
- private final String testGroup = "testGroup";
- private final String testFileName = "rule.json";
- @Mock
- private ConfigFileService configFileService;
-
- @Test
- public void testNoStainingRule() {
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn("");
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
- RuleStainingExecutor ruleStainingExecutor = new RuleStainingExecutor();
- RuleTrafficStainer ruleTrafficStainer = new RuleTrafficStainer(stainingRuleManager, ruleStainingExecutor);
- Map map = ruleTrafficStainer.apply(null);
- assertThat(map).isEmpty();
- }
-
- @Test
- public void testWithStainingRule() {
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn("{\n"
- + " \"rules\":[\n"
- + " {\n"
- + " \"conditions\":[\n"
- + " {\n"
- + " \"key\":\"${http.query.uid}\",\n"
- + " \"values\":[\"1000\"],\n"
- + " \"operation\":\"EQUALS\"\n"
- + " }\n"
- + " ],\n"
- + " \"labels\":[\n"
- + " {\n"
- + " \"key\":\"env\",\n"
- + " \"value\":\"blue\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + "}");
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
- RuleStainingExecutor ruleStainingExecutor = new RuleStainingExecutor();
- RuleTrafficStainer ruleTrafficStainer = new RuleTrafficStainer(stainingRuleManager, ruleStainingExecutor);
-
- MockServerHttpRequest request = MockServerHttpRequest.get("/users")
- .queryParam("uid", "1000").build();
- MockServerWebExchange exchange = new MockServerWebExchange.Builder(request).build();
-
- Map map = ruleTrafficStainer.apply(exchange);
- assertThat(map).isNotNull();
- assertThat(map.size()).isEqualTo(1);
- assertThat(map.get("env")).isEqualTo("blue");
- }
-}
diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManagerTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManagerTest.java
deleted file mode 100644
index d8d90a5f7f..0000000000
--- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-gateway-plugin/src/test/java/com/tencent/cloud/plugin/gateway/staining/rule/StainingRuleManagerTest.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Tencent is pleased to support the open source community by making spring-cloud-tencent available.
- *
- * Copyright (C) 2021 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.plugin.gateway.staining.rule;
-
-import com.tencent.polaris.configuration.api.core.ConfigFile;
-import com.tencent.polaris.configuration.api.core.ConfigFileService;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
-import static org.mockito.Mockito.when;
-
-/**
- * Test for {@link StainingRuleManager}.
- * @author lepdou 2022-07-12
- */
-@ExtendWith(MockitoExtension.class)
-public class StainingRuleManagerTest {
-
- private final String testNamespace = "testNamespace";
- private final String testGroup = "testGroup";
- private final String testFileName = "rule.json";
- @Mock
- private ConfigFileService configFileService;
-
- @Test
- public void testNormalRule() {
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn("{\n"
- + " \"rules\":[\n"
- + " {\n"
- + " \"conditions\":[\n"
- + " {\n"
- + " \"key\":\"${http.query.uid}\",\n"
- + " \"values\":[\"1000\"],\n"
- + " \"operation\":\"EQUALS\"\n"
- + " }\n"
- + " ],\n"
- + " \"labels\":[\n"
- + " {\n"
- + " \"key\":\"env\",\n"
- + " \"value\":\"blue\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + "}");
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
-
- StainingRule stainingRule = stainingRuleManager.getStainingRule();
-
- assertThat(stainingRule).isNotNull();
- assertThat(stainingRule.getRules().size()).isEqualTo(1);
- StainingRule.Rule rule = stainingRule.getRules().get(0);
- assertThat(rule.getConditions().size()).isEqualTo(1);
- assertThat(rule.getLabels().size()).isEqualTo(1);
- }
-
- @Test
- public void testWrongRule() {
- assertThatCode(() -> {
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn("{\n"
- + " \"rules\":[\n"
- + " {\n"
- + " \"conditionsxxxx\":[\n"
- + " {\n"
- + " \"key\":\"${http.query.uid}\",\n"
- + " \"values\":[\"1000\"],\n"
- + " \"operation\":\"EQUALS\"\n"
- + " }\n"
- + " ],\n"
- + " \"labels\":[\n"
- + " {\n"
- + " \"key\":\"env\",\n"
- + " \"value\":\"blue\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " ]\n"
- + "}");
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- new StainingRuleManager(ruleStainingProperties, configFileService);
- }).isInstanceOf(RuntimeException.class);
- }
-
- @Test
- public void testEmptyRule() {
- RuleStainingProperties ruleStainingProperties = new RuleStainingProperties();
- ruleStainingProperties.setNamespace(testNamespace);
- ruleStainingProperties.setGroup(testGroup);
- ruleStainingProperties.setFileName(testFileName);
-
- ConfigFile configFile = Mockito.mock(ConfigFile.class);
- when(configFile.getContent()).thenReturn(null);
- when(configFileService.getConfigFile(testNamespace, testGroup, testFileName)).thenReturn(configFile);
-
- StainingRuleManager stainingRuleManager = new StainingRuleManager(ruleStainingProperties, configFileService);
- assertThat(stainingRuleManager.getStainingRule()).isNull();
- }
-}
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
index ad60f858d1..98f3d7645e 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/feign/EnhancedFeignClient.java
@@ -97,6 +97,7 @@ public Response execute(Request request, Options options) throws IOException {
try {
// Run pre enhanced plugins.
pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
+ startMillis = System.currentTimeMillis();
Response response = delegate.execute(request, options);
enhancedPluginContext.setDelay(System.currentTimeMillis() - startMillis);
diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
index b30c7835ae..f2bed62c0d 100644
--- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
+++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/instrument/resttemplate/EnhancedRestTemplateWrapInterceptor.java
@@ -79,30 +79,12 @@ public ClientHttpResponse intercept(HttpRequest request, String serviceId,
enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance());
-
- // Run pre enhanced plugins.
+ long startMillis = System.currentTimeMillis();
try {
+ // Run pre enhanced plugins.
pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext);
- }
- catch (CallAbortedException callAbortedException) {
- MetadataObjectValue