From 6e7bd2bc3e2964125e46b6dfc779f0193898ff3e Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 16 Apr 2024 19:01:26 +0800 Subject: [PATCH 01/10] fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5 (#1267) Co-authored-by: gzoldou --- CHANGELOG.md | 1 + .../resttemplate/RouterLabelRestTemplateInterceptor.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de45c9eaa..b8397801d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,3 +4,4 @@ - [feat:upgrade jackson version.](https://github.com/Tencent/spring-cloud-tencent/pull/1259) - [fix:fix ApplicationContextAwareUtils NPE bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1294) - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1308) +- [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1267) diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java index 808b2f687..b23c5e14e 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/RouterLabelRestTemplateInterceptor.java @@ -45,6 +45,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; @@ -104,7 +105,8 @@ public ClientHttpResponse intercept(@NonNull HttpRequest request, @NonNull byte[ ClientHttpResponse response = clientHttpRequestExecution.execute(request, body); if (!CollectionUtils.isEmpty(request.getHeaders().get(RouterConstant.ROUTER_LABEL_HEADER))) { - response.getHeaders().addAll(RouterConstant.ROUTER_LABEL_HEADER, Objects.requireNonNull(request.getHeaders() + HttpHeaders responseHeaders = HttpHeaders.writableHttpHeaders(response.getHeaders()); + responseHeaders.addAll(RouterConstant.ROUTER_LABEL_HEADER, Objects.requireNonNull(request.getHeaders() .get(RouterConstant.ROUTER_LABEL_HEADER))); } From be4b20cc895529d11e20c969e04dbc06daadfc57 Mon Sep 17 00:00:00 2001 From: Haotian Zhang <928016560@qq.com> Date: Mon, 15 Jul 2024 19:14:51 +0800 Subject: [PATCH 02/10] fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5 (#1337) Co-authored-by: andrew shan <45474304+andrewshan@users.noreply.github.com> fishtailfu --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8397801d..ff5774879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,4 @@ - [feat:upgrade jackson version.](https://github.com/Tencent/spring-cloud-tencent/pull/1259) - [fix:fix ApplicationContextAwareUtils NPE bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1294) - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1308) -- [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1267) +- [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1337) From 73f7e3e365f7ced4365a419d0b88c74c774cae92 Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Mon, 15 Jul 2024 16:39:52 +0800 Subject: [PATCH 03/10] fix:feat: support lossless online/offline --- .../registry/PolarisServiceRegistry.java | 21 +- .../cloud/common/constant/OrderConstant.java | 5 + .../cloud/common}/util/OkHttpUtil.java | 10 +- .../tencent/cloud/common/util/UrlUtils.java | 78 +++++++ .../cloud/common}/util/OkHttpUtilTest.java | 12 +- .../cloud/common/util/UrlUtilsTest.java | 68 +++++++ spring-cloud-tencent-coverage/pom.xml | 5 + spring-cloud-tencent-dependencies/pom.xml | 6 + .../lossless-callee-service/pom.xml | 63 ++++++ .../callee/LosslessCalleeController.java | 95 +++++++++ .../callee/LosslessCalleeService.java | 29 +++ .../src/main/resources/bootstrap.yml | 36 ++++ .../lossless-nacos-callee-service/pom.xml | 69 +++++++ .../callee/LosslessNacosCalleeController.java | 95 +++++++++ .../callee/LosslessNacosCalleeService.java | 31 +++ .../src/main/resources/bootstrap.yml | 28 +++ .../lossless-example/pom.xml | 25 +++ spring-cloud-tencent-examples/pom.xml | 1 + spring-cloud-tencent-plugin-starters/pom.xml | 1 + .../pom.xml | 16 +- ...acosDiscoveryAdapterAutoConfiguration.java | 9 + .../transformer/NacosInstanceTransformer.java | 1 + .../NacosRegistrationTransformer.java | 43 ++++ .../pom.xml | 75 +++++++ .../lossless/LosslessRegistryAspect.java | 104 ++++++++++ .../SpringCloudLosslessActionProvider.java | 95 +++++++++ .../config/LosslessAutoConfiguration.java | 54 +++++ .../config/LosslessConfigModifier.java | 59 ++++++ .../lossless/config/LosslessProperties.java | 75 +++++++ .../LosslessPropertiesAutoConfiguration.java | 44 ++++ ...slessPropertiesBootstrapConfiguration.java | 35 ++++ ...itional-spring-configuration-metadata.json | 10 + .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../lossless/LosslessConfigModifierTest.java | 91 +++++++++ .../lossless/LosslessPropertiesTest.java | 40 ++++ .../lossless/LosslessRegistryAspectTest.java | 191 ++++++++++++++++++ .../AutoServiceRegistrationUtils.java | 29 +++ .../context/PolarisSDKContextManager.java | 16 ++ .../RpcEnhancementAutoConfiguration.java | 12 +- .../PolarisRegistrationTransformer.java | 25 +++ .../transformer/RegistrationTransformer.java | 55 +++++ 42 files changed, 1739 insertions(+), 21 deletions(-) rename {spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris => spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common}/util/OkHttpUtil.java (88%) create mode 100644 spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/UrlUtils.java rename {spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris => spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common}/util/OkHttpUtilTest.java (82%) create mode 100644 spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/UrlUtilsTest.java create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-callee-service/pom.xml create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeController.java create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeService.java create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/resources/bootstrap.yml create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeController.java create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeService.java create mode 100644 spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/resources/bootstrap.yml create mode 100644 spring-cloud-tencent-examples/lossless-example/pom.xml create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/pom.xml create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspect.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/SpringCloudLosslessActionProvider.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifier.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessProperties.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesAutoConfiguration.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesBootstrapConfiguration.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring.factories create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessPropertiesTest.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java create mode 100644 spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/PolarisRegistrationTransformer.java create mode 100644 spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/RegistrationTransformer.java diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java index ebc16be98..43aeaa8c7 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java @@ -25,10 +25,10 @@ import java.util.concurrent.ScheduledExecutorService; import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.common.util.OkHttpUtil; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.cloud.polaris.context.PolarisSDKContextManager; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; -import com.tencent.cloud.polaris.util.OkHttpUtil; import com.tencent.cloud.rpc.enhancement.stat.config.PolarisStatProperties; import com.tencent.polaris.api.config.global.StatReporterConfig; import com.tencent.polaris.api.core.ProviderAPI; @@ -176,7 +176,7 @@ public void register(PolarisRegistration registration) { public void deregister(PolarisRegistration registration) { LOGGER.info("De-registering from Polaris Server now..."); - if (StringUtils.isEmpty(registration.getServiceId())) { + if (StringUtils.isEmpty(registration.getServiceId()) || !PolarisSDKContextManager.isRegistered) { LOGGER.warn("No dom to de-register for polaris client..."); return; } @@ -191,6 +191,8 @@ public void deregister(PolarisRegistration registration) { try { ProviderAPI providerClient = polarisSDKContextManager.getProviderAPI(); providerClient.deRegister(deRegisterRequest); + PolarisSDKContextManager.isRegistered = false; + LOGGER.info("De-registration finished."); } catch (Exception e) { LOGGER.error("ERR_POLARIS_DEREGISTER, de-register failed...{},", registration, e); @@ -199,8 +201,6 @@ public void deregister(PolarisRegistration registration) { if (null != heartbeatExecutor) { heartbeatExecutor.shutdown(); } - LOGGER.info("De-registration finished."); - PolarisSDKContextManager.isRegistered = false; } } @@ -238,21 +238,14 @@ public Object getStatus(PolarisRegistration registration) { public void heartbeat(InstanceHeartbeatRequest heartbeatRequest) { heartbeatExecutor.scheduleWithFixedDelay(() -> { try { - String healthCheckEndpoint = polarisDiscoveryProperties.getHealthCheckUrl(); // If the health check passes, the heartbeat will be reported. // If it does not pass, the heartbeat will not be reported. - if (!healthCheckEndpoint.startsWith("/")) { - healthCheckEndpoint = "/" + healthCheckEndpoint; - } - - String healthCheckUrl = String.format("http://%s:%s%s", heartbeatRequest.getHost(), - heartbeatRequest.getPort(), healthCheckEndpoint); - Map headers = new HashMap<>(1); headers.put(HttpHeaders.USER_AGENT, "polaris"); - if (!OkHttpUtil.get(healthCheckUrl, headers)) { + if (!OkHttpUtil.checkUrl(heartbeatRequest.getHost(), heartbeatRequest.getPort(), + polarisDiscoveryProperties.getHealthCheckUrl(), headers)) { LOGGER.error("backend service health check failed. health check endpoint = {}", - healthCheckEndpoint); + polarisDiscoveryProperties.getHealthCheckUrl()); return; } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java index ac7aa169f..ee0d8e763 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/OrderConstant.java @@ -172,6 +172,11 @@ public static final class Modifier { */ public static Integer STAT_REPORTER_ORDER = 1; + /** + * Order of lossless configuration modifier. + */ + public static Integer LOSSLESS_ORDER = 2; + /** * Order of service contract configuration modifier. */ diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/OkHttpUtil.java similarity index 88% rename from spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java rename to spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/OkHttpUtil.java index f8580ee19..e34d79f56 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/OkHttpUtil.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.util; +package com.tencent.cloud.common.util; import java.io.BufferedReader; import java.io.InputStreamReader; @@ -87,4 +87,12 @@ public static boolean get(String path, Map headers) { } return false; } + + public static boolean checkUrl(String host, Integer port, String endpoint, Map headers) { + if (!endpoint.startsWith("/")) { + endpoint = "/" + endpoint; + } + String checkUrl = String.format("http://%s:%s%s", host, port, endpoint); + return get(checkUrl, headers); + } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/UrlUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/UrlUtils.java new file mode 100644 index 000000000..6190f33c4 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/UrlUtils.java @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.common.util; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.util.StringUtils; + +import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; + +/** + * Utils for URLDecoder/URLEncoder. + * + * @author Shedfree Wu + */ +public final class UrlUtils { + + private static final Logger LOG = LoggerFactory.getLogger(UrlUtils.class); + + private UrlUtils() { + } + + public static String decode(String s) { + return decode(s, UTF_8); + } + + public static String decode(String s, String enc) { + if (!StringUtils.hasText(s)) { + return s; + } + try { + return URLDecoder.decode(s, enc); + } + catch (UnsupportedEncodingException e) { + LOG.warn("Runtime system does not support {} coding. s:{}, msg:{}", enc, s, e.getMessage()); + // return original string + return s; + } + } + + public static String encode(String s) { + return encode(s, UTF_8); + } + + public static String encode(String s, String enc) { + if (!StringUtils.hasText(s)) { + return s; + } + try { + return URLEncoder.encode(s, enc); + } + catch (UnsupportedEncodingException e) { + LOG.warn("Runtime system does not support {} coding. s:{}, msg:{}", enc, s, e.getMessage()); + // return original string + return s; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/OkHttpUtilTest.java similarity index 82% rename from spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java rename to spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/OkHttpUtilTest.java index b718e88e9..2e250e3d8 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/OkHttpUtilTest.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.util; +package com.tencent.cloud.common.util; import org.assertj.core.util.Maps; import org.junit.jupiter.api.Test; @@ -37,7 +37,13 @@ * @author Haotian Zhang */ @ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = OkHttpUtilTest.TestApplication.class, properties = {"spring.application.name=test", "spring.cloud.polaris.discovery.register=false"}) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + classes = OkHttpUtilTest.TestApplication.class, + properties = { + "spring.application.name=test", + "spring.cloud.polaris.discovery.register=false", + "spring.cloud.gateway.enabled=false" + }) public class OkHttpUtilTest { @LocalServerPort @@ -46,6 +52,8 @@ public class OkHttpUtilTest { @Test public void testGet() { assertThat(OkHttpUtil.get("http://localhost:" + port + "/test", Maps.newHashMap("key", "value"))).isTrue(); + assertThat(OkHttpUtil.checkUrl("localhost", port, "/test", Maps.newHashMap("key", "value"))).isTrue(); + assertThat(OkHttpUtil.checkUrl("localhost", port, "test", Maps.newHashMap("key", "value"))).isTrue(); assertThat(OkHttpUtil.get("http://localhost:" + port + "/error", Maps.newHashMap("key", "value"))).isFalse(); assertThat(OkHttpUtil.get("http://localhost:55555/error", Maps.newHashMap("key", "value"))).isFalse(); } diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/UrlUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/UrlUtilsTest.java new file mode 100644 index 000000000..3d77d047c --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/UrlUtilsTest.java @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.common.util; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Utils for {@link UrlUtils}. + * + * @author Shedfree Wu + */ +public class UrlUtilsTest { + + @Test + public void testEncodeDecode1() { + String expectEncodeValue = "a%2Fb"; + String origin = "a/b"; + String encode1 = UrlUtils.encode(origin); + assertThat(expectEncodeValue).isEqualTo(encode1); + // encode twice is different + String encode2 = UrlUtils.encode(encode1); + assertThat(encode1).isNotEqualTo(encode2); + // test decode + assertThat(origin).isEqualTo(UrlUtils.decode(encode1)); + } + + @Test + public void testEncodeDecode2() { + + String origin = null; + String encode1 = UrlUtils.encode(origin); + assertThat(encode1).isNull(); + + origin = ""; + encode1 = UrlUtils.encode(origin); + assertThat(encode1).isEqualTo(origin); + } + + @Test + public void testError() { + String origin = "a/b"; + String encode = UrlUtils.encode(origin, "error-enc"); + assertThat(encode).isEqualTo(origin); + + encode = "a%2Fb"; + String decode = UrlUtils.decode(encode, "error-enc"); + assertThat(decode).isEqualTo(encode); + } + +} diff --git a/spring-cloud-tencent-coverage/pom.xml b/spring-cloud-tencent-coverage/pom.xml index 15aef4a4e..8481f826b 100644 --- a/spring-cloud-tencent-coverage/pom.xml +++ b/spring-cloud-tencent-coverage/pom.xml @@ -83,6 +83,11 @@ com.tencent.cloud spring-cloud-tencent-gateway-plugin + + + com.tencent.cloud + spring-cloud-tencent-lossless-plugin + diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml index e3842eeaa..9bc67a445 100644 --- a/spring-cloud-tencent-dependencies/pom.xml +++ b/spring-cloud-tencent-dependencies/pom.xml @@ -190,6 +190,12 @@ ${revision} + + com.tencent.cloud + spring-cloud-tencent-lossless-plugin + ${revision} + + com.google.guava diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/pom.xml b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/pom.xml new file mode 100644 index 000000000..9117a7314 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/pom.xml @@ -0,0 +1,63 @@ + + 4.0.0 + + lossless-example + com.tencent.cloud + ${revision} + ../pom.xml + + lossless-callee-service + Spring Cloud Starter Tencent Lossless Callee Service Example + + + + com.tencent.cloud + spring-cloud-starter-tencent-all + + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.tencent.cloud + spring-cloud-tencent-lossless-plugin + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeController.java b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeController.java new file mode 100644 index 000000000..ec7347c91 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeController.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.lossless.callee; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import com.tencent.cloud.common.constant.MetadataConstant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; + +@RestController +@RequestMapping("/lossless/callee") +public class LosslessCalleeController { + + private static final Logger LOG = LoggerFactory.getLogger(LosslessCalleeController.class); + + @Value("${lossless.healthy.delay.second:0}") + private int healthyDelay; + + private final AtomicBoolean calledHealthyEndpoint = new AtomicBoolean(false); + + private final AtomicInteger healthy = new AtomicInteger(0); + + @GetMapping("/health") + public ResponseEntity health() { + if (healthy.get() == 1) { + return new ResponseEntity<>("OK", HttpStatus.OK); + } + else { + if (calledHealthyEndpoint.compareAndSet(false, true)) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + if (healthyDelay > 0) { + try { + Thread.sleep(healthyDelay * 1000L); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + healthy.set(1); + } + }); + thread.start(); + } + return new ResponseEntity<>("NOK", HttpStatus.SERVICE_UNAVAILABLE); + } + } + + /** + * Get metadata in HTTP query. + * + * @param metadataStr metadata string + * @return metadata in HTTP header + * @throws UnsupportedEncodingException encoding exception + */ + @RequestMapping("/echo") + public String echoHeader(@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr) + throws UnsupportedEncodingException { + LOG.info(URLDecoder.decode(metadataStr, UTF_8)); + metadataStr = URLDecoder.decode(metadataStr, UTF_8); + return metadataStr; + } + +} diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeService.java b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeService.java new file mode 100644 index 000000000..542653ba0 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/java/com/tencent/cloud/lossless/callee/LosslessCalleeService.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.lossless.callee; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LosslessCalleeService { + public static void main(String[] args) { + SpringApplication.run(LosslessCalleeService.class, args); + } +} diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..cdd07b831 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-callee-service/src/main/resources/bootstrap.yml @@ -0,0 +1,36 @@ +server: + port: 48090 +spring: + application: + name: LosslessCalleeService + cloud: + polaris: + address: grpc://119.91.66.223:8091 + namespace: default + enabled: true + discovery: + enabled: true + register: true + contract: + exposure: true + report: + enabled: true + stat: + enabled: true + port: 28084 + lossless: + enabled: true + healthCheckPath: /lossless/callee/health + healthCheckInterval: 5000 +lossless: + healthy: + delay: + second: 20 +management: + endpoints: + web: + exposure: + include: + - polaris-discovery + - polaris-ratelimit + - polaris-config \ No newline at end of file diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml new file mode 100644 index 000000000..2be4b3bc1 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + lossless-example + com.tencent.cloud + ${revision} + ../pom.xml + + com.tencent.polaris + lossless-nacos-callee-service + Spring Cloud Starter Tencent Lossless Nacos Callee Service Example + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2023.0.0.0-RC1 + + + + com.tencent.cloud + spring-cloud-starter-tencent-discovery-adapter-plugin + + + org.springframework.boot + spring-boot-starter-webflux + + + + org.springframework.boot + spring-boot-starter-actuator + + + + com.tencent.cloud + spring-cloud-tencent-lossless-plugin + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeController.java b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeController.java new file mode 100644 index 000000000..ea96f64b3 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeController.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.lossless.nacos.callee; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import com.tencent.cloud.common.constant.MetadataConstant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; + +@RestController +@RequestMapping("/lossless/nacos/callee") +public class LosslessNacosCalleeController { + + private static final Logger LOG = LoggerFactory.getLogger(LosslessNacosCalleeController.class); + + @Value("${lossless.healthy.delay.second:0}") + private int healthyDelay; + + private final AtomicBoolean calledHealthyEndpoint = new AtomicBoolean(false); + + private final AtomicInteger healthy = new AtomicInteger(0); + + @GetMapping("/health") + public ResponseEntity health() { + if (healthy.get() == 1) { + return new ResponseEntity<>("OK", HttpStatus.OK); + } + else { + if (calledHealthyEndpoint.compareAndSet(false, true)) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + if (healthyDelay > 0) { + try { + Thread.sleep(healthyDelay * 1000L); + } + catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + healthy.set(1); + } + }); + thread.start(); + } + return new ResponseEntity<>("NOK", HttpStatus.SERVICE_UNAVAILABLE); + } + } + + /** + * Get metadata in HTTP query. + * + * @param metadataStr metadata string + * @return metadata in HTTP header + * @throws UnsupportedEncodingException encoding exception + */ + @RequestMapping("/echo") + public String echoHeader(@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr) + throws UnsupportedEncodingException { + LOG.info(URLDecoder.decode(metadataStr, UTF_8)); + metadataStr = URLDecoder.decode(metadataStr, UTF_8); + return metadataStr; + } + +} diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeService.java b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeService.java new file mode 100644 index 000000000..a2d29436a --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/java/com/tencent/cloud/lossless/nacos/callee/LosslessNacosCalleeService.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.lossless.nacos.callee; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LosslessNacosCalleeService { + + public static void main(String[] args) { + SpringApplication.run(LosslessNacosCalleeService.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/resources/bootstrap.yml new file mode 100644 index 000000000..1487942e9 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/src/main/resources/bootstrap.yml @@ -0,0 +1,28 @@ +server: + port: 48091 +spring: + application: + name: LosslessNacosCalleeService + cloud: + nacos: + discovery: + server-addr: 9.134.5.52:8848 + enabled: true + namespace: "test1" + polaris: + lossless: + enabled: true + healthCheckPath: /lossless/nacos/callee/health + healthCheckInterval: 5000 +lossless: + healthy: + delay: + second: 20 +management: + endpoints: + web: + exposure: + include: + - polaris-discovery + - polaris-ratelimit + - polaris-config \ No newline at end of file diff --git a/spring-cloud-tencent-examples/lossless-example/pom.xml b/spring-cloud-tencent-examples/lossless-example/pom.xml new file mode 100644 index 000000000..ddd10b8a6 --- /dev/null +++ b/spring-cloud-tencent-examples/lossless-example/pom.xml @@ -0,0 +1,25 @@ + + + spring-cloud-tencent-examples + com.tencent.cloud + ${revision} + ../pom.xml + + + lossless-callee-service + lossless-nacos-callee-service + + 4.0.0 + + lossless-example + pom + Spring Cloud Starter Tencent Lossless Example + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + diff --git a/spring-cloud-tencent-examples/pom.xml b/spring-cloud-tencent-examples/pom.xml index 2d19adca7..0ff219d6b 100644 --- a/spring-cloud-tencent-examples/pom.xml +++ b/spring-cloud-tencent-examples/pom.xml @@ -22,6 +22,7 @@ polaris-router-featureenv-example polaris-config-data-example quickstart-example + lossless-example diff --git a/spring-cloud-tencent-plugin-starters/pom.xml b/spring-cloud-tencent-plugin-starters/pom.xml index bc2f153b9..0258ceeea 100644 --- a/spring-cloud-tencent-plugin-starters/pom.xml +++ b/spring-cloud-tencent-plugin-starters/pom.xml @@ -18,6 +18,7 @@ spring-cloud-tencent-featureenv-plugin spring-cloud-tencent-gateway-plugin spring-cloud-starter-tencent-discovery-adapter-plugin + spring-cloud-tencent-lossless-plugin diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml index a2928ae4f..157adbd6c 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml @@ -8,6 +8,18 @@ ${revision} ../pom.xml + + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + + 4.0.0 spring-cloud-starter-tencent-discovery-adapter-plugin @@ -29,8 +41,8 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery - 2021.0.5.0 - test + 2023.0.0.0-RC1 + true diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java index 594ba0976..6e9474c47 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/config/NacosDiscoveryAdapterAutoConfiguration.java @@ -19,8 +19,10 @@ package com.tencent.cloud.plugin.discovery.adapter.config; import com.tencent.cloud.plugin.discovery.adapter.transformer.NacosInstanceTransformer; +import com.tencent.cloud.plugin.discovery.adapter.transformer.NacosRegistrationTransformer; import com.tencent.cloud.polaris.router.config.LoadBalancerConfiguration; import com.tencent.cloud.rpc.enhancement.transformer.InstanceTransformer; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -47,4 +49,11 @@ public InstanceTransformer instanceTransformer() { return new NacosInstanceTransformer(); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(name = "com.alibaba.cloud.nacos.registry.NacosRegistration") + public RegistrationTransformer registrationTransformer() { + return new NacosRegistrationTransformer(); + } + } diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosInstanceTransformer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosInstanceTransformer.java index 2959e86df..7bb90d194 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosInstanceTransformer.java +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosInstanceTransformer.java @@ -44,6 +44,7 @@ public void transformCustom(DefaultInstance instance, ServiceInstance serviceIns ); String nacosInstanceId = serviceInstance.getMetadata().get("nacos.instanceId"); instance.setId(nacosInstanceId); + } } diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java new file mode 100644 index 000000000..553e829d4 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.plugin.discovery.adapter.transformer; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.registry.NacosRegistration; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.utils.StringUtils; + +import org.springframework.cloud.client.serviceregistry.Registration; + +public class NacosRegistrationTransformer implements RegistrationTransformer { + + @Override + public void transformCustom(DefaultInstance instance, Registration registration) { + if (registration instanceof NacosRegistration nacosRegistration) { + NacosDiscoveryProperties nacosDiscoveryProperties = nacosRegistration.getNacosDiscoveryProperties(); + String namespace = nacosDiscoveryProperties.getNamespace(); + if (StringUtils.isBlank(namespace)) { + namespace = "default"; + } + instance.setNamespace(namespace); + } + } + +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/pom.xml new file mode 100644 index 000000000..59a313be9 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/pom.xml @@ -0,0 +1,75 @@ + + + + spring-cloud-tencent-plugin-starters + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + spring-cloud-tencent-lossless-plugin + Spring Cloud Tencent Lossless Plugin + + + + + com.tencent.cloud + spring-cloud-tencent-polaris-context + + + + com.tencent.cloud + spring-cloud-tencent-commons + + + + com.tencent.polaris + lossless-register + + + + com.tencent.polaris + lossless-deregister + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-web + test + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + true + + + + com.tencent.polaris + polaris-test-mock-discovery + test + + + junit + junit + + + + + + + diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspect.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspect.java new file mode 100644 index 000000000..c9af94576 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspect.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless; + +import com.tencent.cloud.plugin.lossless.config.LosslessProperties; +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; +import com.tencent.polaris.api.pojo.BaseInstance; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; + +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +/** + * Intercept for of register and deregister. + * + * @author Shedfree Wu + */ +@Aspect +public class LosslessRegistryAspect { + + private ServiceRegistry serviceRegistry; + + private Registration registration; + + private LosslessProperties losslessProperties; + + private PolarisSDKContextManager polarisSDKContextManager; + + private RegistrationTransformer registrationTransformer; + + private PolarisContextProperties properties; + + public LosslessRegistryAspect(ServiceRegistry serviceRegistry, Registration registration, + PolarisContextProperties properties, LosslessProperties losslessProperties, + PolarisSDKContextManager polarisSDKContextManager, RegistrationTransformer registrationTransformer) { + this.serviceRegistry = serviceRegistry; + this.registration = registration; + this.losslessProperties = losslessProperties; + this.polarisSDKContextManager = polarisSDKContextManager; + this.registrationTransformer = registrationTransformer; + this.properties = properties; + } + + @Pointcut("execution(public * org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(..))") + public void registerPointcut() { + + } + + @Pointcut("execution(public * org.springframework.cloud.client.serviceregistry.ServiceRegistry.deregister(..))") + public void deregisterPointcut() { + + } + + @Around("registerPointcut()") + public Object invokeRegister(ProceedingJoinPoint joinPoint) throws Throwable { + + // web started, get port from registration + BaseInstance instance = SpringCloudLosslessActionProvider.getBaseInstance(registration, registrationTransformer); + + Runnable registerAction = () -> executeJoinPoint(joinPoint); + + SpringCloudLosslessActionProvider losslessActionProvider = + new SpringCloudLosslessActionProvider(serviceRegistry, registration, losslessProperties, registerAction); + + polarisSDKContextManager.getLosslessAPI().setLosslessActionProvider(instance, losslessActionProvider); + polarisSDKContextManager.getLosslessAPI().losslessRegister(instance); + // return void + return null; + } + + @Around("deregisterPointcut()") + public Object invokeDeregister(ProceedingJoinPoint joinPoint) throws Throwable { + return joinPoint.proceed(); + } + + public void executeJoinPoint(ProceedingJoinPoint joinPoint) { + try { + joinPoint.proceed(); + } + catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/SpringCloudLosslessActionProvider.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/SpringCloudLosslessActionProvider.java new file mode 100644 index 000000000..a97df5960 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/SpringCloudLosslessActionProvider.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.cloud.common.util.OkHttpUtil; +import com.tencent.cloud.plugin.lossless.config.LosslessProperties; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; +import com.tencent.polaris.api.plugin.lossless.InstanceProperties; +import com.tencent.polaris.api.plugin.lossless.LosslessActionProvider; +import com.tencent.polaris.api.pojo.BaseInstance; +import com.tencent.polaris.api.utils.StringUtils; + +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.http.HttpHeaders; + +/** + * LosslessActionProvider for Spring Cloud. + * + * @author Shedfree Wu + */ +public class SpringCloudLosslessActionProvider implements LosslessActionProvider { + private ServiceRegistry serviceRegistry; + + private LosslessProperties losslessProperties; + + private Runnable originalRegisterAction; + + private Registration registration; + + public SpringCloudLosslessActionProvider(ServiceRegistry serviceRegistry, Registration registration, + LosslessProperties losslessProperties, Runnable originalRegisterAction) { + this.serviceRegistry = serviceRegistry; + this.registration = registration; + this.losslessProperties = losslessProperties; + this.originalRegisterAction = originalRegisterAction; + } + + @Override + public String getName() { + return "spring-cloud"; + } + + @Override + public void doRegister(InstanceProperties instanceProperties) { + // use lambda to do original register + originalRegisterAction.run(); + } + + @Override + public void doDeregister() { + serviceRegistry.deregister(registration); + } + + /** + * Check whether health check is enable. + * @return true: register after passing doHealthCheck, false: register after delayRegisterInterval. + */ + @Override + public boolean isEnableHealthCheck() { + return StringUtils.isNotBlank(losslessProperties.getHealthCheckPath()); + } + + @Override + public boolean doHealthCheck() { + Map headers = new HashMap<>(1); + headers.put(HttpHeaders.USER_AGENT, "polaris"); + + return OkHttpUtil.checkUrl("localhost", registration.getPort(), + losslessProperties.getHealthCheckPath(), headers); + } + + public static BaseInstance getBaseInstance(Registration registration, RegistrationTransformer registrationTransformer) { + return registrationTransformer.transform(registration); + } + +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java new file mode 100644 index 000000000..8b2cffe49 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessAutoConfiguration.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.plugin.lossless.config; + +import com.tencent.cloud.plugin.lossless.LosslessRegistryAspect; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Autoconfiguration of lossless. + * + * @author Shedfree Wu + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled +@Import(LosslessPropertiesAutoConfiguration.class) +public class LosslessAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public LosslessRegistryAspect losslessRegistryAspect( + ServiceRegistry serviceRegistry, Registration registration, PolarisContextProperties properties, + LosslessProperties losslessProperties, PolarisSDKContextManager polarisSDKContextManager, + RegistrationTransformer registrationTransformer) { + return new LosslessRegistryAspect(serviceRegistry, registration, properties, losslessProperties, + polarisSDKContextManager, registrationTransformer); + } + +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifier.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifier.java new file mode 100644 index 000000000..f5049df76 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifier.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless.config; + +import java.util.Objects; + +import com.tencent.cloud.common.constant.OrderConstant.Modifier; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.config.provider.LosslessConfigImpl; + +/** + * Config modifier for lossless. + * + * @author Shedfree Wu + */ +public class LosslessConfigModifier implements PolarisConfigModifier { + + private final LosslessProperties losslessProperties; + + public LosslessConfigModifier(LosslessProperties losslessProperties) { + this.losslessProperties = losslessProperties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + if (losslessProperties.isEnabled()) { + LosslessConfigImpl losslessConfig = (LosslessConfigImpl) configuration.getProvider().getLossless(); + losslessConfig.setEnable(true); + losslessConfig.setPort(losslessProperties.getPort()); + if (Objects.nonNull(losslessProperties.getDelayRegisterInterval())) { + losslessConfig.setDelayRegisterInterval(losslessProperties.getDelayRegisterInterval()); + } + if (Objects.nonNull(losslessProperties.getHealthCheckInterval())) { + losslessConfig.setHealthCheckInterval(losslessProperties.getHealthCheckInterval()); + } + } + } + + @Override + public int getOrder() { + return Modifier.LOSSLESS_ORDER; + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessProperties.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessProperties.java new file mode 100644 index 000000000..3f2960883 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessProperties.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.plugin.lossless.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("spring.cloud.polaris.lossless") +public class LosslessProperties { + + private boolean enabled = true; + + private int port = 28080; + + private String healthCheckPath; + + private Long delayRegisterInterval; + + private Long healthCheckInterval; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getHealthCheckPath() { + return healthCheckPath; + } + + public void setHealthCheckPath(String healthCheckPath) { + this.healthCheckPath = healthCheckPath; + } + + public Long getDelayRegisterInterval() { + return delayRegisterInterval; + } + + public void setDelayRegisterInterval(Long delayRegisterInterval) { + this.delayRegisterInterval = delayRegisterInterval; + } + + public Long getHealthCheckInterval() { + return healthCheckInterval; + } + + public void setHealthCheckInterval(Long healthCheckInterval) { + this.healthCheckInterval = healthCheckInterval; + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesAutoConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesAutoConfiguration.java new file mode 100644 index 000000000..23826a37c --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.plugin.lossless.config; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Autoconfiguration of lossless properties. + * + * @author Shedfree Wu + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled +@EnableConfigurationProperties(LosslessProperties.class) +public class LosslessPropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public LosslessConfigModifier losslessConfigModifier(LosslessProperties losslessProperties) { + return new LosslessConfigModifier(losslessProperties); + } + +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesBootstrapConfiguration.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesBootstrapConfiguration.java new file mode 100644 index 000000000..dd047cabc --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/java/com/tencent/cloud/plugin/lossless/config/LosslessPropertiesBootstrapConfiguration.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + + +/** + * BootstrapConfiguration of lossless properties. + * + * @author Shedfree Wu + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty("spring.cloud.polaris.enabled") +@Import(LosslessPropertiesAutoConfiguration.class) +public class LosslessPropertiesBootstrapConfiguration { + +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 000000000..48a31c1cd --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,10 @@ +{ + "properties": [ + { + "name": "spring.cloud.polaris.lossless.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "the switch for lossless plugin." + } + ] +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..1308d2ebb --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ + com.tencent.cloud.plugin.lossless.config.LosslessPropertiesBootstrapConfiguration diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..b1326e228 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.tencent.cloud.plugin.lossless.config.LosslessAutoConfiguration \ No newline at end of file diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java new file mode 100644 index 000000000..0b0d847ff --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java @@ -0,0 +1,91 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless; + +import com.tencent.cloud.plugin.lossless.config.LosslessConfigModifier; +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.polaris.api.config.provider.LosslessConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link LosslessConfigModifier}. + * + * @author Shedfree Wu + */ +public class LosslessConfigModifierTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TestApplication.class)) + .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.enabled=true") + .withPropertyValues("spring.cloud.polaris.lossless.enabled=true") + .withPropertyValues("spring.cloud.polaris.lossless.port=20000") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=/xxx") + .withPropertyValues("spring.cloud.polaris.lossless.delayRegisterInterval=10") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckInterval=5") + .withPropertyValues("spring.application.name=test") + .withPropertyValues("spring.cloud.gateway.enabled=false"); + private final ApplicationContextRunner disabledContextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(TestApplication.class)) + .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.enabled=true") + .withPropertyValues("spring.cloud.polaris.lossless.enabled=false") + .withPropertyValues("spring.application.name=test") + .withPropertyValues("spring.cloud.gateway.enabled=false"); + + @BeforeEach + void setUp() { + PolarisSDKContextManager.innerDestroy(); + } + + @Test + void testModify() { + contextRunner.run(context -> { + PolarisSDKContextManager polarisSDKContextManager = context.getBean(PolarisSDKContextManager.class); + LosslessConfig losslessConfig = polarisSDKContextManager.getSDKContext(). + getConfig().getProvider().getLossless(); + assertThat(losslessConfig.getHost()).isEqualTo("0.0.0.0"); + assertThat(losslessConfig.getPort()).isEqualTo(20000); + assertThat(losslessConfig.getDelayRegisterInterval()).isEqualTo(10); + assertThat(losslessConfig.getHealthCheckInterval()).isEqualTo(5); + }); + } + + + @Test + void testDisabled() { + disabledContextRunner.run(context -> { + PolarisSDKContextManager polarisSDKContextManager = context.getBean(PolarisSDKContextManager.class); + LosslessConfig losslessConfig = polarisSDKContextManager.getSDKContext(). + getConfig().getProvider().getLossless(); + assertThat(losslessConfig.isEnable()).isFalse(); + }); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessPropertiesTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessPropertiesTest.java new file mode 100644 index 000000000..02e6f4087 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessPropertiesTest.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless; + +import com.tencent.cloud.plugin.lossless.config.LosslessProperties; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link LosslessProperties}. + * + * @author Shedfree Wu + */ +public class LosslessPropertiesTest { + + @Test + void testGetAndSet() { + LosslessProperties polarisStatProperties = new LosslessProperties(); + + // healthCheckPath + polarisStatProperties.setHealthCheckPath("/xxx"); + assertThat(polarisStatProperties.getHealthCheckPath()).isEqualTo("/xxx"); + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java new file mode 100644 index 000000000..45f656529 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java @@ -0,0 +1,191 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.lossless; + +import java.util.Collections; + +import com.tencent.cloud.common.util.OkHttpUtil; +import com.tencent.cloud.plugin.lossless.config.LosslessAutoConfiguration; +import com.tencent.cloud.plugin.lossless.config.LosslessPropertiesBootstrapConfiguration; +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; +import com.tencent.cloud.polaris.registry.PolarisRegistration; +import com.tencent.cloud.polaris.registry.PolarisServiceRegistry; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationUtils; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; + +/** + * Test for {@link LosslessRegistryAspect}. + * + * @author Shedfree Wu + */ +public class LosslessRegistryAspectTest { + + private static String NAMESPACE_TEST = "Test"; + + private static String SERVICE_PROVIDER = "java_provider_test"; + + private static String HOST = "127.0.0.1"; + + private static int APPLICATION_PORT = 19091; + + private static int LOSSLESS_PORT_1 = 28083; + + private static NamingServer namingServer; + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + LosslessAutoConfiguration.class, + LosslessPropertiesBootstrapConfiguration.class, + PolarisContextAutoConfiguration.class, + PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class)) + .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.lossless.delayRegisterInterval=5000") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=") + .withPropertyValues("spring.cloud.polaris.lossless.port=" + LOSSLESS_PORT_1) + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.localIpAddress=" + HOST) + .withPropertyValues("spring.cloud.polaris.localPort=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + + private final WebApplicationContextRunner contextRunner2 = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + LosslessAutoConfiguration.class, + LosslessPropertiesBootstrapConfiguration.class, + PolarisContextAutoConfiguration.class, + PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class)) + .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckInterval=1000") + .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=/test") + .withPropertyValues("spring.cloud.polaris.lossless.port=28082") + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.localIpAddress=" + HOST) + .withPropertyValues("spring.cloud.polaris.localPort=" + APPLICATION_PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues("spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + + @BeforeAll + static void beforeAll() throws Exception { + namingServer = NamingServer.startNamingServer(10081); + + // add service + namingServer.getNamingService().addService(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER)); + } + + @AfterAll + static void afterAll() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @BeforeEach + void setUp() { + PolarisSDKContextManager.innerDestroy(); + } + + @Test + public void testRegister() { + this.contextRunner.run(context -> { + + AbstractAutoServiceRegistration autoServiceRegistration = context.getBean(AbstractAutoServiceRegistration.class); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.register(autoServiceRegistration); + }).doesNotThrowAnyException(); + Thread.sleep(1000); + // before register online status is false + assertThatCode(() -> { + assertThat(OkHttpUtil.checkUrl(HOST, LOSSLESS_PORT_1, "/online", Collections.EMPTY_MAP)).isFalse(); + }).doesNotThrowAnyException(); + // delay register after 5s + Thread.sleep(5000); + PolarisServiceRegistry registry = context.getBean(PolarisServiceRegistry.class); + PolarisRegistration registration = context.getBean(PolarisRegistration.class); + + assertThatCode(() -> { + assertThat(registry.getStatus(registration)).isEqualTo("DOWN"); + }).doesNotThrowAnyException(); + + assertThatCode(() -> { + assertThat(OkHttpUtil.checkUrl(HOST, LOSSLESS_PORT_1, "/online", Collections.EMPTY_MAP)).isTrue(); + }).doesNotThrowAnyException(); + + assertThatCode(() -> { + assertThat(OkHttpUtil.checkUrl(HOST, LOSSLESS_PORT_1, "/offline", Collections.EMPTY_MAP)).isTrue(); + }).doesNotThrowAnyException(); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.deRegister(autoServiceRegistration); + }).doesNotThrowAnyException(); + + assertThatCode(() -> { + assertThat(registry.getStatus(registration)).isEqualTo("DOWN"); + }).doesNotThrowAnyException(); + }); + } + + @Test + public void testRegister2() { + this.contextRunner2.run(context -> { + + AbstractAutoServiceRegistration autoServiceRegistration = context.getBean(AbstractAutoServiceRegistration.class); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.register(autoServiceRegistration); + }).doesNotThrowAnyException(); + + Thread.sleep(2000); + + assertThatCode(() -> { + AutoServiceRegistrationUtils.deRegister(autoServiceRegistration); + }).doesNotThrowAnyException(); + }); + } + + @Configuration + @EnableAutoConfiguration + static class PolarisPropertiesConfiguration { + + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java new file mode 100644 index 000000000..1c8e09f91 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.cloud.client.serviceregistry; + +public class AutoServiceRegistrationUtils { + + public static void register(AbstractAutoServiceRegistration autoServiceRegistration) { + autoServiceRegistration.register(); + } + + public static void deRegister(AbstractAutoServiceRegistration autoServiceRegistration) { + autoServiceRegistration.deregister(); + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisSDKContextManager.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisSDKContextManager.java index e28a60770..143b6fbb7 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisSDKContextManager.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisSDKContextManager.java @@ -23,6 +23,7 @@ import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.api.control.Destroyable; import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.LosslessAPI; import com.tencent.polaris.api.core.ProviderAPI; import com.tencent.polaris.assembly.api.AssemblyAPI; import com.tencent.polaris.assembly.factory.AssemblyAPIFactory; @@ -55,6 +56,7 @@ public class PolarisSDKContextManager { private volatile static SDKContext sdkContext; private volatile static ProviderAPI providerAPI; private volatile static ConsumerAPI consumerAPI; + private volatile static LosslessAPI losslessAPI; private volatile static RouterAPI routerAPI; private volatile static CircuitBreakAPI circuitBreakAPI; private volatile static LimitAPI limitAPI; @@ -81,6 +83,12 @@ public static void innerDestroy() { providerAPI = null; } + // destroy LosslessAPI + if (Objects.nonNull(losslessAPI)) { + ((AutoCloseable) losslessAPI).close(); + losslessAPI = null; + } + // destroy ConsumerAPI if (Objects.nonNull(consumerAPI)) { ((AutoCloseable) consumerAPI).close(); @@ -135,6 +143,9 @@ public void init() { // init ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPIByContext(sdkContext); + // init losslessAPI + losslessAPI = DiscoveryAPIFactory.createLosslessAPIByContext(sdkContext); + // init ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); @@ -183,6 +194,11 @@ public ProviderAPI getProviderAPI() { return providerAPI; } + public LosslessAPI getLosslessAPI() { + init(); + return losslessAPI; + } + public ConsumerAPI getConsumerAPI() { init(); return consumerAPI; diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java index 3f458676c..e333451bd 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/config/RpcEnhancementAutoConfiguration.java @@ -37,6 +37,8 @@ import com.tencent.cloud.rpc.enhancement.scg.EnhancedGatewayGlobalFilter; import com.tencent.cloud.rpc.enhancement.transformer.InstanceTransformer; import com.tencent.cloud.rpc.enhancement.transformer.PolarisInstanceTransformer; +import com.tencent.cloud.rpc.enhancement.transformer.PolarisRegistrationTransformer; +import com.tencent.cloud.rpc.enhancement.transformer.RegistrationTransformer; import com.tencent.cloud.rpc.enhancement.webclient.EnhancedWebClientExchangeFilterFunction; import com.tencent.cloud.rpc.enhancement.webclient.PolarisLoadBalancerClientRequestTransformer; @@ -47,7 +49,6 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -82,11 +83,18 @@ public class RpcEnhancementAutoConfiguration { @Bean @ConditionalOnMissingBean - @ConditionalOnMissingClass("com.alibaba.cloud.nacos.NacosServiceInstance") + @ConditionalOnClass(name = "com.tencent.cloud.common.pojo.PolarisServiceInstance") public InstanceTransformer instanceTransformer() { return new PolarisInstanceTransformer(); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(name = "com.tencent.cloud.polaris.registry.PolarisRegistration") + public RegistrationTransformer registrationTransformer() { + return new PolarisRegistrationTransformer(); + } + @Bean @Lazy public EnhancedPluginRunner enhancedFeignPluginRunner( diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/PolarisRegistrationTransformer.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/PolarisRegistrationTransformer.java new file mode 100644 index 000000000..8c4000a76 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/PolarisRegistrationTransformer.java @@ -0,0 +1,25 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.rpc.enhancement.transformer; + +public class PolarisRegistrationTransformer implements RegistrationTransformer { + + + +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/RegistrationTransformer.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/RegistrationTransformer.java new file mode 100644 index 000000000..c79fe3401 --- /dev/null +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/transformer/RegistrationTransformer.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.rpc.enhancement.transformer; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.pojo.Instance; + +import org.springframework.cloud.client.serviceregistry.Registration; + + +/** + * RegistrationTransformer extensions to adapt 3rd registration to polaris instance. + * + * @author andrew shan + */ +public interface RegistrationTransformer { + + default Instance transform(Registration registration) { + DefaultInstance instance = new DefaultInstance(); + transformDefault(instance, registration); + transformCustom(instance, registration); + return instance; + } + + default void transformDefault(DefaultInstance instance, Registration registration) { + instance.setNamespace(MetadataContext.LOCAL_NAMESPACE); + instance.setService(registration.getServiceId()); + instance.setProtocol(registration.getScheme()); + instance.setId(registration.getInstanceId()); + instance.setHost(registration.getHost()); + instance.setPort(registration.getPort()); + instance.setMetadata(registration.getMetadata()); + } + + default void transformCustom(DefaultInstance instance, Registration registration) { + + } +} From aa6674fac469d6b1b861faca2689a7f94c6cd557 Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Mon, 15 Jul 2024 19:34:18 +0800 Subject: [PATCH 04/10] fix:feat: support lossless online/offline (#1338) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff5774879..35cc9cb0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,4 @@ - [fix:fix ApplicationContextAwareUtils NPE bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1294) - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1308) - [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1337) +- [fix:feat: support lossless online/offline](https://github.com/Tencent/spring-cloud-tencent/pull/1338) From 69309237f42d6f43285f3179968f83589b4c50b5 Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Mon, 15 Jul 2024 20:23:53 +0800 Subject: [PATCH 05/10] feat: support lossless online/offline (#1338) --- .../pom.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml index 157adbd6c..0053a3ee6 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml @@ -8,18 +8,6 @@ ${revision} ../pom.xml - - - - org.apache.maven.plugins - maven-compiler-plugin - - 16 - 16 - - - - 4.0.0 spring-cloud-starter-tencent-discovery-adapter-plugin From 37879d5aa22b801b3154f906b152a5fd69bbd61e Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Mon, 15 Jul 2024 20:24:18 +0800 Subject: [PATCH 06/10] feat: support lossless online/offline (#1338) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35cc9cb0a..16a84a781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,4 +5,4 @@ - [fix:fix ApplicationContextAwareUtils NPE bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1294) - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1308) - [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1337) -- [fix:feat: support lossless online/offline](https://github.com/Tencent/spring-cloud-tencent/pull/1338) +- [feat: support lossless online/offline](https://github.com/Tencent/spring-cloud-tencent/pull/1338) From deb54210ecbf4927306859afeba2f290f2e1397e Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Tue, 16 Jul 2024 10:50:11 +0800 Subject: [PATCH 07/10] feat: support lossless online/offline (#1338) --- .../adapter/transformer/NacosRegistrationTransformer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java index 553e829d4..23c327bf4 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/src/main/java/com/tencent/cloud/plugin/discovery/adapter/transformer/NacosRegistrationTransformer.java @@ -30,7 +30,8 @@ public class NacosRegistrationTransformer implements RegistrationTransformer { @Override public void transformCustom(DefaultInstance instance, Registration registration) { - if (registration instanceof NacosRegistration nacosRegistration) { + if (registration instanceof NacosRegistration) { + NacosRegistration nacosRegistration = (NacosRegistration) registration; NacosDiscoveryProperties nacosDiscoveryProperties = nacosRegistration.getNacosDiscoveryProperties(); String namespace = nacosDiscoveryProperties.getNamespace(); if (StringUtils.isBlank(namespace)) { From 7083c07740210e5e60cd43fd7ed6bb3a8e567328 Mon Sep 17 00:00:00 2001 From: fuyuwei01 Date: Tue, 16 Jul 2024 12:04:18 +0800 Subject: [PATCH 08/10] feat: support lossless online/offline (#1338) --- .../lossless-example/lossless-nacos-callee-service/pom.xml | 2 +- .../pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml index 2be4b3bc1..4039bf703 100644 --- a/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml +++ b/spring-cloud-tencent-examples/lossless-example/lossless-nacos-callee-service/pom.xml @@ -15,7 +15,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery - 2023.0.0.0-RC1 + 2021.0.5.0 diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml index 0053a3ee6..3e5b27d4b 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-discovery-adapter-plugin/pom.xml @@ -29,7 +29,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery - 2023.0.0.0-RC1 + 2021.0.5.0 true From 854b2823dbaa64ab4d4ec9d55384aaf09e4bef85 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Wed, 17 Apr 2024 16:15:11 +0800 Subject: [PATCH 09/10] feat: support lane router Co-authored-by: andrew shan <45474304+andrewshan@users.noreply.github.com> --- CHANGELOG.md | 1 + .../pom.xml | 24 +++ .../MetadataTransferAutoConfiguration.java | 69 +++---- .../DecodeTransferMetadataReactiveFilter.java | 32 ++- .../DecodeTransferMetadataServletFilter.java | 25 +-- ...codeTransferMedataFeignEnhancedPlugin.java | 140 ++++++++++++++ .../EncodeTransferMedataFeignInterceptor.java | 102 ---------- ...sferMedataRestTemplateEnhancedPlugin.java} | 63 +++--- ...ncodeTransferMedataScgEnhancedPlugin.java} | 62 +++--- ...ransferMedataWebClientEnhancedPlugin.java} | 58 ++++-- .../provider/ReactiveMetadataProvider.java | 71 +++++++ .../provider/ServletMetadataProvider.java | 71 +++++++ ...MetadataTransferAutoConfigurationTest.java | 51 ++--- ...odeTransferMetadataReactiveFilterTest.java | 5 +- ...codeTransferMetadataServletFilterTest.java | 4 +- ...odeTransferMedataFeignInterceptorTest.java | 6 +- ...sferMedataRestTemplateInterceptorTest.java | 53 ++++- .../EncodeTransferMedataScgFilterTest.java | 137 +++++++++---- ...codeTransferMedataWebClientFilterTest.java | 11 +- .../provider/MetadataProviderTest.java | 139 +++++++++++++ spring-cloud-tencent-commons/pom.xml | 9 + .../common/metadata/MetadataContext.java | 182 +++++++++++++----- .../metadata/MetadataContextHolder.java | 101 +++++----- .../cloud/common/util/ReflectionUtils.java | 19 ++ .../expresstion/ExpressionLabelUtils.java | 10 +- .../ServletExpressionLabelUtils.java | 8 +- .../SpringWebExpressionLabelUtils.java | 20 +- .../metadata/MetadataContextHolderTest.java | 5 +- spring-cloud-tencent-dependencies/pom.xml | 6 + spring-cloud-tencent-plugin-starters/pom.xml | 1 + .../pom.xml | 35 ++++ .../threadlocal/TaskExecutorWrapper.java | 45 +++++ .../threadlocal/TaskExecutorWrapperTest.java | 62 ++++++ .../feign/EnhancedFeignClient.java | 18 +- .../plugin/EnhancedPluginContext.java | 10 + .../plugin/PluginOrderConstant.java | 14 ++ .../EnhancedRestTemplateInterceptor.java | 1 + .../scg/EnhancedGatewayGlobalFilter.java | 47 +++-- ...hancedWebClientExchangeFilterFunction.java | 14 +- .../scg/EnhancedGatewayGlobalFilterTest.java | 23 +-- 40 files changed, 1273 insertions(+), 481 deletions(-) create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java delete mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java rename spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/{EncodeTransferMedataRestTemplateInterceptor.java => EncodeTransferMedataRestTemplateEnhancedPlugin.java} (60%) rename spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/{EncodeTransferMedataScgFilter.java => EncodeTransferMedataScgEnhancedPlugin.java} (58%) rename spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/{EncodeTransferMedataWebClientFilter.java => EncodeTransferMedataWebClientEnhancedPlugin.java} (57%) create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java create mode 100644 spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java create mode 100644 spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 16a84a781..4ebbf6b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,4 @@ - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1308) - [fix: fix RouterLabelRestTemplateInterceptor add response headers exception with httpclient5.](https://github.com/Tencent/spring-cloud-tencent/pull/1337) - [feat: support lossless online/offline](https://github.com/Tencent/spring-cloud-tencent/pull/1338) +- [feat: support lane router](https://github.com/Tencent/spring-cloud-tencent/pull/1339) diff --git a/spring-cloud-starter-tencent-metadata-transfer/pom.xml b/spring-cloud-starter-tencent-metadata-transfer/pom.xml index 642bbec1a..ca8350c50 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/pom.xml +++ b/spring-cloud-starter-tencent-metadata-transfer/pom.xml @@ -19,6 +19,12 @@ com.tencent.cloud spring-cloud-tencent-commons + + + com.tencent.cloud + spring-cloud-tencent-rpc-enhancement + + @@ -50,6 +56,24 @@ spring-cloud-starter-loadbalancer test + + + com.tencent.polaris + polaris-test-mock-discovery + test + + + + com.tencent.polaris + polaris-test-common + test + + + + org.mockito + mockito-inline + test + diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java index 2b2bd4cb9..53f9efccc 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java @@ -18,29 +18,21 @@ package com.tencent.cloud.metadata.config; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.metadata.core.DecodeTransferMetadataReactiveFilter; import com.tencent.cloud.metadata.core.DecodeTransferMetadataServletFilter; -import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor; -import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor; -import com.tencent.cloud.metadata.core.EncodeTransferMedataScgFilter; -import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientFilter; +import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin; +import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin; +import com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin; +import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; -import org.springframework.beans.factory.SmartInitializingSingleton; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.reactive.function.client.WebClient; import static javax.servlet.DispatcherType.ASYNC; import static javax.servlet.DispatcherType.ERROR; @@ -74,8 +66,8 @@ public FilterRegistrationBean metadataServl } @Bean - public DecodeTransferMetadataServletFilter metadataServletFilter() { - return new DecodeTransferMetadataServletFilter(); + public DecodeTransferMetadataServletFilter metadataServletFilter(PolarisContextProperties polarisContextProperties) { + return new DecodeTransferMetadataServletFilter(polarisContextProperties); } } @@ -87,8 +79,8 @@ public DecodeTransferMetadataServletFilter metadataServletFilter() { protected static class MetadataReactiveFilterConfig { @Bean - public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() { - return new DecodeTransferMetadataReactiveFilter(); + public DecodeTransferMetadataReactiveFilter metadataReactiveFilter(PolarisContextProperties polarisContextProperties) { + return new DecodeTransferMetadataReactiveFilter(polarisContextProperties); } } @@ -97,11 +89,12 @@ public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() { */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.GlobalFilter") + @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true) protected static class MetadataTransferScgFilterConfig { @Bean - public GlobalFilter encodeTransferMedataScgFilter() { - return new EncodeTransferMedataScgFilter(); + public EncodeTransferMedataScgEnhancedPlugin encodeTransferMedataScgEnhancedPlugin() { + return new EncodeTransferMedataScgEnhancedPlugin(); } } @@ -110,11 +103,12 @@ public GlobalFilter encodeTransferMedataScgFilter() { */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "feign.Feign") + @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true) protected static class MetadataTransferFeignInterceptorConfig { @Bean - public EncodeTransferMedataFeignInterceptor encodeTransferMedataFeignInterceptor() { - return new EncodeTransferMedataFeignInterceptor(); + public EncodeTransferMedataFeignEnhancedPlugin encodeTransferMedataFeignEnhancedPlugin() { + return new EncodeTransferMedataFeignEnhancedPlugin(); } } @@ -123,23 +117,12 @@ public EncodeTransferMedataFeignInterceptor encodeTransferMedataFeignInterceptor */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") + @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true) protected static class MetadataTransferRestTemplateConfig { - @Autowired(required = false) - private List restTemplates = Collections.emptyList(); - - @Bean - public EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor() { - return new EncodeTransferMedataRestTemplateInterceptor(); - } - @Bean - public SmartInitializingSingleton addEncodeTransferMetadataInterceptorForRestTemplate(EncodeTransferMedataRestTemplateInterceptor interceptor) { - return () -> restTemplates.forEach(restTemplate -> { - List list = new ArrayList<>(restTemplate.getInterceptors()); - list.add(interceptor); - restTemplate.setInterceptors(list); - }); + public EncodeTransferMedataRestTemplateEnhancedPlugin encodeTransferMedataRestTemplateEnhancedPlugin() { + return new EncodeTransferMedataRestTemplateEnhancedPlugin(); } } @@ -148,20 +131,12 @@ public SmartInitializingSingleton addEncodeTransferMetadataInterceptorForRestTem */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.springframework.web.reactive.function.client.WebClient") + @ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true) protected static class MetadataTransferWebClientConfig { - @Autowired(required = false) - private List webClientBuilder = Collections.emptyList(); - - @Bean - public EncodeTransferMedataWebClientFilter encodeTransferMedataWebClientFilter() { - return new EncodeTransferMedataWebClientFilter(); - } @Bean - public SmartInitializingSingleton addEncodeTransferMetadataFilterForWebClient(EncodeTransferMedataWebClientFilter filter) { - return () -> webClientBuilder.forEach(webClient -> { - webClient.filter(filter); - }); + public EncodeTransferMedataWebClientEnhancedPlugin encodeTransferMedataWebClientEnhancedPlugin() { + return new EncodeTransferMedataWebClientEnhancedPlugin(); } } } 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 a1bf7a86e..44a8e881b 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 @@ -18,8 +18,6 @@ package com.tencent.cloud.metadata.core; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; @@ -27,6 +25,9 @@ import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.metadata.provider.ReactiveMetadataProvider; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; @@ -34,12 +35,10 @@ import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; @@ -51,8 +50,14 @@ */ public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered { + private PolarisContextProperties polarisContextProperties; + private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataReactiveFilter.class); + public DecodeTransferMetadataReactiveFilter(PolarisContextProperties polarisContextProperties) { + this.polarisContextProperties = polarisContextProperties; + } + @Override public int getOrder() { return OrderConstant.Server.Reactive.DECODE_TRANSFER_METADATA_FILTER_ORDER; @@ -62,19 +67,16 @@ public int getOrder() { public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) { // Get metadata string from http header. ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest(); - Map internalTransitiveMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_METADATA); Map customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange); Map mergedTransitiveMetadata = new HashMap<>(); mergedTransitiveMetadata.putAll(internalTransitiveMetadata); mergedTransitiveMetadata.putAll(customTransitiveMetadata); - Map internalDisposableMetadata = getIntervalMetadata(serverHttpRequest, CUSTOM_DISPOSABLE_METADATA); Map mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata); - - MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata); - + ReactiveMetadataProvider metadataProvider = new ReactiveMetadataProvider(serverHttpRequest, polarisContextProperties.getLocalIpAddress()); + MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider); // Save to ServerWebExchange. serverWebExchange.getAttributes().put( MetadataConstant.HeaderName.METADATA_CONTEXT, @@ -82,20 +84,14 @@ public Mono filter(ServerWebExchange serverWebExchange, WebFilterChain web TransHeadersTransfer.transfer(serverHttpRequest); return webFilterChain.filter(serverWebExchange) + .doOnError(throwable -> LOG.error("handle metadata[{}] error.", + MetadataContextHolder.get(), throwable)) .doFinally((type) -> MetadataContextHolder.remove()); } private Map getIntervalMetadata(ServerHttpRequest serverHttpRequest, String headerName) { HttpHeaders httpHeaders = serverHttpRequest.getHeaders(); - String customMetadataStr = httpHeaders.getFirst(headerName); - try { - if (StringUtils.hasText(customMetadataStr)) { - customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8); - } - } - catch (UnsupportedEncodingException e) { - LOG.error("Runtime system does not support utf-8 coding.", e); - } + String customMetadataStr = UrlUtils.decode(httpHeaders.getFirst(headerName)); LOG.debug("Get upstream metadata string: {}", customMetadataStr); return JacksonUtils.deserialize2Map(customMetadataStr); diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java index e1d26efcc..72bd4f2d9 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java @@ -32,6 +32,10 @@ import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.metadata.provider.ServletMetadataProvider; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +59,12 @@ public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { private static final Logger LOG = LoggerFactory.getLogger(DecodeTransferMetadataServletFilter.class); + private PolarisContextProperties polarisContextProperties; + + public DecodeTransferMetadataServletFilter(PolarisContextProperties polarisContextProperties) { + this.polarisContextProperties = polarisContextProperties; + } + @Override protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, FilterChain filterChain) @@ -65,11 +75,10 @@ protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest, Map mergedTransitiveMetadata = new HashMap<>(); mergedTransitiveMetadata.putAll(internalTransitiveMetadata); mergedTransitiveMetadata.putAll(customTransitiveMetadata); - Map internalDisposableMetadata = getInternalMetadata(httpServletRequest, CUSTOM_DISPOSABLE_METADATA); Map mergedDisposableMetadata = new HashMap<>(internalDisposableMetadata); - - MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata); + ServletMetadataProvider metadataProvider = new ServletMetadataProvider(httpServletRequest, polarisContextProperties.getLocalIpAddress()); + MetadataContextHolder.init(mergedTransitiveMetadata, mergedDisposableMetadata, metadataProvider); TransHeadersTransfer.transfer(httpServletRequest); try { @@ -83,15 +92,7 @@ protected void doFilterInternal(@NonNull HttpServletRequest httpServletRequest, private Map getInternalMetadata(HttpServletRequest httpServletRequest, String headerName) { // Get custom metadata string from http header. - String customMetadataStr = httpServletRequest.getHeader(headerName); - try { - if (StringUtils.hasText(customMetadataStr)) { - customMetadataStr = URLDecoder.decode(customMetadataStr, UTF_8); - } - } - catch (UnsupportedEncodingException e) { - LOG.error("Runtime system does not support utf-8 coding.", e); - } + String customMetadataStr = UrlUtils.decode(httpServletRequest.getHeader(headerName)); LOG.debug("Get upstream metadata string: {}", customMetadataStr); // create custom metadata. diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java new file mode 100644 index 000000000..ff722edc6 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignEnhancedPlugin.java @@ -0,0 +1,140 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.metadata.core; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; +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.ReflectionUtils; +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataType; +import feign.Request; + +import org.springframework.util.CollectionUtils; + +import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; +import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; + +/** + * Pre EnhancedPlugin for feign to encode transfer metadata. + * + * @author Shedfree Wu + */ +public class EncodeTransferMedataFeignEnhancedPlugin implements EnhancedPlugin { + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; + } + + @Override + public void run(EnhancedPluginContext context) throws Throwable { + if (!(context.getOriginRequest() instanceof Request)) { + return; + } + Request request = (Request) context.getOriginRequest(); + + // get metadata of current thread + MetadataContext metadataContext = MetadataContextHolder.get(); + Map customMetadata = metadataContext.getCustomMetadata(); + Map disposableMetadata = metadataContext.getDisposableMetadata(); + Map transHeaders = metadataContext.getTransHeadersKV(); + + MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); + Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); + // currently only support transitive header from calleeMessageMetadataContainer + this.buildHeaderMap(request, calleeTransitiveHeaders); + + // build custom disposable metadata request header + this.buildMetadataHeader(request, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); + + // process custom metadata + this.buildMetadataHeader(request, customMetadata, CUSTOM_METADATA); + + // set headers that need to be transmitted from the upstream + this.buildTransmittedHeader(request, transHeaders); + } + + private void buildTransmittedHeader(Request request, Map transHeaders) { + if (!CollectionUtils.isEmpty(transHeaders)) { + Map> headers = getModifiableHeaders(request); + transHeaders.entrySet().stream().forEach(entry -> { + headers.remove(entry.getKey()); + headers.put(entry.getKey(), Arrays.asList(entry.getValue())); + }); + } + } + + /** + * Set metadata into the request header for {@link Request} . + * @param request instance of {@link Request} + * @param metadata metadata map . + * @param headerName target metadata http header name . + */ + private void buildMetadataHeader(Request request, Map metadata, String headerName) { + if (!CollectionUtils.isEmpty(metadata)) { + buildHeaderMap(request, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata))); + } + } + + + /** + * Set headerMap into the request header for {@link Request} . + * @param request instance of {@link Request} + * @param headerMap header map . + */ + private void buildHeaderMap(Request request, Map headerMap) { + if (!CollectionUtils.isEmpty(headerMap)) { + Map> headers = getModifiableHeaders(request); + headerMap.forEach((key, value) -> headers.put(key, Arrays.asList(UrlUtils.encode(value)))); + } + } + + /** + * The value obtained directly from the headers method is an unmodifiable map. + * If the Feign client uses the URL, the original headers are unmodifiable. + * @param request feign request + * @return modifiable headers + */ + private Map> getModifiableHeaders(Request request) { + Map> headers; + headers = (Map>) ReflectionUtils.getFieldValue(request, "headers"); + + if (!(headers instanceof LinkedHashMap)) { + headers = new LinkedHashMap<>(headers); + ReflectionUtils.setFieldValue(request, "headers", headers); + } + return headers; + } + + @Override + public int getOrder() { + return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER; + } +} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java deleted file mode 100644 index edbf5f0bc..000000000 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - */ - -package com.tencent.cloud.metadata.core; - -import java.io.UnsupportedEncodingException; -import java.util.Map; - -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 feign.RequestInterceptor; -import feign.RequestTemplate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.springframework.core.Ordered; -import org.springframework.util.CollectionUtils; -import org.springframework.web.client.RestTemplate; - -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; -import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; -import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; -import static java.net.URLEncoder.encode; - -/** - * Interceptor used for adding the metadata in http headers from context when web client - * is Feign. - * - * @author Haotian Zhang - */ -public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Ordered { - - private static final Logger LOG = LoggerFactory.getLogger(EncodeTransferMedataFeignInterceptor.class); - - @Override - public int getOrder() { - return OrderConstant.Client.Feign.ENCODE_TRANSFER_METADATA_INTERCEPTOR_ORDER; - } - - @Override - public void apply(RequestTemplate requestTemplate) { - // get metadata of current thread - MetadataContext metadataContext = MetadataContextHolder.get(); - Map customMetadata = metadataContext.getCustomMetadata(); - Map disposableMetadata = metadataContext.getDisposableMetadata(); - Map transHeaders = metadataContext.getTransHeadersKV(); - - this.buildMetadataHeader(requestTemplate, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); - - // process custom metadata - this.buildMetadataHeader(requestTemplate, customMetadata, CUSTOM_METADATA); - - // set headers that need to be transmitted from the upstream - this.buildTransmittedHeader(requestTemplate, transHeaders); - } - - private void buildTransmittedHeader(RequestTemplate requestTemplate, Map transHeaders) { - if (!CollectionUtils.isEmpty(transHeaders)) { - transHeaders.entrySet().stream().forEach(entry -> { - requestTemplate.removeHeader(entry.getKey()); - requestTemplate.header(entry.getKey(), entry.getValue()); - }); - } - } - - /** - * Set metadata into the request header for {@link RestTemplate} . - * @param requestTemplate instance of {@link RestTemplate} - * @param metadata metadata map . - * @param headerName target metadata http header name . - */ - private void buildMetadataHeader(RequestTemplate requestTemplate, Map metadata, String headerName) { - if (!CollectionUtils.isEmpty(metadata)) { - String encodedMetadata = JacksonUtils.serialize2Json(metadata); - requestTemplate.removeHeader(headerName); - try { - requestTemplate.header(headerName, encode(encodedMetadata, UTF_8)); - } - catch (UnsupportedEncodingException e) { - LOG.error("Set header failed.", e); - requestTemplate.header(headerName, encodedMetadata); - } - } - } -} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java similarity index 60% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java index cbf4901b3..50e42885b 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateEnhancedPlugin.java @@ -18,49 +18,53 @@ package com.tencent.cloud.metadata.core; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.Map; -import com.tencent.cloud.common.constant.OrderConstant; +import com.google.common.collect.ImmutableMap; 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; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataType; -import org.springframework.core.Ordered; import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.lang.NonNull; import org.springframework.util.CollectionUtils; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; /** - * Interceptor used for adding the metadata in http headers from context when web client - * is RestTemplate. + * Pre EnhancedPlugin for rest template to encode transfer metadata. * - * @author Haotian Zhang + * @author Shedfree Wu */ -public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered { - +public class EncodeTransferMedataRestTemplateEnhancedPlugin implements EnhancedPlugin { @Override - public int getOrder() { - return OrderConstant.Client.RestTemplate.ENCODE_TRANSFER_METADATA_INTERCEPTOR_ORDER; + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; } @Override - public ClientHttpResponse intercept(@NonNull HttpRequest httpRequest, @NonNull byte[] bytes, - @NonNull ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { + public void run(EnhancedPluginContext context) throws Throwable { + if (!(context.getOriginRequest() instanceof HttpRequest)) { + return; + } + HttpRequest httpRequest = (HttpRequest) context.getOriginRequest(); + // get metadata of current thread MetadataContext metadataContext = MetadataContextHolder.get(); Map customMetadata = metadataContext.getCustomMetadata(); Map disposableMetadata = metadataContext.getDisposableMetadata(); Map transHeaders = metadataContext.getTransHeadersKV(); + MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); + Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); + // currently only support transitive header from calleeMessageMetadataContainer + this.buildHeaderMap(httpRequest, calleeTransitiveHeaders); // build custom disposable metadata request header this.buildMetadataHeader(httpRequest, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); @@ -70,8 +74,6 @@ public ClientHttpResponse intercept(@NonNull HttpRequest httpRequest, @NonNull b // set headers that need to be transmitted from the upstream this.buildTransmittedHeader(httpRequest, transHeaders); - - return clientHttpRequestExecution.execute(httpRequest, bytes); } private void buildTransmittedHeader(HttpRequest request, Map transHeaders) { @@ -82,6 +84,12 @@ private void buildTransmittedHeader(HttpRequest request, Map tra } } + private void buildHeaderMap(HttpRequest request, Map headerMap) { + if (!CollectionUtils.isEmpty(headerMap)) { + headerMap.forEach((key, value) -> request.getHeaders().set(key, UrlUtils.encode(value))); + } + } + /** * Set metadata into the request header for {@link HttpRequest} . * @@ -91,13 +99,12 @@ private void buildTransmittedHeader(HttpRequest request, Map tra */ private void buildMetadataHeader(HttpRequest request, Map metadata, String headerName) { if (!CollectionUtils.isEmpty(metadata)) { - String encodedMetadata = JacksonUtils.serialize2Json(metadata); - try { - request.getHeaders().set(headerName, URLEncoder.encode(encodedMetadata, UTF_8)); - } - catch (UnsupportedEncodingException e) { - request.getHeaders().set(headerName, encodedMetadata); - } + buildHeaderMap(request, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata))); } } + + @Override + public int getOrder() { + return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER; + } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java similarity index 58% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java index 2eacd21a7..8c996bbf1 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgEnhancedPlugin.java @@ -18,42 +18,46 @@ package com.tencent.cloud.metadata.core; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.Map; +import com.google.common.collect.ImmutableMap; 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 reactor.core.publisher.Mono; +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataType; -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 com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; /** - * Scg filter used for writing metadata in HTTP request header. + * Pre EnhancedPlugin for scg to encode transfer metadata. * - * @author Haotian Zhang + * @author Shedfree Wu */ -public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered { - +public class EncodeTransferMedataScgEnhancedPlugin implements EnhancedPlugin { @Override - public int getOrder() { - return OrderConstant.Client.Scg.ENCODE_TRANSFER_METADATA_FILTER_ORDER; + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; } @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + public void run(EnhancedPluginContext context) throws Throwable { + if (!(context.getOriginRequest() instanceof ServerWebExchange)) { + return; + } + ServerWebExchange exchange = (ServerWebExchange) context.getOriginRequest(); + // get request builder ServerHttpRequest.Builder builder = exchange.getRequest().mutate(); @@ -66,11 +70,22 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { Map customMetadata = metadataContext.getCustomMetadata(); Map disposableMetadata = metadataContext.getDisposableMetadata(); + MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); + Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); + // currently only support transitive header from calleeMessageMetadataContainer + this.buildHeaderMap(builder, calleeTransitiveHeaders); + this.buildMetadataHeader(builder, customMetadata, CUSTOM_METADATA); this.buildMetadataHeader(builder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); - TransHeadersTransfer.transfer(exchange.getRequest()); - return chain.filter(exchange.mutate().request(builder.build()).build()); + + context.setOriginRequest(exchange.mutate().request(builder.build()).build()); + } + + private void buildHeaderMap(ServerHttpRequest.Builder builder, Map headerMap) { + if (!CollectionUtils.isEmpty(headerMap)) { + headerMap.forEach((key, value) -> builder.header(key, UrlUtils.encode(value))); + } } /** @@ -81,13 +96,12 @@ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { */ private void buildMetadataHeader(ServerHttpRequest.Builder builder, Map metadata, String headerName) { if (!CollectionUtils.isEmpty(metadata)) { - String encodedMetadata = JacksonUtils.serialize2Json(metadata); - try { - builder.header(headerName, URLEncoder.encode(encodedMetadata, UTF_8)); - } - catch (UnsupportedEncodingException e) { - builder.header(headerName, encodedMetadata); - } + buildHeaderMap(builder, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata))); } } + + @Override + public int getOrder() { + return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER; + } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java similarity index 57% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java index 3547add0e..2967a2b4c 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientEnhancedPlugin.java @@ -18,48 +18,61 @@ package com.tencent.cloud.metadata.core; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.Map; +import com.google.common.collect.ImmutableMap; import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.util.JacksonUtils; -import reactor.core.publisher.Mono; +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPlugin; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; +import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; +import com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataType; import org.springframework.util.CollectionUtils; import org.springframework.web.reactive.function.client.ClientRequest; -import org.springframework.web.reactive.function.client.ClientResponse; -import org.springframework.web.reactive.function.client.ExchangeFilterFunction; -import org.springframework.web.reactive.function.client.ExchangeFunction; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; /** - * web client filter used for writing metadata in HTTP request header. + * Pre EnhancedPlugin for web client to encode transfer metadata. * - * @author sean yu + * @author Shedfree Wu */ -public class EncodeTransferMedataWebClientFilter implements ExchangeFilterFunction { +public class EncodeTransferMedataWebClientEnhancedPlugin implements EnhancedPlugin { + @Override + public EnhancedPluginType getType() { + return EnhancedPluginType.Client.PRE; + } @Override - public Mono filter(ClientRequest clientRequest, ExchangeFunction next) { + public void run(EnhancedPluginContext context) throws Throwable { + if (!(context.getOriginRequest() instanceof ClientRequest)) { + return; + } + ClientRequest clientRequest = (ClientRequest) context.getOriginRequest(); + MetadataContext metadataContext = MetadataContextHolder.get(); Map customMetadata = metadataContext.getCustomMetadata(); Map disposableMetadata = metadataContext.getDisposableMetadata(); Map transHeaders = metadataContext.getTransHeadersKV(); + MessageMetadataContainer calleeMessageMetadataContainer = metadataContext.getMetadataContainer(MetadataType.MESSAGE, false); + Map calleeTransitiveHeaders = calleeMessageMetadataContainer.getTransitiveHeaders(); ClientRequest.Builder requestBuilder = ClientRequest.from(clientRequest); + // currently only support transitive header from calleeMessageMetadataContainer + this.buildHeaderMap(requestBuilder, calleeTransitiveHeaders); + this.buildMetadataHeader(requestBuilder, customMetadata, CUSTOM_METADATA); this.buildMetadataHeader(requestBuilder, disposableMetadata, CUSTOM_DISPOSABLE_METADATA); this.buildTransmittedHeader(requestBuilder, transHeaders); - ClientRequest request = requestBuilder.build(); - - return next.exchange(request); + context.setOriginRequest(requestBuilder.build()); } private void buildTransmittedHeader(ClientRequest.Builder requestBuilder, Map transHeaders) { @@ -68,6 +81,11 @@ private void buildTransmittedHeader(ClientRequest.Builder requestBuilder, Map headerMap) { + if (!CollectionUtils.isEmpty(headerMap)) { + headerMap.forEach((key, value) -> requestBuilder.header(key, UrlUtils.encode(value))); + } + } /** * Set metadata into the request header for {@link ClientRequest} . @@ -77,14 +95,12 @@ private void buildTransmittedHeader(ClientRequest.Builder requestBuilder, Map metadata, String headerName) { if (!CollectionUtils.isEmpty(metadata)) { - String encodedMetadata = JacksonUtils.serialize2Json(metadata); - try { - requestBuilder.header(headerName, URLEncoder.encode(encodedMetadata, UTF_8)); - } - catch (UnsupportedEncodingException e) { - requestBuilder.header(headerName, encodedMetadata); - } + buildHeaderMap(requestBuilder, ImmutableMap.of(headerName, JacksonUtils.serialize2Json(metadata))); } } + @Override + public int getOrder() { + return PluginOrderConstant.ClientPluginOrder.CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER; + } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java new file mode 100644 index 000000000..d39d74ed8 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ReactiveMetadataProvider.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.metadata.provider; + +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.common.util.expresstion.SpringWebExpressionLabelUtils; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataProvider; + +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * MetadataProvider used for Reactive. + * + * @author Shedfree Wu + */ +public class ReactiveMetadataProvider implements MetadataProvider { + + private ServerHttpRequest serverHttpRequest; + + private String callerIp; + + public ReactiveMetadataProvider(ServerHttpRequest serverHttpRequest, String callerIp) { + this.serverHttpRequest = serverHttpRequest; + this.callerIp = callerIp; + } + + @Override + public String getRawMetadataStringValue(String key) { + switch (key) { + case MessageMetadataContainer.LABEL_KEY_METHOD: + return serverHttpRequest.getMethod().name(); + case MessageMetadataContainer.LABEL_KEY_PATH: + return UrlUtils.decode(serverHttpRequest.getPath().toString()); + case MessageMetadataContainer.LABEL_KEY_CALLER_IP: + return callerIp; + default: + return null; + } + } + + @Override + public String getRawMetadataMapValue(String key, String mapKey) { + switch (key) { + case MessageMetadataContainer.LABEL_MAP_KEY_HEADER: + return UrlUtils.decode(SpringWebExpressionLabelUtils.getHeaderValue(serverHttpRequest, mapKey, null)); + case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE: + return UrlUtils.decode(SpringWebExpressionLabelUtils.getCookieValue(serverHttpRequest, mapKey, null)); + case MessageMetadataContainer.LABEL_MAP_KEY_QUERY: + return UrlUtils.decode(SpringWebExpressionLabelUtils.getQueryValue(serverHttpRequest, mapKey, null)); + default: + return null; + } + } +} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java new file mode 100644 index 000000000..f3c404ad2 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.metadata.provider; + +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils; +import com.tencent.cloud.common.util.expresstion.ServletExpressionLabelUtils; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataProvider; +import javax.servlet.http.HttpServletRequest; + +/** + * MetadataProvider used for Servlet. + * + * @author Shedfree Wu + */ +public class ServletMetadataProvider implements MetadataProvider { + + private HttpServletRequest httpServletRequest; + + private String callerIp; + + public ServletMetadataProvider(HttpServletRequest httpServletRequest, String callerIp) { + this.httpServletRequest = httpServletRequest; + this.callerIp = callerIp; + } + + @Override + public String getRawMetadataStringValue(String key) { + switch (key) { + case MessageMetadataContainer.LABEL_KEY_METHOD: + return httpServletRequest.getMethod(); + case MessageMetadataContainer.LABEL_KEY_PATH: + return UrlUtils.decode(httpServletRequest.getRequestURI()); + case MessageMetadataContainer.LABEL_KEY_CALLER_IP: + return callerIp; + default: + return null; + } + } + + @Override + public String getRawMetadataMapValue(String key, String mapKey) { + switch (key) { + case MessageMetadataContainer.LABEL_MAP_KEY_HEADER: + return UrlUtils.decode(httpServletRequest.getHeader(mapKey)); + case MessageMetadataContainer.LABEL_MAP_KEY_COOKIE: + return UrlUtils.decode(ServletExpressionLabelUtils.getCookieValue(httpServletRequest.getCookies(), mapKey, null)); + case MessageMetadataContainer.LABEL_MAP_KEY_QUERY: + return UrlUtils.decode(ExpressionLabelUtils.getQueryValue(httpServletRequest.getQueryString(), mapKey, null)); + default: + return null; + } + } +} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java index 12073bc9e..4da4dc8d3 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java @@ -18,24 +18,18 @@ package com.tencent.cloud.metadata.config; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor; -import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor; -import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientFilter; +import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin; +import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin; +import com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -48,6 +42,7 @@ public class MetadataTransferAutoConfigurationTest { private final ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(); + private final ReactiveWebApplicationContextRunner reactiveWebApplicationContextRunner = new ReactiveWebApplicationContextRunner(); /** * No any web application. @@ -57,36 +52,26 @@ public void test1() { this.applicationContextRunner.withConfiguration(AutoConfigurations.of(MetadataTransferAutoConfiguration.class)) .run(context -> { assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferFeignInterceptorConfig.class); - assertThat(context).hasSingleBean(EncodeTransferMedataFeignInterceptor.class); + assertThat(context).hasSingleBean(EncodeTransferMedataFeignEnhancedPlugin.class); + assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateEnhancedPlugin.class); assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.class); - assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateInterceptor.class); assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferScgFilterConfig.class); - assertThat(context).hasSingleBean(GlobalFilter.class); - assertThat(context).hasSingleBean(EncodeTransferMedataWebClientFilter.class); }); } - + /** + * Reactive web application. + */ @Test public void test2() { - this.applicationContextRunner - .withConfiguration( - AutoConfigurations.of(MetadataTransferAutoConfiguration.class, RestTemplateConfiguration.class)) + this.reactiveWebApplicationContextRunner.withConfiguration(AutoConfigurations.of(MetadataTransferAutoConfiguration.class, PolarisContextProperties.class)) .run(context -> { - assertThat(context).hasSingleBean(EncodeTransferMedataFeignInterceptor.class); - EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor = context.getBean(EncodeTransferMedataRestTemplateInterceptor.class); - Map restTemplateMap = context.getBeansOfType(RestTemplate.class); - assertThat(restTemplateMap.size()).isEqualTo(2); - for (String beanName : Arrays.asList("restTemplate", "loadBalancedRestTemplate")) { - RestTemplate restTemplate = restTemplateMap.get(beanName); - assertThat(restTemplate).isNotNull(); - List encodeTransferMedataFeignInterceptorList = restTemplate.getInterceptors() - .stream() - .filter(interceptor -> Objects.equals(interceptor, encodeTransferMedataRestTemplateInterceptor)) - .collect(Collectors.toList()); - //EncodeTransferMetadataFeignInterceptor is not added repeatedly - assertThat(encodeTransferMedataFeignInterceptorList.size()).isEqualTo(1); - } + assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferFeignInterceptorConfig.class); + assertThat(context).hasSingleBean(EncodeTransferMedataFeignEnhancedPlugin.class); + assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.class); + assertThat(context).hasSingleBean(EncodeTransferMedataRestTemplateEnhancedPlugin.class); + assertThat(context).hasSingleBean(MetadataTransferAutoConfiguration.MetadataTransferScgFilterConfig.class); + assertThat(context).hasSingleBean(EncodeTransferMedataWebClientEnhancedPlugin.class); }); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java index 18886a917..ff27c0c4d 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java @@ -20,6 +20,7 @@ import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.constant.OrderConstant; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -44,7 +45,7 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = MOCK, classes = DecodeTransferMetadataServletFilterTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml"}) + properties = {"spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive"}) public class DecodeTransferMetadataReactiveFilterTest { @Autowired @@ -54,7 +55,7 @@ public class DecodeTransferMetadataReactiveFilterTest { @BeforeEach public void setUp() { - this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(); + this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(new PolarisContextProperties()); } @Test diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java index 900e82ed0..0107a8f98 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java @@ -45,7 +45,9 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = DecodeTransferMetadataServletFilterTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml"}) + properties = {"spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = servlet", + "spring.cloud.gateway.enabled = false"}) public class DecodeTransferMetadataServletFilterTest { @Autowired diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java index 93e553787..e34f4e9ae 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java @@ -40,14 +40,16 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; /** - * Test for {@link EncodeTransferMedataFeignInterceptor}. + * Test for {@link EncodeTransferMedataFeignEnhancedPlugin}. * * @author Haotian Zhang */ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = DEFINED_PORT, classes = EncodeTransferMedataFeignInterceptorTest.TestApplication.class, - properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml"}) + properties = {"server.port=48081", "spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = servlet", + "spring.cloud.gateway.enabled = false"}) public class EncodeTransferMedataFeignInterceptorTest { @Autowired diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java index 95f0f32a1..b0ce9217c 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java @@ -17,8 +17,14 @@ package com.tencent.cloud.metadata.core; +import java.net.URI; +import java.util.Arrays; +import java.util.Map; + import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateInterceptor; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -26,6 +32,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -39,14 +46,15 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * Test for {@link EncodeTransferMedataRestTemplateInterceptor}. + * Test for {@link EncodeTransferMedataRestTemplateEnhancedPlugin}. * * @author Haotian Zhang */ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = EncodeTransferMedataRestTemplateInterceptorTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml"}) + properties = {"spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = reactive"}) public class EncodeTransferMedataRestTemplateInterceptorTest { @Autowired @@ -71,7 +79,13 @@ protected static class TestApplication { @Bean public RestTemplate restTemplate() { - return new RestTemplate(); + + EncodeTransferMedataRestTemplateEnhancedPlugin plugin = new EncodeTransferMedataRestTemplateEnhancedPlugin(); + EnhancedRestTemplateInterceptor interceptor = new EnhancedRestTemplateInterceptor( + new DefaultEnhancedPluginRunner(Arrays.asList(plugin), new MockRegistration(), null)); + RestTemplate template = new RestTemplate(); + template.setInterceptors(Arrays.asList(interceptor)); + return template; } @RequestMapping("/test") @@ -79,4 +93,37 @@ public String test() { return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); } } + + static class MockRegistration implements Registration { + + @Override + public String getServiceId() { + return "test"; + } + + @Override + public String getHost() { + return "localhost"; + } + + @Override + public int getPort() { + return 0; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public URI getUri() { + return null; + } + + @Override + public Map getMetadata() { + return null; + } + } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java index bdfcfb440..cbc1116bc 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java @@ -13,65 +13,134 @@ * 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.metadata.core; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.Map; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; import com.tencent.cloud.common.constant.MetadataConstant; -import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.rpc.enhancement.plugin.DefaultEnhancedPluginRunner; +import com.tencent.cloud.rpc.enhancement.scg.EnhancedGatewayGlobalFilter; +import org.assertj.core.util.Maps; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.route.Route; import org.springframework.context.ApplicationContext; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; -import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.web.server.ServerWebExchange; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; +import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; +import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; /** - * Test for {@link EncodeTransferMedataScgFilter}. - * - * @author quan + * Test for {@link EncodeTransferMedataScgEnhancedPlugin}. + * @author quan, Shedfree Wu */ -@ExtendWith(SpringExtension.class) -@SpringBootTest(webEnvironment = RANDOM_PORT, classes = EncodeTransferMedataScgFilterTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml", - "spring.main.web-application-type = reactive"}) +@ExtendWith(MockitoExtension.class) public class EncodeTransferMedataScgFilterTest { - @Autowired - private ApplicationContext applicationContext; - + private static MockedStatic mockedApplicationContextAwareUtils; @Mock - private GatewayFilterChain chain; + Registration registration; + @Mock + GatewayFilterChain chain; - @Test - public void testTransitiveMetadataFromApplicationConfig() throws UnsupportedEncodingException { - EncodeTransferMedataScgFilter filter = applicationContext.getBean(EncodeTransferMedataScgFilter.class); - MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get(""); - MockServerWebExchange exchange = MockServerWebExchange.from(builder); - filter.filter(exchange, chain); - String metadataStr = exchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); - String decode = URLDecoder.decode(metadataStr, UTF_8); - Map transitiveMap = JacksonUtils.deserialize2Map(decode); - assertThat(transitiveMap.size()).isEqualTo(1); - assertThat(transitiveMap.get("b")).isEqualTo("2"); + @BeforeAll + static void beforeAll() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn("unit-test"); + ApplicationContext applicationContext = mock(ApplicationContext.class); + MetadataLocalProperties metadataLocalProperties = mock(MetadataLocalProperties.class); + StaticMetadataManager staticMetadataManager = mock(StaticMetadataManager.class); + doReturn(metadataLocalProperties).when(applicationContext).getBean(MetadataLocalProperties.class); + doReturn(staticMetadataManager).when(applicationContext).getBean(StaticMetadataManager.class); + mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext) + .thenReturn(applicationContext); } - @SpringBootApplication - protected static class TestApplication { + @AfterAll + static void afterAll() { + mockedApplicationContextAwareUtils.close(); + } + + @BeforeEach + void setUp() { + MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST; + MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER; + } + + @Test + public void testRun() throws URISyntaxException { + + Route route = mock(Route.class); + URI uri = new URI("http://TEST/"); + doReturn(uri).when(route).getUri(); + + MetadataContext metadataContext = MetadataContextHolder.get(); + metadataContext.setTransitiveMetadata(Maps.newHashMap("t-key", "t-value")); + metadataContext.setDisposableMetadata(Maps.newHashMap("d-key", "d-value")); + + MockServerHttpRequest mockServerHttpRequest = MockServerHttpRequest.get("/test").build(); + + EncodeTransferMedataScgEnhancedPlugin plugin = new EncodeTransferMedataScgEnhancedPlugin(); + plugin.getOrder(); + EnhancedGatewayGlobalFilter filter = new EnhancedGatewayGlobalFilter(new DefaultEnhancedPluginRunner(Arrays.asList(plugin), registration, null)); + filter.getOrder(); + + MockServerWebExchange mockServerWebExchange = MockServerWebExchange.builder(mockServerHttpRequest).build(); + mockServerWebExchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route); + mockServerWebExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI("http://0.0.0.0/")); + mockServerWebExchange.getAttributes().put(MetadataConstant.HeaderName.METADATA_CONTEXT, metadataContext); + doReturn(Mono.empty()).when(chain).filter(any()); + + + filter.filter(mockServerWebExchange, chain).block(); + + + ArgumentCaptor captor = ArgumentCaptor.forClass(ServerWebExchange.class); + // capture the result exchange + Mockito.verify(chain).filter(captor.capture()); + ServerWebExchange filteredExchange = captor.getValue(); + + assertThat(filteredExchange.getRequest().getHeaders().get(CUSTOM_METADATA)).isNotNull(); + assertThat(filteredExchange.getRequest().getHeaders().get(CUSTOM_DISPOSABLE_METADATA)).isNotNull(); + + // test metadataContext init in EnhancedPlugin + mockServerWebExchange.getAttributes().remove(MetadataConstant.HeaderName.METADATA_CONTEXT); + assertThatCode(() -> filter.filter(mockServerWebExchange, chain).block()).doesNotThrowAnyException(); } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java index ec44eec32..fe7c30d27 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataWebClientFilterTest.java @@ -37,18 +37,21 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * Test for {@link EncodeTransferMedataWebClientFilter}. + * Test for {@link EncodeTransferMedataWebClientEnhancedPlugin}. * * @author sean yu */ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = EncodeTransferMedataWebClientFilterTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml"}) + properties = {"spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = reactive"}) public class EncodeTransferMedataWebClientFilterTest { @Autowired private WebClient.Builder webClientBuilder; + @LocalServerPort + private int localServerPort; @Test public void testTransitiveMetadataFromApplicationConfig() { @@ -63,10 +66,6 @@ public void testTransitiveMetadataFromApplicationConfig() { assertThat(metadata).isEqualTo("2"); } - @LocalServerPort - private int localServerPort; - - @SpringBootApplication @RestController protected static class TestApplication { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java new file mode 100644 index 000000000..dab78d99a --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/provider/MetadataProviderTest.java @@ -0,0 +1,139 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + +package com.tencent.cloud.metadata.provider; + +import com.tencent.cloud.common.util.UrlUtils; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import org.junit.jupiter.api.Test; + +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpMethod; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.MockCookie; +import org.springframework.mock.web.MockHttpServletRequest; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link ReactiveMetadataProvider} and {@link ServletMetadataProvider}. + * + * @author quan, Shedfree Wu + */ +public class MetadataProviderTest { + + private static final String notExistKey = "empty"; + + @Test + public void testReactiveMetadataProvider() { + String headerKey1 = "header1"; + String headerKey2 = "header2"; + String headerValue1 = "value1"; + String headerValue2 = "value2/test"; + String queryKey1 = "qk1"; + String queryKey2 = "qk2"; + String queryValue1 = "qv1"; + String queryValue2 = "qv2/test"; + String cookieKey1 = "ck1"; + String cookieKey2 = "ck2"; + String cookieValue1 = "cv1"; + String cookieValue2 = "cv2/test"; + String path = "/echo/test"; + String callerIp = "localhost"; + MockServerHttpRequest request = MockServerHttpRequest.get(path) + .header(headerKey1, headerValue1) + .header(headerKey2, UrlUtils.encode(headerValue2)) + .queryParam(queryKey1, queryValue1) + .queryParam(queryKey2, UrlUtils.encode(queryValue2)) + .cookie(new HttpCookie(cookieKey1, cookieValue1)) + .cookie(new HttpCookie(cookieKey2, UrlUtils.encode(cookieValue2))) + .build(); + + ReactiveMetadataProvider reactiveMetadataProvider = new ReactiveMetadataProvider(request, callerIp); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey1)).isEqualTo(headerValue1); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey2)).isEqualTo(headerValue2); + // com.tencent.polaris.metadata.core.manager.ComposeMetadataProvider.getRawMetadataMapValue need return null when key don't exist + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, notExistKey)).isNull(); + + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey1)).isEqualTo(cookieValue1); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey2)).isEqualTo(cookieValue2); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, notExistKey)).isNull(); + + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey1)).isEqualTo(queryValue1); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey2)).isEqualTo(queryValue2); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, notExistKey)).isNull(); + assertThat(reactiveMetadataProvider.getRawMetadataMapValue(notExistKey, queryKey1)).isNull(); + + assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo("GET"); + assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo(path); + assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_CALLER_IP)).isEqualTo(callerIp); + assertThat(reactiveMetadataProvider.getRawMetadataStringValue(notExistKey)).isNull(); + + request = MockServerHttpRequest.get("/echo/" + UrlUtils.decode("a@b")).build(); + reactiveMetadataProvider = new ReactiveMetadataProvider(request, callerIp); + assertThat(reactiveMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/echo/a@b"); + } + + @Test + public void testServletMetadataProvider() { + String headerKey1 = "header1"; + String headerKey2 = "header2"; + String headerValue1 = "value1"; + String headerValue2 = "value2/test"; + String queryKey1 = "qk1"; + String queryKey2 = "qk2"; + String queryValue1 = "qv1"; + String queryValue2 = "qv2/test"; + String cookieKey1 = "ck1"; + String cookieKey2 = "ck2"; + String cookieValue1 = "cv1"; + String cookieValue2 = "cv2/test"; + String path = "/echo/test"; + String callerIp = "localhost"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(headerKey1, headerValue1); + request.addHeader(headerKey2, UrlUtils.encode(headerValue2)); + request.setCookies(new MockCookie(cookieKey1, cookieValue1), new MockCookie(cookieKey2, UrlUtils.encode(cookieValue2))); + request.setMethod(HttpMethod.GET.name()); + request.setRequestURI(path); + request.setQueryString(queryKey1 + "=" + queryValue1 + "&" + queryKey2 + "=" + UrlUtils.encode(queryValue2)); + + ServletMetadataProvider servletMetadataProvider = new ServletMetadataProvider(request, callerIp); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey1)).isEqualTo(headerValue1); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, headerKey2)).isEqualTo(headerValue2); + // com.tencent.polaris.metadata.core.manager.ComposeMetadataProvider.getRawMetadataMapValue need return null when key don't exist + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_HEADER, notExistKey)).isNull(); + + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey1)).isEqualTo(cookieValue1); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, cookieKey2)).isEqualTo(cookieValue2); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_COOKIE, notExistKey)).isNull(); + + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey1)).isEqualTo(queryValue1); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, queryKey2)).isEqualTo(queryValue2); + assertThat(servletMetadataProvider.getRawMetadataMapValue(MessageMetadataContainer.LABEL_MAP_KEY_QUERY, notExistKey)).isNull(); + assertThat(servletMetadataProvider.getRawMetadataMapValue(notExistKey, queryKey1)).isNull(); + + assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_METHOD)).isEqualTo("GET"); + assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo(path); + assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_CALLER_IP)).isEqualTo(callerIp); + assertThat(servletMetadataProvider.getRawMetadataStringValue(notExistKey)).isNull(); + + request.setRequestURI("/echo/" + UrlUtils.decode("a@b")); + assertThat(servletMetadataProvider.getRawMetadataStringValue(MessageMetadataContainer.LABEL_KEY_PATH)).isEqualTo("/echo/a@b"); + } +} diff --git a/spring-cloud-tencent-commons/pom.xml b/spring-cloud-tencent-commons/pom.xml index 6cc5556be..8632ccdd3 100644 --- a/spring-cloud-tencent-commons/pom.xml +++ b/spring-cloud-tencent-commons/pom.xml @@ -24,8 +24,17 @@ com.tencent.polaris polaris-model + + com.tencent.polaris + polaris-metadata + + + com.tencent.cloud + spring-cloud-starter-tencent-threadlocal-plugin + + org.springframework.boot spring-boot-autoconfigure diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java index 8fcd89855..11b667c1a 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java @@ -21,22 +21,25 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Optional; +import java.util.function.BiConsumer; +import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.DiscoveryUtil; -import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.polaris.metadata.core.MetadataContainer; +import com.tencent.polaris.metadata.core.MetadataMapValue; +import com.tencent.polaris.metadata.core.MetadataObjectValue; +import com.tencent.polaris.metadata.core.MetadataStringValue; +import com.tencent.polaris.metadata.core.MetadataType; +import com.tencent.polaris.metadata.core.MetadataValue; +import com.tencent.polaris.metadata.core.TransitiveType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; -/** - * Metadata Context. - * - * @author Haotian Zhang - */ -public class MetadataContext { +public class MetadataContext extends com.tencent.polaris.metadata.core.manager.MetadataContext { /** * transitive context. @@ -63,6 +66,11 @@ public class MetadataContext { */ public static final String FRAGMENT_RAW_TRANSHEADERS_KV = "trans-headers-kv"; + /** + * the key of the header(key-value) list needed to be store as loadbalance data. + */ + public static final String FRAGMENT_LB_METADATA = "load-balance-metadata"; + private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class); /** * Namespace of local instance. @@ -108,22 +116,70 @@ public class MetadataContext { LOCAL_SERVICE = serviceName; } - private final Map> fragmentContexts; + public MetadataContext() { + super(MetadataConstant.POLARIS_TRANSITIVE_HEADER_PREFIX); + } - private final Map loadbalancerMetadata; + private Map getMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean downstream) { + MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream); + Map values = new HashMap<>(); + metadataContainer.iterateMetadataValues(new BiConsumer() { + @Override + public void accept(String s, MetadataValue metadataValue) { + if (metadataValue instanceof MetadataStringValue) { + MetadataStringValue metadataStringValue = (MetadataStringValue) metadataValue; + if (metadataStringValue.getTransitiveType() == transitiveType) { + values.put(s, metadataStringValue.getStringValue()); + } + } + } + }); + return values; + } + private void putMetadataAsMap(MetadataType metadataType, TransitiveType transitiveType, boolean downstream, Map values) { + MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream); + for (Map.Entry entry : values.entrySet()) { + metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), transitiveType); + } + } - public MetadataContext() { - this.fragmentContexts = new ConcurrentHashMap<>(); - this.loadbalancerMetadata = new ConcurrentHashMap<>(); + private Map getMapMetadataAsMap(MetadataType metadataType, String mapKey, TransitiveType transitiveType, boolean downstream) { + MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream); + Map values = new HashMap<>(); + MetadataValue metadataValue = metadataContainer.getMetadataValue(mapKey); + if (!(metadataValue instanceof MetadataMapValue)) { + return values; + } + MetadataMapValue metadataMapValue = (MetadataMapValue) metadataValue; + metadataMapValue.iterateMapValues(new BiConsumer() { + @Override + public void accept(String s, MetadataValue metadataValue) { + if (metadataValue instanceof MetadataStringValue) { + MetadataStringValue metadataStringValue = (MetadataStringValue) metadataValue; + if (metadataStringValue.getTransitiveType() == transitiveType) { + values.put(s, metadataStringValue.getStringValue()); + } + } + } + }); + return values; + } + + private void putMapMetadataAsMap(MetadataType metadataType, String mapKey, + TransitiveType transitiveType, boolean downstream, Map values) { + MetadataContainer metadataContainer = getMetadataContainer(metadataType, downstream); + for (Map.Entry entry : values.entrySet()) { + metadataContainer.putMetadataMapValue(mapKey, entry.getKey(), entry.getValue(), transitiveType); + } } public Map getDisposableMetadata() { - return this.getFragmentContext(MetadataContext.FRAGMENT_DISPOSABLE); + return getFragmentContext(FRAGMENT_DISPOSABLE); } public Map getTransitiveMetadata() { - return this.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + return getFragmentContext(FRAGMENT_TRANSITIVE); } public Map getCustomMetadata() { @@ -140,51 +196,76 @@ public Map getCustomMetadata() { } public Map getTransHeaders() { - return this.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS); + return this.getFragmentContext(FRAGMENT_RAW_TRANSHEADERS); } public Map getTransHeadersKV() { - return this.getFragmentContext(MetadataContext.FRAGMENT_RAW_TRANSHEADERS_KV); + return getFragmentContext(FRAGMENT_RAW_TRANSHEADERS_KV); } public Map getLoadbalancerMetadata() { - return this.loadbalancerMetadata; + MetadataContainer metadataContainer = getMetadataContainer(MetadataType.APPLICATION, false); + MetadataValue metadataValue = metadataContainer.getMetadataValue(FRAGMENT_LB_METADATA); + Map values = new HashMap<>(); + if (metadataValue instanceof MetadataMapValue) { + MetadataMapValue metadataMapValue = (MetadataMapValue) metadataValue; + metadataMapValue.iterateMapValues(new BiConsumer() { + @Override + public void accept(String s, MetadataValue metadataValue) { + if (metadataValue instanceof MetadataObjectValue) { + Optional objectValue = ((MetadataObjectValue) metadataValue).getObjectValue(); + objectValue.ifPresent(o -> values.put(s, o)); + } + } + }); + } + return values; + } + + public void setLoadbalancer(String key, Object value) { + MetadataContainer metadataContainer = getMetadataContainer(MetadataType.APPLICATION, false); + metadataContainer.putMetadataMapObjectValue(FRAGMENT_LB_METADATA, key, value); } public void setTransitiveMetadata(Map transitiveMetadata) { - this.putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata)); + putFragmentContext(FRAGMENT_TRANSITIVE, Collections.unmodifiableMap(transitiveMetadata)); } public void setDisposableMetadata(Map disposableMetadata) { - this.putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata)); + putFragmentContext(FRAGMENT_DISPOSABLE, Collections.unmodifiableMap(disposableMetadata)); } public void setUpstreamDisposableMetadata(Map upstreamDisposableMetadata) { - this.putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(upstreamDisposableMetadata)); + putFragmentContext(FRAGMENT_UPSTREAM_DISPOSABLE, Collections.unmodifiableMap(upstreamDisposableMetadata)); } public void setTransHeadersKV(String key, String value) { - this.putContext(FRAGMENT_RAW_TRANSHEADERS_KV, key, value); + putContext(FRAGMENT_RAW_TRANSHEADERS_KV, key, value); } public void setTransHeaders(String key, String value) { - this.putContext(FRAGMENT_RAW_TRANSHEADERS, key, value); - } - - public void setLoadbalancer(String key, Object value) { - this.loadbalancerMetadata.put(key, value); + putContext(FRAGMENT_RAW_TRANSHEADERS, key, value); } public Map getFragmentContext(String fragment) { - Map fragmentContext = fragmentContexts.get(fragment); - if (fragmentContext == null) { - return Collections.emptyMap(); + switch (fragment) { + case FRAGMENT_TRANSITIVE: + return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.PASS_THROUGH, false); + case FRAGMENT_DISPOSABLE: + return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, false); + case FRAGMENT_UPSTREAM_DISPOSABLE: + return getMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true); + case FRAGMENT_RAW_TRANSHEADERS: + return getMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false); + case FRAGMENT_RAW_TRANSHEADERS_KV: + return getMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS_KV, TransitiveType.PASS_THROUGH, false); + default: + return getMapMetadataAsMap(MetadataType.CUSTOM, fragment, TransitiveType.NONE, false); } - return Collections.unmodifiableMap(fragmentContext); } public String getContext(String fragment, String key) { - Map fragmentContext = fragmentContexts.get(fragment); + Map fragmentContext = getFragmentContext(fragment); if (fragmentContext == null) { return null; } @@ -192,22 +273,31 @@ public String getContext(String fragment, String key) { } public void putContext(String fragment, String key, String value) { - Map fragmentContext = fragmentContexts.get(fragment); - if (fragmentContext == null) { - fragmentContext = new ConcurrentHashMap<>(); - fragmentContexts.put(fragment, fragmentContext); - } - fragmentContext.put(key, value); + Map values = new HashMap<>(); + values.put(key, value); + putFragmentContext(fragment, values); } public void putFragmentContext(String fragment, Map context) { - fragmentContexts.put(fragment, context); - } - - @Override - public String toString() { - return "MetadataContext{" + - "fragmentContexts=" + JacksonUtils.serialize2Json(fragmentContexts) + - '}'; + switch (fragment) { + case FRAGMENT_TRANSITIVE: + putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.PASS_THROUGH, false, context); + break; + case FRAGMENT_DISPOSABLE: + putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, false, context); + break; + case FRAGMENT_UPSTREAM_DISPOSABLE: + putMetadataAsMap(MetadataType.CUSTOM, TransitiveType.DISPOSABLE, true, context); + break; + case FRAGMENT_RAW_TRANSHEADERS: + putMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS, TransitiveType.NONE, false, context); + break; + case FRAGMENT_RAW_TRANSHEADERS_KV: + putMapMetadataAsMap(MetadataType.CUSTOM, FRAGMENT_RAW_TRANSHEADERS_KV, TransitiveType.PASS_THROUGH, false, context); + break; + default: + putMapMetadataAsMap(MetadataType.CUSTOM, fragment, TransitiveType.NONE, false, context); + break; + } } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java index db9d1e5b2..71e1b7754 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java @@ -24,6 +24,11 @@ import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.polaris.metadata.core.MessageMetadataContainer; +import com.tencent.polaris.metadata.core.MetadataContainer; +import com.tencent.polaris.metadata.core.MetadataProvider; +import com.tencent.polaris.metadata.core.MetadataType; +import com.tencent.polaris.metadata.core.TransitiveType; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -38,48 +43,52 @@ */ public final class MetadataContextHolder { - private static final ThreadLocal METADATA_CONTEXT = new InheritableThreadLocal<>(); - private static MetadataLocalProperties metadataLocalProperties; private static StaticMetadataManager staticMetadataManager; + static { + com.tencent.polaris.metadata.core.manager.MetadataContextHolder.setInitializer(MetadataContextHolder::createMetadataManager); + } + private MetadataContextHolder() { } - /** - * Get metadata context. Create if not existing. - * @return METADATA_CONTEXT - */ public static MetadataContext get() { - if (METADATA_CONTEXT.get() != null) { - return METADATA_CONTEXT.get(); - } + return (MetadataContext) com.tencent.polaris.metadata.core.manager.MetadataContextHolder.getOrCreate(); + } + private static MetadataContext createMetadataManager() { + MetadataContext metadataManager = new MetadataContext(); if (metadataLocalProperties == null) { - metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext().getBean(MetadataLocalProperties.class); + metadataLocalProperties = ApplicationContextAwareUtils.getApplicationContext() + .getBean(MetadataLocalProperties.class); } if (staticMetadataManager == null) { - staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext().getBean(StaticMetadataManager.class); + staticMetadataManager = ApplicationContextAwareUtils.getApplicationContext() + .getBean(StaticMetadataManager.class); + } + MetadataContainer metadataContainer = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false); + Map mergedStaticTransitiveMetadata = staticMetadataManager.getMergedStaticTransitiveMetadata(); + for (Map.Entry entry : mergedStaticTransitiveMetadata.entrySet()) { + metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH); + } + Map mergedStaticDisposableMetadata = staticMetadataManager.getMergedStaticDisposableMetadata(); + for (Map.Entry entry : mergedStaticDisposableMetadata.entrySet()) { + metadataContainer.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE); } - - // init static transitive metadata - MetadataContext metadataContext = new MetadataContext(); - metadataContext.setTransitiveMetadata(staticMetadataManager.getMergedStaticTransitiveMetadata()); - metadataContext.setDisposableMetadata(staticMetadataManager.getMergedStaticDisposableMetadata()); if (StringUtils.hasText(staticMetadataManager.getTransHeader())) { - metadataContext.setTransHeaders(staticMetadataManager.getTransHeader(), ""); + String transHeader = staticMetadataManager.getTransHeader(); + metadataContainer.putMetadataMapValue(MetadataContext.FRAGMENT_RAW_TRANSHEADERS, transHeader, "", TransitiveType.NONE); } - - METADATA_CONTEXT.set(metadataContext); - - return METADATA_CONTEXT.get(); + return metadataManager; } /** * Get disposable metadata value from thread local . - * @param key metadata key . + * + * @param key metadata key . * @param upstream upstream disposable , otherwise will return local static disposable metadata . * @return target disposable metadata value . */ @@ -95,6 +104,7 @@ public static Optional getDisposableMetadata(String key, boolean upstrea /** * Get all disposable metadata value from thread local . + * * @param upstream upstream disposable , otherwise will return local static disposable metadata . * @return target disposable metadata value . */ @@ -112,43 +122,46 @@ public static Map getAllDisposableMetadata(boolean upstream) { /** * Set metadata context. + * * @param metadataContext metadata context */ public static void set(MetadataContext metadataContext) { - METADATA_CONTEXT.set(metadataContext); + com.tencent.polaris.metadata.core.manager.MetadataContextHolder.set(metadataContext); } /** * Save metadata map to thread local. + * * @param dynamicTransitiveMetadata custom metadata collection * @param dynamicDisposableMetadata custom disposable metadata connection + * @param callerMetadataProvider caller metadata provider */ - public static void init(Map dynamicTransitiveMetadata, Map dynamicDisposableMetadata) { - // Init ThreadLocal. - MetadataContextHolder.remove(); - MetadataContext metadataContext = MetadataContextHolder.get(); - - // Save transitive metadata to ThreadLocal. - if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) { - Map staticTransitiveMetadata = metadataContext.getTransitiveMetadata(); - Map mergedTransitiveMetadata = new HashMap<>(); - mergedTransitiveMetadata.putAll(staticTransitiveMetadata); - mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata); - metadataContext.setTransitiveMetadata(Collections.unmodifiableMap(mergedTransitiveMetadata)); - } - if (!CollectionUtils.isEmpty(dynamicDisposableMetadata)) { - Map mergedUpstreamDisposableMetadata = new HashMap<>(dynamicDisposableMetadata); - metadataContext.setUpstreamDisposableMetadata(Collections.unmodifiableMap(mergedUpstreamDisposableMetadata)); - } - Map staticDisposableMetadata = metadataContext.getDisposableMetadata(); - metadataContext.setDisposableMetadata(Collections.unmodifiableMap(staticDisposableMetadata)); - MetadataContextHolder.set(metadataContext); + public static void init(Map dynamicTransitiveMetadata, Map dynamicDisposableMetadata, + MetadataProvider callerMetadataProvider) { + com.tencent.polaris.metadata.core.manager.MetadataContextHolder.refresh(metadataManager -> { + MetadataContainer metadataContainerUpstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, false); + if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) { + for (Map.Entry entry : dynamicTransitiveMetadata.entrySet()) { + metadataContainerUpstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.PASS_THROUGH); + } + } + MetadataContainer metadataContainerDownstream = metadataManager.getMetadataContainer(MetadataType.CUSTOM, true); + if (!CollectionUtils.isEmpty(dynamicDisposableMetadata)) { + for (Map.Entry entry : dynamicDisposableMetadata.entrySet()) { + metadataContainerDownstream.putMetadataStringValue(entry.getKey(), entry.getValue(), TransitiveType.DISPOSABLE); + } + } + if (callerMetadataProvider != null) { + MessageMetadataContainer callerMessageContainer = metadataManager.getMetadataContainer(MetadataType.MESSAGE, true); + callerMessageContainer.setMetadataProvider(callerMetadataProvider); + } + }); } /** * Remove metadata context. */ public static void remove() { - METADATA_CONTEXT.remove(); + com.tencent.polaris.metadata.core.manager.MetadataContextHolder.remove(); } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java index bc2e20c9d..d822b1943 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java @@ -31,12 +31,15 @@ public final class ReflectionUtils extends org.springframework.util.ReflectionUtils { private final static String SET_PREFIX = "set"; + private ReflectionUtils() { } public static boolean writableBeanField(Field field) { String fieldName = field.getName(); + String setMethodName = SET_PREFIX + capitalize(fieldName); + return ClassUtils.hasMethod(field.getDeclaringClass(), setMethodName, field.getType()); } @@ -65,4 +68,20 @@ public static Object getFieldValue(Object instance, String fieldName) { } return null; } + + public static void setFieldValue(Object instance, String fieldName, Object value) { + Field field = org.springframework.util.ReflectionUtils.findField(instance.getClass(), fieldName); + if (field == null) { + return; + } + + field.setAccessible(true); + + try { + setField(field, instance, value); + } + finally { + field.setAccessible(false); + } + } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java index c7029deff..9c5e73b9d 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ExpressionLabelUtils.java @@ -138,12 +138,16 @@ public static boolean isCallerIPLabel(String expression) { } public static String getQueryValue(String queryString, String queryKey) { + return getQueryValue(queryString, queryKey, StringUtils.EMPTY); + } + + public static String getQueryValue(String queryString, String queryKey, String defaultValue) { if (StringUtils.isBlank(queryString)) { - return StringUtils.EMPTY; + return defaultValue; } String[] queries = StringUtils.split(queryString, "&"); if (queries == null || queries.length == 0) { - return StringUtils.EMPTY; + return defaultValue; } for (String query : queries) { String[] queryKV = StringUtils.split(query, "="); @@ -151,7 +155,7 @@ public static String getQueryValue(String queryString, String queryKey) { return queryKV[1]; } } - return StringUtils.EMPTY; + return defaultValue; } public static String getFirstValue(Map> valueMaps, String key) { diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java index a054c8f65..45ad670b0 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/ServletExpressionLabelUtils.java @@ -83,14 +83,18 @@ else if (ExpressionLabelUtils.isUriLabel(labelKey)) { } public static String getCookieValue(Cookie[] cookies, String key) { + return getCookieValue(cookies, key, StringUtils.EMPTY); + } + + public static String getCookieValue(Cookie[] cookies, String key, String defaultValue) { if (cookies == null || cookies.length == 0) { - return StringUtils.EMPTY; + return defaultValue; } for (Cookie cookie : cookies) { if (StringUtils.equals(cookie.getName(), key)) { return cookie.getValue(); } } - return StringUtils.EMPTY; + return defaultValue; } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java index b8b1a59ad..476aeeecf 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/expresstion/SpringWebExpressionLabelUtils.java @@ -129,29 +129,41 @@ else if (ExpressionLabelUtils.isUriLabel(labelKey)) { } public static String getHeaderValue(ServerHttpRequest request, String key) { + return getHeaderValue(request, key, StringUtils.EMPTY); + } + + public static String getHeaderValue(ServerHttpRequest request, String key, String defaultValue) { String value = request.getHeaders().getFirst(key); if (value == null) { - return StringUtils.EMPTY; + return defaultValue; } return value; } public static String getQueryValue(ServerHttpRequest request, String key) { + return getQueryValue(request, key, StringUtils.EMPTY); + } + + public static String getQueryValue(ServerHttpRequest request, String key, String defaultValue) { MultiValueMap queries = request.getQueryParams(); if (CollectionUtils.isEmpty(queries)) { - return StringUtils.EMPTY; + return defaultValue; } String value = queries.getFirst(key); if (value == null) { - return StringUtils.EMPTY; + return defaultValue; } return value; } public static String getCookieValue(ServerHttpRequest request, String key) { + return getCookieValue(request, key, StringUtils.EMPTY); + } + + public static String getCookieValue(ServerHttpRequest request, String key, String defaultValue) { HttpCookie cookie = request.getCookies().getFirst(key); if (cookie == null) { - return StringUtils.EMPTY; + return defaultValue; } return cookie.getValue(); } diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java index fd12133d0..c2cd05b8d 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java @@ -36,7 +36,8 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MetadataContextHolderTest.TestApplication.class, - properties = {"spring.config.location = classpath:application-test.yml"}) + properties = {"spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = reactive"}) public class MetadataContextHolderTest { @Test @@ -62,7 +63,7 @@ public void test1() { customMetadata.put("a", "1"); customMetadata.put("b", "22"); customMetadata.put("c", "3"); - MetadataContextHolder.init(customMetadata, new HashMap<>()); + MetadataContextHolder.init(customMetadata, new HashMap<>(), null); metadataContext = MetadataContextHolder.get(); customMetadata = metadataContext.getTransitiveMetadata(); Assertions.assertThat(customMetadata.get("a")).isEqualTo("1"); diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml index 9bc67a445..336d1435a 100644 --- a/spring-cloud-tencent-dependencies/pom.xml +++ b/spring-cloud-tencent-dependencies/pom.xml @@ -196,6 +196,12 @@ ${revision} + + com.tencent.cloud + spring-cloud-starter-tencent-threadlocal-plugin + ${revision} + + com.google.guava diff --git a/spring-cloud-tencent-plugin-starters/pom.xml b/spring-cloud-tencent-plugin-starters/pom.xml index 0258ceeea..5debf6360 100644 --- a/spring-cloud-tencent-plugin-starters/pom.xml +++ b/spring-cloud-tencent-plugin-starters/pom.xml @@ -19,6 +19,7 @@ spring-cloud-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-starter-tencent-threadlocal-plugin/pom.xml b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml new file mode 100644 index 000000000..91975c141 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/pom.xml @@ -0,0 +1,35 @@ + + + + spring-cloud-tencent-plugin-starters + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + spring-cloud-starter-tencent-threadlocal-plugin + Spring Cloud Starter Tencent ThreadLocal plugin + + + + + com.tencent.polaris + polaris-threadlocal + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java new file mode 100644 index 000000000..f2f9dc1c4 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/main/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapper.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.threadlocal; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import com.tencent.polaris.threadlocal.cross.RunnableWrapper; + +import org.springframework.core.task.TaskExecutor; + +public class TaskExecutorWrapper implements TaskExecutor { + + private final TaskExecutor taskExecutor; + + private final Supplier contextGetter; + + private final Consumer contextSetter; + + public TaskExecutorWrapper(TaskExecutor taskExecutor, Supplier contextGetter, Consumer contextSetter) { + this.taskExecutor = taskExecutor; + this.contextGetter = contextGetter; + this.contextSetter = contextSetter; + } + + @Override + public void execute(Runnable command) { + taskExecutor.execute(new RunnableWrapper<>(command, contextGetter, contextSetter)); + } +} diff --git a/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java new file mode 100644 index 000000000..15dd313c6 --- /dev/null +++ b/spring-cloud-tencent-plugin-starters/spring-cloud-starter-tencent-threadlocal-plugin/src/test/java/com/tencent/cloud/plugin/threadlocal/TaskExecutorWrapperTest.java @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.plugin.threadlocal; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.Test; + +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; + +/** + * Test for {@link TaskExecutorWrapper}. + * + * @author Haotian Zhang + */ +public class TaskExecutorWrapperTest { + + private static final ThreadLocal TEST_THREAD_LOCAL = new ThreadLocal<>(); + + private static final String TEST = "TEST"; + + @Test + public void testExecute() { + TEST_THREAD_LOCAL.set(TEST); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.initialize(); + AtomicReference result = new AtomicReference<>(false); + CountDownLatch latch = new CountDownLatch(1); + TaskExecutorWrapper taskExecutorWrapper = new TaskExecutorWrapper<>( + executor, TEST_THREAD_LOCAL::get, TEST_THREAD_LOCAL::set); + taskExecutorWrapper.execute(() -> { + result.set(TEST.equals(TEST_THREAD_LOCAL.get())); + latch.countDown(); + }); + try { + latch.await(); + assertThat(result.get()).isTrue(); + } + catch (InterruptedException e) { + fail(e.getMessage()); + } + } +} diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java index b7ee6d11b..0735a90df 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/feign/EnhancedFeignClient.java @@ -21,7 +21,6 @@ import java.net.URI; import java.util.ArrayList; -import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginContext; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginRunner; import com.tencent.cloud.rpc.enhancement.plugin.EnhancedPluginType; @@ -32,11 +31,10 @@ import feign.Request.Options; import feign.Response; -import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import static com.tencent.cloud.rpc.enhancement.resttemplate.PolarisLoadBalancerRequestTransformer.LOAD_BALANCER_SERVICE_INSTANCE; import static feign.Util.checkNotNull; /** @@ -69,10 +67,20 @@ public Response execute(Request request, Options options) throws IOException { .url(url) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); + enhancedPluginContext.setOriginRequest(request); enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); - enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get() - .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), url); + String svcName = request.requestTemplate().feignTarget().name(); + DefaultServiceInstance serviceInstance = new DefaultServiceInstance( + String.format("%s-%s-%d", svcName, url.getHost(), url.getPort()), + svcName, url.getHost(), url.getPort(), url.getScheme().equals("https")); + // -1 means access directly by url + if (serviceInstance.getPort() == -1) { + enhancedPluginContext.setTargetServiceInstance(null, url); + } + else { + enhancedPluginContext.setTargetServiceInstance(serviceInstance, url); + } // Run pre enhanced plugins. pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java index 7eb14ae89..e9ef0cc67 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/EnhancedPluginContext.java @@ -36,6 +36,8 @@ public class EnhancedPluginContext { private static final Logger LOGGER = LoggerFactory.getLogger(EnhancedPluginContext.class); + private Object originRequest; + private EnhancedRequestContext request; private EnhancedResponseContext response; @@ -51,6 +53,14 @@ public class EnhancedPluginContext { */ private ServiceInstance targetServiceInstance; + public Object getOriginRequest() { + return originRequest; + } + + public void setOriginRequest(Object originRequest) { + this.originRequest = originRequest; + } + public EnhancedRequestContext getRequest() { return request; } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java index 6677dac0a..eff88f13a 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/plugin/PluginOrderConstant.java @@ -44,5 +44,19 @@ public static class ClientPluginOrder { * {@link com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter}. */ public static final int CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 2; + + /** + * order for + * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataFeignEnhancedPlugin} + * and + * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataScgEnhancedPlugin} + * and + * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataWebClientEnhancedPlugin} + * and + * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataZuulEnhancedPlugin} + * and + * {@link com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateEnhancedPlugin}. + */ + public static final int CONSUMER_TRANSFER_METADATA_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 10; } } diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java index c50cbb907..6241ac598 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/resttemplate/EnhancedRestTemplateInterceptor.java @@ -58,6 +58,7 @@ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttp .url(request.getURI()) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); + enhancedPluginContext.setOriginRequest(request); enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get() diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java index a838b6466..7b65f2e81 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilter.java @@ -27,15 +27,15 @@ import com.tencent.cloud.rpc.enhancement.plugin.EnhancedResponseContext; import reactor.core.publisher.Mono; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; /** * EnhancedGatewayGlobalFilter. @@ -51,31 +51,40 @@ public EnhancedGatewayGlobalFilter(EnhancedPluginRunner pluginRunner) { } @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + public Mono filter(ServerWebExchange originExchange, GatewayFilterChain chain) { EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); - URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() - .httpHeaders(exchange.getRequest().getHeaders()) - .httpMethod(exchange.getRequest().getMethod()) - .url(uri) + .httpHeaders(originExchange.getRequest().getHeaders()) + .httpMethod(originExchange.getRequest().getMethod()) + .url(originExchange.getRequest().getURI()) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); - - enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); - Response serviceInstanceResponse = exchange.getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); - if (serviceInstanceResponse != null && serviceInstanceResponse.hasServer()) { - ServiceInstance instance = serviceInstanceResponse.getServer(); - enhancedPluginContext.setTargetServiceInstance(instance, exchange.getRequest().getURI()); - } - else { - enhancedPluginContext.setTargetServiceInstance(null, uri); - } + enhancedPluginContext.setOriginRequest(originExchange); // Run pre enhanced plugins. pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); - + // Exchange may be changed in plugin + ServerWebExchange exchange = (ServerWebExchange) enhancedPluginContext.getOriginRequest(); long startTime = System.currentTimeMillis(); return chain.filter(exchange) + .doOnSubscribe(v -> { + Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); + URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + enhancedPluginContext.getRequest().setUrl(uri); + if (uri != null) { + if (route != null && route.getUri().getScheme().contains("lb")) { + DefaultServiceInstance serviceInstance = new DefaultServiceInstance(); + serviceInstance.setServiceId(route.getUri().getHost()); + serviceInstance.setHost(uri.getHost()); + serviceInstance.setPort(uri.getPort()); + enhancedPluginContext.setTargetServiceInstance(serviceInstance, null); + } + else { + enhancedPluginContext.setTargetServiceInstance(null, uri); + } + } + }) .doOnSuccess(v -> { enhancedPluginContext.setDelay(System.currentTimeMillis() - startTime); EnhancedResponseContext enhancedResponseContext = EnhancedResponseContext.builder() diff --git a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java index 673cac3da..a215cfe8a 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java +++ b/spring-cloud-tencent-rpc-enhancement/src/main/java/com/tencent/cloud/rpc/enhancement/webclient/EnhancedWebClientExchangeFilterFunction.java @@ -46,23 +46,25 @@ public EnhancedWebClientExchangeFilterFunction(EnhancedPluginRunner pluginRunner } @Override - public Mono filter(ClientRequest request, ExchangeFunction next) { + public Mono filter(ClientRequest originRequest, ExchangeFunction next) { EnhancedPluginContext enhancedPluginContext = new EnhancedPluginContext(); EnhancedRequestContext enhancedRequestContext = EnhancedRequestContext.builder() - .httpHeaders(request.headers()) - .httpMethod(request.method()) - .url(request.url()) + .httpHeaders(originRequest.headers()) + .httpMethod(originRequest.method()) + .url(originRequest.url()) .build(); enhancedPluginContext.setRequest(enhancedRequestContext); + enhancedPluginContext.setOriginRequest(originRequest); enhancedPluginContext.setLocalServiceInstance(pluginRunner.getLocalServiceInstance()); enhancedPluginContext.setTargetServiceInstance((ServiceInstance) MetadataContextHolder.get() - .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), request.url()); + .getLoadbalancerMetadata().get(LOAD_BALANCER_SERVICE_INSTANCE), originRequest.url()); // Run post enhanced plugins. pluginRunner.run(EnhancedPluginType.Client.PRE, enhancedPluginContext); - + // request may be changed by plugin + ClientRequest request = (ClientRequest) enhancedPluginContext.getOriginRequest(); long startTime = System.currentTimeMillis(); return next.exchange(request) .doOnSuccess(response -> { diff --git a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java index d25ea12ec..397068162 100644 --- a/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java +++ b/spring-cloud-tencent-rpc-enhancement/src/test/java/com/tencent/cloud/rpc/enhancement/scg/EnhancedGatewayGlobalFilterTest.java @@ -39,10 +39,9 @@ import org.mockito.junit.jupiter.MockitoExtension; import reactor.core.publisher.Mono; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.loadbalancer.Response; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.route.Route; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -56,8 +55,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_LOADBALANCER_RESPONSE_ATTR; import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; @ExtendWith(MockitoExtension.class) public class EnhancedGatewayGlobalFilterTest { @@ -111,20 +110,10 @@ public void testRun() throws URISyntaxException { doReturn(HttpMethod.GET).when(request).getMethod(); doReturn(new HttpHeaders()).when(response).getHeaders(); doReturn(Mono.empty()).when(chain).filter(exchange); - - ServiceInstance serviceInstance = mock(ServiceInstance.class); - Response serviceInstanceResponse = new Response() { - @Override - public boolean hasServer() { - return true; - } - - @Override - public ServiceInstance getServer() { - return serviceInstance; - } - }; - doReturn(serviceInstanceResponse).when(exchange).getAttribute(GATEWAY_LOADBALANCER_RESPONSE_ATTR); + Route route = mock(Route.class); + URI uri = new URI("http://TEST/"); + doReturn(uri).when(route).getUri(); + doReturn(route).when(exchange).getAttribute(GATEWAY_ROUTE_ATTR); doReturn(new URI("http://0.0.0.0/")).when(exchange).getAttribute(GATEWAY_REQUEST_URL_ATTR); doReturn(request).when(exchange).getRequest(); doReturn(response).when(exchange).getResponse(); From e7a44dc2c3f42adece449d47fd356404eace9382 Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Tue, 16 Jul 2024 14:51:31 +0800 Subject: [PATCH 10/10] feat: support lane router Co-authored-by: andrew shan <45474304+andrewshan@users.noreply.github.com> --- .../metadata/core/DecodeTransferMetadataServletFilter.java | 6 +----- .../cloud/metadata/provider/ServletMetadataProvider.java | 3 ++- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java index 72bd4f2d9..f1945a287 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java @@ -19,8 +19,6 @@ package com.tencent.cloud.metadata.core; import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import java.util.HashMap; import java.util.Map; @@ -35,16 +33,14 @@ import com.tencent.cloud.common.util.UrlUtils; import com.tencent.cloud.metadata.provider.ServletMetadataProvider; import com.tencent.cloud.polaris.context.config.PolarisContextProperties; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.lang.NonNull; -import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; -import static com.tencent.cloud.common.constant.ContextConstant.UTF_8; + import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_DISPOSABLE_METADATA; import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUSTOM_METADATA; diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java index f3c404ad2..082e13f11 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/provider/ServletMetadataProvider.java @@ -17,13 +17,14 @@ */ package com.tencent.cloud.metadata.provider; +import javax.servlet.http.HttpServletRequest; import com.tencent.cloud.common.util.UrlUtils; import com.tencent.cloud.common.util.expresstion.ExpressionLabelUtils; import com.tencent.cloud.common.util.expresstion.ServletExpressionLabelUtils; import com.tencent.polaris.metadata.core.MessageMetadataContainer; import com.tencent.polaris.metadata.core.MetadataProvider; -import javax.servlet.http.HttpServletRequest; + /** * MetadataProvider used for Servlet.