From 3d057398fb08a67cfded4ae0811874af211bf3ea Mon Sep 17 00:00:00 2001 From: Haotian Zhang Date: Thu, 6 Jun 2024 17:57:28 +0800 Subject: [PATCH] feat:add Tencent Cloud TSF support. (#1317) --- CHANGELOG.md | 1 + .../PolarisConfigCustomExtensionLayer.java | 2 + .../adapter/PolarisServiceLoaderUtil.java | 13 +- ...arisAdaptorTsfConfigAutoConfiguration.java | 77 ++ ...daptorTsfConfigBootstrapConfiguration.java | 39 + .../config/tsf/TsfConfigurationModifier.java | 54 ++ ...PolarisAdaptorTsfConfigExtensionLayer.java | 291 +++++++ .../tsf/cache/PolarisPropertyCache.java | 48 ++ .../PolarisAdaptorTsfConfigController.java | 60 ++ .../tsf/encrypt/ConfigEncryptAESProvider.java | 48 ++ .../tsf/encrypt/ConfigEncryptProvider.java | 44 ++ .../encrypt/ConfigEncryptProviderFactory.java | 39 + .../config/tsf/encrypt/EncryptAlgorithm.java | 146 ++++ .../config/tsf/encrypt/EncryptConfig.java | 114 +++ .../config/watch/ConfigChangeCallback.java | 28 + .../config/watch/ConfigChangeListener.java | 40 + .../consul/config/watch/ConfigProperty.java | 46 ++ .../TsfConsulConfigRefreshEventListener.java | 122 +++ ....adapter.PolarisConfigCustomExtensionLayer | 1 + .../main/resources/META-INF/spring.factories | 4 +- ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../contract/tsf/TsfApiMetadataGrapher.java | 137 ++++ .../contract/tsf/TsfContractProperties.java | 136 ++++ ...sfContractPropertiesAutoConfiguration.java | 40 + ...tractPropertiesBootstrapConfiguration.java | 35 + .../tsf/TsfSwaggerAutoConfiguration.java | 50 ++ .../main/resources/META-INF/spring.factories | 3 +- ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../pom.xml | 16 +- .../registry/PolarisServiceRegistry.java | 4 +- .../tsf/TsfDiscoveryConfigModifier.java | 193 +++++ .../polaris/tsf/TsfDiscoveryProperties.java | 744 ++++++++++++++++++ ...fDiscoveryPropertiesAutoConfiguration.java | 83 ++ ...overyPropertiesBootstrapConfiguration.java | 39 + .../polaris/tsf/TsfHeartbeatProperties.java | 109 +++ .../tsf/TsfZeroProtectionConfigModifier.java | 40 + .../cloud/polaris/tsf/consts/WarmupCons.java | 38 + .../lossless/TsfLosslessConfigModifier.java | 50 ++ .../tsf/lossless/TsfLosslessProperties.java | 48 ++ .../TsfApiPolarisRegistrationCustomizer.java | 62 ++ ...TsfDiscoveryRegistryAutoConfiguration.java | 75 ++ ...MetadataPolarisRegistrationCustomizer.java | 60 ++ .../TsfPortPolarisRegistrationCustomizer.java | 62 ++ .../TsfServletRegistrationCustomizer.java | 65 ++ .../polaris/tsf/util/RegistrationUtil.java | 231 ++++++ .../main/resources/META-INF/spring.factories | 3 +- ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../lossless/LosslessRegistryAspectTest.java | 2 + .../config}/LosslessConfigModifierTest.java | 3 +- .../AutoServiceRegistrationUtils.java | 0 spring-cloud-tencent-commons/pom.xml | 5 + .../cloud/common/util/AddressUtils.java | 82 +- .../cloud/common/util/BeanFactoryUtils.java | 38 +- .../common/util/inet/PolarisInetUtils.java | 320 ++++++++ .../PolarisInetUtilsAutoConfiguration.java | 39 + ...olarisInetUtilsBootstrapConfiguration.java | 30 + .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + spring-cloud-tencent-dependencies/pom.xml | 9 +- .../cloud/polaris/config/example/Person.java | 58 -- .../src/main/resources/application.yml | 26 - spring-cloud-tencent-examples/pom.xml | 2 +- .../consumer-demo}/pom.xml | 21 +- .../demo/consumer/ConsumerApplication.java | 41 + .../controller/ConsumerController.java | 53 ++ .../demo/consumer/entity/CustomMetadata.java | 32 + .../consumer/proxy/ProviderDemoService.java | 36 + .../demo/consumer/proxy/ProviderService.java} | 41 +- .../src/main/resources/application.yml | 20 + .../tsf-example/pom.xml | 41 + .../tsf-example/provider-demo/pom.xml | 54 ++ .../demo/provider/ProviderApplication.java} | 11 +- .../provider/ProviderConfigController.java} | 35 +- .../tsf/demo/provider/ProviderController.java | 153 ++++ .../provider/config/ProviderNameConfig.java | 37 + .../ProviderNameConfigChangeListener.java | 40 + .../controller/SwaggerApiController.java | 145 ++++ .../provider/swagger/model/MessageBox.java | 102 +++ .../provider/swagger/model/MessageModel.java | 92 +++ .../provider/swagger/model/MessageUser.java | 69 ++ .../src/main/resources/application.yml | 26 + .../pom.xml | 17 +- .../lossless/config/LosslessProperties.java | 2 +- spring-cloud-tencent-polaris-context/pom.xml | 5 + .../context/tsf/ConditionalOnTsfEnabled.java | 50 ++ .../context/tsf/config/TsfCoreProperties.java | 72 ++ .../TsfCorePropertiesAutoConfiguration.java | 43 + ...fCorePropertiesBootstrapConfiguration.java | 32 + .../consul/TsfConsulAutoConfiguration.java | 42 + .../TsfConsulBootstrapConfiguration.java | 29 + .../tsf/consul/TsfConsulProperties.java | 107 +++ .../env/TsfCoreEnvironmentPostProcessor.java | 69 ++ .../tsf/annotation/EnableTsf.java | 45 ++ .../main/resources/META-INF/spring.factories | 6 +- ...ot.autoconfigure.AutoConfiguration.imports | 2 + 95 files changed, 5539 insertions(+), 193 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigBootstrapConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/TsfConfigurationModifier.java create mode 100755 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/adaptor/PolarisAdaptorTsfConfigExtensionLayer.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/cache/PolarisPropertyCache.java create mode 100755 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/controller/PolarisAdaptorTsfConfigController.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptAESProvider.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProvider.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProviderFactory.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptAlgorithm.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptConfig.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeCallback.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeListener.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigProperty.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/TsfConsulConfigRefreshEventListener.java create mode 100644 spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/services/com.tencent.cloud.polaris.config.adapter.PolarisConfigCustomExtensionLayer create mode 100644 spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfApiMetadataGrapher.java create mode 100644 spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractProperties.java create mode 100644 spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesBootstrapConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfSwaggerAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryConfigModifier.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryProperties.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesBootstrapConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfHeartbeatProperties.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfZeroProtectionConfigModifier.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/consts/WarmupCons.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessConfigModifier.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessProperties.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfApiPolarisRegistrationCustomizer.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfDiscoveryRegistryAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfMetadataPolarisRegistrationCustomizer.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfPortPolarisRegistrationCustomizer.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfServletRegistrationCustomizer.java create mode 100644 spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/util/RegistrationUtil.java rename {spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin => spring-cloud-starter-tencent-polaris-discovery}/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java (98%) rename {spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless => spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/config}/LosslessConfigModifierTest.java (96%) rename {spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin => spring-cloud-starter-tencent-polaris-discovery}/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java (100%) create mode 100644 spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtils.java create mode 100644 spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsAutoConfiguration.java create mode 100644 spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsBootstrapConfiguration.java create mode 100644 spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories delete mode 100644 spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java delete mode 100644 spring-cloud-tencent-examples/polaris-config-data-example/src/main/resources/application.yml rename spring-cloud-tencent-examples/{polaris-config-data-example => tsf-example/consumer-demo}/pom.xml (79%) create mode 100644 spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/ConsumerApplication.java create mode 100644 spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/ConsumerController.java create mode 100644 spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/entity/CustomMetadata.java create mode 100644 spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java rename spring-cloud-tencent-examples/{polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java => tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderService.java} (51%) create mode 100644 spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/application.yml create mode 100644 spring-cloud-tencent-examples/tsf-example/pom.xml create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml rename spring-cloud-tencent-examples/{polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PolarisConfigDataExampleApplication.java => tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderApplication.java} (82%) rename spring-cloud-tencent-examples/{polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/ConfigController.java => tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderConfigController.java} (63%) create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderController.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfig.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfigChangeListener.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/controller/SwaggerApiController.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageBox.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageModel.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageUser.java create mode 100644 spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/application.yml create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/ConditionalOnTsfEnabled.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCoreProperties.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesAutoConfiguration.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesBootstrapConfiguration.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulAutoConfiguration.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulBootstrapConfiguration.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulProperties.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/env/TsfCoreEnvironmentPostProcessor.java create mode 100644 spring-cloud-tencent-polaris-context/src/main/java/org/springframework/tsf/annotation/EnableTsf.java diff --git a/CHANGELOG.md b/CHANGELOG.md index cf3cd051..6eba9447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,3 +17,4 @@ - [feat:upgrade jacoco version.](https://github.com/Tencent/spring-cloud-tencent/pull/1309) - [fix:fix no registry when lossless is disabled.](https://github.com/Tencent/spring-cloud-tencent/pull/1311) - [fix:fix the ratelimit bug for 2022](https://github.com/Tencent/spring-cloud-tencent/pull/1314) +- [feat:add Tencent Cloud TSF support.](https://github.com/Tencent/spring-cloud-tencent/pull/1317) diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigCustomExtensionLayer.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigCustomExtensionLayer.java index 19d87385..592368c6 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigCustomExtensionLayer.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigCustomExtensionLayer.java @@ -29,6 +29,8 @@ import org.springframework.core.env.Environment; * @author juanyinyang */ public interface PolarisConfigCustomExtensionLayer { + boolean isEnabled(); + void initRegisterConfig(PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher); void initConfigFiles(Environment environment, CompositePropertySource compositePropertySource, ConfigFileService configFileService); diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisServiceLoaderUtil.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisServiceLoaderUtil.java index 343a5b31..bf61aac4 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisServiceLoaderUtil.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisServiceLoaderUtil.java @@ -30,20 +30,25 @@ import org.slf4j.LoggerFactory; public final class PolarisServiceLoaderUtil { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisServiceLoaderUtil.class); - private PolarisServiceLoaderUtil() { - } // this class provides customized logic for some customers to configure special business group files private static PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer; + static { ServiceLoader polarisConfigCustomExtensionLayerLoader = ServiceLoader.load(PolarisConfigCustomExtensionLayer.class); Iterator polarisConfigCustomExtensionLayerIterator = polarisConfigCustomExtensionLayerLoader.iterator(); // Generally, there is only one implementation class. If there are multiple, the last one is loaded while (polarisConfigCustomExtensionLayerIterator.hasNext()) { - polarisConfigCustomExtensionLayer = polarisConfigCustomExtensionLayerIterator.next(); - LOGGER.info("[SCT Config] PolarisConfigFileLocator init polarisConfigCustomExtensionLayer:{}", polarisConfigCustomExtensionLayer); + PolarisConfigCustomExtensionLayer temp = polarisConfigCustomExtensionLayerIterator.next(); + if (temp.isEnabled()) { + polarisConfigCustomExtensionLayer = temp; + LOGGER.info("[SCT Config] PolarisConfigFileLocator init polarisConfigCustomExtensionLayer:{}", polarisConfigCustomExtensionLayer); + } } } + private PolarisServiceLoaderUtil() { + } + public static PolarisConfigCustomExtensionLayer getPolarisConfigCustomExtensionLayer() { return polarisConfigCustomExtensionLayer; } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigAutoConfiguration.java new file mode 100644 index 00000000..fbb1ad02 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigAutoConfiguration.java @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf; + +import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.config.tsf.controller.PolarisAdaptorTsfConfigController; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; +import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties; +import com.tencent.tsf.consul.config.watch.TsfConsulConfigRefreshEventListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author juanyinyang + * @Date Jul 23, 2023 3:52:48 PM + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +@ConditionalOnPolarisConfigEnabled +public class PolarisAdaptorTsfConfigAutoConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisAdaptorTsfConfigAutoConfiguration.class); + + { + System.setProperty("spring.cloud.polaris.config.refresh-type", "refresh_context"); + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigAutoConfiguration init set spring.cloud.polaris.config.refresh-type to refresh_context"); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(name = "spring.cloud.consul.config.watch.enabled", matchIfMissing = true) + public TsfConsulConfigRefreshEventListener polarisAdaptorTsfConsulRefreshEventListener() { + return new TsfConsulConfigRefreshEventListener(); + } + + /** + * 初始化本类的条件: + * 1、关闭Spring Cloud Consul Config配置开关(如果开启Consul Config配置开关,那么初始化的是tsf自身的类ConfigController) + * 2、开启北极星配置(本类通过注解@ConditionalOnPolarisConfigEnabled开启) + * 3、tsf.config.instance.released-config.lookup.enabled的开关是打开的(默认不配置就是打开的). + */ + @Bean + @ConditionalOnMissingBean + @ConditionalOnExpression("${spring.cloud.consul.config.enabled:true} == false and ${tsf.config.instance.released-config.lookup.enabled:true} == true") + public PolarisAdaptorTsfConfigController polarisAdaptorTsfConfigController() { + return new PolarisAdaptorTsfConfigController(); + } + + @Bean + @ConditionalOnMissingBean + public TsfConfigurationModifier tsfConfigModifier(TsfCoreProperties tsfCoreProperties, PolarisConfigProperties polarisConfigProperties) { + return new TsfConfigurationModifier(tsfCoreProperties, polarisConfigProperties); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigBootstrapConfiguration.java new file mode 100644 index 00000000..1ee9176c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/PolarisAdaptorTsfConfigBootstrapConfiguration.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf; + +import com.tencent.cloud.polaris.config.ConditionalOnPolarisConfigEnabled; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author juanyinyang + * @Date Jul 23, 2023 3:52:48 PM + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty("spring.cloud.polaris.enabled") +@ConditionalOnTsfEnabled +@ConditionalOnPolarisConfigEnabled +@Import(PolarisAdaptorTsfConfigAutoConfiguration.class) +public class PolarisAdaptorTsfConfigBootstrapConfiguration { + + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/TsfConfigurationModifier.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/TsfConfigurationModifier.java new file mode 100644 index 00000000..9799e872 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/TsfConfigurationModifier.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.polaris.config.tsf; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties; +import com.tencent.polaris.factory.config.ConfigurationImpl; + +/** + * TSF config modifier. + * + * @author Haotian Zhang + */ +public class TsfConfigurationModifier implements PolarisConfigModifier { + + + private final TsfCoreProperties tsfCoreProperties; + + private final PolarisConfigProperties polarisConfigProperties; + + public TsfConfigurationModifier(TsfCoreProperties tsfCoreProperties, PolarisConfigProperties polarisConfigProperties) { + this.tsfCoreProperties = tsfCoreProperties; + this.polarisConfigProperties = polarisConfigProperties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + if (polarisConfigProperties != null && tsfCoreProperties != null) { + polarisConfigProperties.setEnabled(tsfCoreProperties.isTsePolarisEnable()); + } + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.CONFIG_ORDER - 1; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/adaptor/PolarisAdaptorTsfConfigExtensionLayer.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/adaptor/PolarisAdaptorTsfConfigExtensionLayer.java new file mode 100755 index 00000000..94d32cff --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/adaptor/PolarisAdaptorTsfConfigExtensionLayer.java @@ -0,0 +1,291 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.adaptor; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.google.common.collect.Sets; +import com.tencent.cloud.polaris.config.adapter.PolarisConfigCustomExtensionLayer; +import com.tencent.cloud.polaris.config.adapter.PolarisConfigPropertyAutoRefresher; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.enums.ConfigFileFormat; +import com.tencent.cloud.polaris.config.tsf.cache.PolarisPropertyCache; +import com.tencent.cloud.polaris.config.tsf.encrypt.EncryptConfig; +import com.tencent.polaris.configuration.api.core.ConfigFileGroup; +import com.tencent.polaris.configuration.api.core.ConfigFileGroupChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigFileGroupChangedEvent; +import com.tencent.polaris.configuration.api.core.ConfigFileMetadata; +import com.tencent.polaris.configuration.api.core.ConfigFileService; +import com.tencent.polaris.configuration.api.core.ConfigKVFile; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.core.env.CompositePropertySource; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +/** + * @author juanyinyang + */ +public class PolarisAdaptorTsfConfigExtensionLayer implements PolarisConfigCustomExtensionLayer { + + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisAdaptorTsfConfigExtensionLayer.class); + /** + * 应用配置. + */ + private static final String APP_CONFIG = "appconfig"; + /** + * 全局配置. + */ + private static final String PUB_CONFIG = "pubconfig"; + /** + * TSF应用ID. + */ + private static final String TSF_APPLICATION_ID = "tsf_application_id"; + /** + * TSF部署组ID. + */ + private static final String TSF_GROUP_ID = "tsf_group_id"; + /** + * TSF命名空间ID. + */ + private static final String TSF_NAMESPACE_ID = "tsf_namespace_id"; + /** + * TSF命名空间ID. + */ + private static final String POLARIS_ADAPTOR_TSF_CONFIG_FORMAT = "spring.cloud.polaris.config.format"; + + // 最近一次的全量PolarisPropertySource集合(PolarisPropertySource按 namespace + fileGroup + fileName 确保唯一) + private static final Set registedPolarisPropertySets = Sets.newConcurrentHashSet(); + // 命名空间分组(namespace + fileGroup)的去重Set集合,如果这个分组已添加了ConfigFileGroupListener + private static final Set registedConfigFileGroupListenerSets = Sets.newConcurrentHashSet(); + + private PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher; + + @Override + public boolean isEnabled() { + // tse_polaris_enable + String tsePolarisEnable = System.getenv("tse_polaris_enable"); + if (StringUtils.isBlank(tsePolarisEnable)) { + tsePolarisEnable = System.getProperty("tse_polaris_enable", "false"); + } + return StringUtils.equals(tsePolarisEnable, "true"); + } + + /** + * @see PolarisConfigCustomExtensionLayer#initConfigFiles(CompositePropertySource, + * PolarisPropertySourceManager, + * ConfigFileService) + */ + @Override + public void initConfigFiles(Environment environment, CompositePropertySource compositePropertySource, + ConfigFileService configFileService) { + String tsfApplicationId = environment.getProperty(TSF_APPLICATION_ID); + String tsfGroupId = environment.getProperty(TSF_GROUP_ID); + String tsfNamespaceId = environment.getProperty(TSF_NAMESPACE_ID); + String polarisAdaptorTsfConfigFormat = environment.getProperty(POLARIS_ADAPTOR_TSF_CONFIG_FORMAT); + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer initConfigFiles start, tsfNamespaceId:{}, tsfApplicationId:{}, tsfGroupId:{}", + tsfNamespaceId, tsfApplicationId, tsfGroupId); + loadAllPolarisConfigFile(compositePropertySource, configFileService, + tsfNamespaceId, tsfApplicationId, tsfGroupId, polarisAdaptorTsfConfigFormat); + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer initConfigFiles end"); + } + + private void loadAllPolarisConfigFile(CompositePropertySource compositePropertySource, + ConfigFileService configFileService, String tsfNamespaceId, String tsfApplicationId, String tsfGroupId, + String polarisAdaptorTsfConfigFormat) { + boolean isInitTsfEnv = StringUtils.isNotBlank(tsfNamespaceId) && StringUtils.isNotBlank(tsfApplicationId) + && StringUtils.isNotBlank(tsfGroupId); + if (isInitTsfEnv) { + String appConfigGroup = APP_CONFIG + "." + tsfApplicationId + "." + tsfGroupId; + loadPolarisConfigFile(tsfNamespaceId, tsfApplicationId, tsfGroupId, polarisAdaptorTsfConfigFormat, + compositePropertySource, configFileService, appConfigGroup); + } + + String pubConfigGroup = PUB_CONFIG; + loadPolarisConfigFile(tsfNamespaceId, tsfApplicationId, tsfGroupId, polarisAdaptorTsfConfigFormat, + compositePropertySource, configFileService, pubConfigGroup); + } + + private PolarisPropertySource loadPolarisPropertySource(String namespace, String group, String fileName, + String polarisAdaptorTsfConfigFormat, ConfigFileService configFileService) { + ConfigKVFile configKVFile; + if (StringUtils.isNotBlank(polarisAdaptorTsfConfigFormat)) { + switch (polarisAdaptorTsfConfigFormat) { + case "properties": + configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName); + case "yaml": + default: + configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName); + } + } + // unknown extension is resolved as yaml file + else if (ConfigFileFormat.isYamlFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) { + configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName); + } + else if (ConfigFileFormat.isPropertyFile(fileName)) { + configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName); + } + else { + LOGGER.warn("[SCTT Config] Unsupported config file. namespace = {}, group = {}, fileName = {}", namespace, + group, fileName); + + throw new IllegalStateException("Only configuration files in the format of properties / yaml / yaml" + + " can be injected into the spring context"); + } + + Map map = new ConcurrentHashMap<>(); + for (String key : configKVFile.getPropertyNames()) { + String value = configKVFile.getProperty(key, null); + if (EncryptConfig.needDecrypt(value)) { + LOGGER.debug("[SCTT Config] Need Decrypt {}: {}", key, value); + value = EncryptConfig.getProvider() + .decrypt(EncryptConfig.realContent(value), EncryptConfig.getPassword()); + } + map.put(key, value); + } + + return new PolarisPropertySource(namespace, group, fileName, configKVFile, map); + } + + private void loadPolarisConfigFile(String namespace, String tsfApplicationId, String tsfGroupId, + String polarisAdaptorTsfConfigFormat, CompositePropertySource compositePropertySource, + ConfigFileService configFileService, String configGroup) { + LOGGER.debug( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer loadPolarisConfigFile start, namespace:{}, group:{}", + namespace, configGroup); + ConfigFileGroup configFileGroup = configFileService.getConfigFileGroup(namespace, configGroup); + if (configFileGroup == null) { + throw new IllegalStateException( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer configFileGroup is null"); + } + List configFileMetadataList = configFileGroup.getConfigFileMetadataList(); + if (!CollectionUtils.isEmpty(configFileMetadataList)) { + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer getConfigFileMetadataList:{}", + configFileMetadataList); + for (ConfigFileMetadata configFile : configFileMetadataList) { + PolarisPropertySource polarisPropertySource = loadPolarisPropertySource(configFile.getNamespace(), + configFile.getFileGroup(), configFile.getFileName(), polarisAdaptorTsfConfigFormat, + configFileService); + + compositePropertySource.addPropertySource(polarisPropertySource); + + PolarisPropertySourceManager.addPropertySource(polarisPropertySource); + + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer Load and inject polaris config file from config group:{}. file = {}", + configGroup, configFile); + } + } + + String namespaceConfigGroup = namespace + "-" + configGroup; + // 用ConcurrentHashSet保证不重复添加ConfigFileGroupChangeListener + if (registedConfigFileGroupListenerSets.add(namespaceConfigGroup)) { + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer configFileGroup addChangeListener namespaceConfigGroup:{}", + namespaceConfigGroup); + configFileGroup.addChangeListener(new ConfigFileGroupChangeListener() { + + @Override + public void onChange(ConfigFileGroupChangedEvent event) { + try { + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer receive onChange event:{}", + event); + List configFileMetadataList = event.getConfigFileMetadataList(); + if (CollectionUtils.isEmpty(configFileMetadataList)) { + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer receive configFileMetadataList is empty"); + return; + } + boolean needRefreshAll = false; + for (ConfigFileMetadata configFile : configFileMetadataList) { + PolarisPropertySource polarisPropertySource = loadPolarisPropertySource( + configFile.getNamespace(), configFile.getFileGroup(), configFile.getFileName(), + polarisAdaptorTsfConfigFormat, configFileService); + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer Load and inject polaris config file from onChange event config group:{}. file = {}", + configGroup, configFile); + // 用ConcurrentHashSet保证不重复注册PolarisConfigPublishChangeListener + if (executeRegisterPublishChangeListener(polarisPropertySource)) { + polarisConfigPropertyAutoRefresher.registerPolarisConfigPublishChangeListener( + polarisPropertySource); + needRefreshAll = true; + } + } + if (needRefreshAll) { + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer start refresh All Config"); + polarisConfigPropertyAutoRefresher.refreshConfigurationProperties(null); + } + } + catch (Exception e) { + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer receive onChange exception:", + e); + } + } + }); + } + + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer loadPolarisConfigFile end, namespace:{}, group:{}", + namespace, configGroup); + } + + /** + * @see PolarisConfigCustomExtensionLayer#executeAfterLocateConfigReturning(CompositePropertySource) + */ + @Override + public void executeAfterLocateConfigReturning(CompositePropertySource compositePropertySource) { + PolarisPropertyCache.getInstance().clear(); + PolarisPropertyCache.getInstance().getCache() + .addAll(new HashSet<>(Arrays.asList(compositePropertySource.getPropertyNames()))); + LOGGER.info("[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer executeAfterLocateConfigReturning finished"); + } + + /** + * @see PolarisConfigCustomExtensionLayer#initRegisterConfig(PolarisConfigPropertyAutoRefresher) + */ + @Override + public void initRegisterConfig(PolarisConfigPropertyAutoRefresher polarisConfigPropertyAutoRefresher) { + LOGGER.info( + "[SCTT Config] PolarisAdaptorTsfConfigExtensionLayer initRegisterConfig polarisConfigPropertyAutoRefresher:{}", + polarisConfigPropertyAutoRefresher.getClass()); + this.polarisConfigPropertyAutoRefresher = polarisConfigPropertyAutoRefresher; + } + + /** + * @see PolarisConfigCustomExtensionLayer#executeRegisterPublishChangeListener(PolarisPropertySource) + */ + @Override + public boolean executeRegisterPublishChangeListener(PolarisPropertySource polarisPropertySource) { + boolean isRegisterSuccess = registedPolarisPropertySets.add(polarisPropertySource.getPropertySourceName()); + if (isRegisterSuccess) { + // 已防止重复注册,仅打印注册成功的即可 + LOGGER.info("[SCTT Config] start to register configFile polarisConfigPublishChangeListener:{}", + polarisPropertySource); + } + return isRegisterSuccess; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/cache/PolarisPropertyCache.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/cache/PolarisPropertyCache.java new file mode 100644 index 00000000..4e80d038 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/cache/PolarisPropertyCache.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.tencent.cloud.polaris.config.tsf.cache; + +import java.util.HashSet; +import java.util.Set; + + +/** + * @author juanyinyang + * @Date 2023年8月8日 下午4:56:18 + */ +public final class PolarisPropertyCache { + + private static final PolarisPropertyCache instance = new PolarisPropertyCache(); + + private final Set cache = new HashSet<>(); + + private PolarisPropertyCache() { + + } + + public static PolarisPropertyCache getInstance() { + return instance; + } + + public Set getCache() { + return cache; + } + + public void clear() { + cache.clear(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/controller/PolarisAdaptorTsfConfigController.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/controller/PolarisAdaptorTsfConfigController.java new file mode 100755 index 00000000..b8719b0f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/controller/PolarisAdaptorTsfConfigController.java @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.controller; + + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.polaris.config.tsf.cache.PolarisPropertyCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author juanyinyang + * @Date 2023年8月2日 下午5:08:29 + */ +@RestController +public class PolarisAdaptorTsfConfigController { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisAdaptorTsfConfigController.class); + + @Autowired + Environment environment; + + public PolarisAdaptorTsfConfigController() { + LOG.info("[SCTT Config] init PolarisAdaptorTsfConfigController"); + } + + /** + * 兼容目前TSF控制台的用法,提供北极星查询当前SDK配置接口. + */ + @RequestMapping("/tsf/innerApi/config/findAllConfig") + public Map findAllConfig() { + Set keys = PolarisPropertyCache.getInstance().getCache(); + return keys.stream() + .collect(HashMap::new, (map, key) -> map.put(key, environment.getProperty(key)), HashMap::putAll); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptAESProvider.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptAESProvider.java new file mode 100644 index 00000000..51f99e8b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptAESProvider.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.encrypt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfigEncryptAESProvider extends ConfigEncryptProvider { + + private static final Logger log = LoggerFactory.getLogger(ConfigEncryptAESProvider.class); + + @Override + public String encrypt(String content, String password) { + try { + return EncryptAlgorithm.AES256.encrypt(content, password); + } + catch (Exception e) { + log.error("[SCTT Config] Error on encrypting.", e); + throw e; + } + } + + @Override + public String decrypt(String encryptedContent, String password) { + try { + return EncryptAlgorithm.AES256.decrypt(encryptedContent, password); + } + catch (Exception e) { + log.error("[SCTT Config] Error on decrypting.", e); + throw e; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProvider.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProvider.java new file mode 100644 index 00000000..e5cafca1 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProvider.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.polaris.config.tsf.encrypt; + +/** + * TSF 配置加密提供器接口. + * + * @author hongweizhu + */ +public abstract class ConfigEncryptProvider { + + /** + * 加密. + * + * @param content 明文 + * @param password 密码 + * @return 密文 + */ + public abstract String encrypt(String content, String password); + + /** + * 解密. + * + * @param encryptedContent 密文 + * @param password 密码 + * @return 明文 + */ + public abstract String decrypt(String encryptedContent, String password); +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProviderFactory.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProviderFactory.java new file mode 100644 index 00000000..980417bb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/ConfigEncryptProviderFactory.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.encrypt; + +public final class ConfigEncryptProviderFactory { + + private static ConfigEncryptProvider configEncryptProvider = null; + + private ConfigEncryptProviderFactory() { + } + + public static ConfigEncryptProvider getInstance() { + if (null == configEncryptProvider) { + try { + Class providerClass = Class.forName(EncryptConfig.getProviderClass()); + configEncryptProvider = (ConfigEncryptProvider) providerClass.newInstance(); + } + catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + e.printStackTrace(); + } + } + return configEncryptProvider; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptAlgorithm.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptAlgorithm.java new file mode 100644 index 00000000..9a795993 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptAlgorithm.java @@ -0,0 +1,146 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.encrypt; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Base64; + +public class EncryptAlgorithm { + public static class AES256 { + + /** + * 加密. + * + * @param content 明文 + * @param password 密钥 + * @return 密文 + */ + public static final String encrypt(String content, String password) { + if (null == password || "".equals(password)) { + throw new PasswordNotFoundException(); + } + try { + // AES SK生成器 + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + // SHA-256摘要密钥后生成安全随机数 + SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); + sr.setSeed(SHA256.encode(password)); + kgen.init(256, sr); + // 生成秘密(对称)密钥 + SecretKey secretKey = kgen.generateKey(); + // 返回基本编码格式的密钥 + byte[] enCodeFormat = secretKey.getEncoded(); + // 根据给定的字节数组构造一个密钥。enCodeFormat:密钥内容;"AES":与给定的密钥内容相关联的密钥算法的名称 + SecretKeySpec skSpec = new SecretKeySpec(enCodeFormat, "AES"); + // 将提供程序添加到下一个可用位置 + Security.addProvider(new BouncyCastleProvider()); + // 创建一个实现指定转换的 Cipher对象,该转换由指定的提供程序提供。 + // "AES/ECB/PKCS7Padding":转换的名称;"BC":提供程序的名称 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); + // 初始化cipher:加密模式 + cipher.init(Cipher.ENCRYPT_MODE, skSpec); + byte[] byteContent = content.getBytes(StandardCharsets.UTF_8); + byte[] cryptograph = cipher.doFinal(byteContent); + byte[] enryptedContent = Base64.encode(cryptograph); + return new String(enryptedContent); + } + catch (Exception e) { + throw new RuntimeException("[SCTT Encrypt] Failed encrypt.", e); + } + } + + /** + * 解密. + * + * @param encryptedContent 密文 + * @param password 密钥 + * @return 明文 + */ + public static final String decrypt(String encryptedContent, String password) { + if (null == password || "".equals(password)) { + throw new PasswordNotFoundException(); + } + try { + // AES SK生成器 + KeyGenerator kgen = KeyGenerator.getInstance("AES"); + // SHA-256摘要密钥后生成安全随机数 + SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); + sr.setSeed(SHA256.encode(password)); + kgen.init(256, sr); + // 生成秘密(对称)密钥 + SecretKey secretKey = kgen.generateKey(); + // 返回基本编码格式的密钥 + byte[] enCodeFormat = secretKey.getEncoded(); + // 根据给定的字节数组构造一个密钥。enCodeFormat:密钥内容;"AES":与给定的密钥内容相关联的密钥算法的名称 + SecretKeySpec skSpec = new SecretKeySpec(enCodeFormat, "AES"); + // 将提供程序添加到下一个可用位置 + Security.addProvider(new BouncyCastleProvider()); + // 创建一个实现指定转换的 Cipher对象,该转换由指定的提供程序提供。 + // "AES/ECB/PKCS7Padding":转换的名称;"BC":提供程序的名称 + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC"); + // 初始化cipher:解密模式 + cipher.init(Cipher.DECRYPT_MODE, skSpec); + byte[] result = cipher.doFinal(Base64.decode(encryptedContent.getBytes(StandardCharsets.UTF_8))); + return new String(result); + } + catch (Exception e) { + throw new RuntimeException("[SCTT Encrypt] Failed decrypt.", e); + } + } + } + + public static class SHA256 { + + /** + * 计算SHA-256摘要. + * + * @param content 原文 + * @return 摘要 + * @throws NoSuchAlgorithmException 算法不存在时抛出 + */ + public static byte[] encode(String content) throws NoSuchAlgorithmException { + MessageDigest digester = MessageDigest.getInstance("SHA-256"); + digester.update(content.getBytes(StandardCharsets.UTF_8)); + return digester.digest(); + } + } + + public static class PasswordNotFoundException extends RuntimeException { + + /** + * serialVersionUID. + */ + private static final long serialVersionUID = -2843758461182470411L; + + public PasswordNotFoundException() { + super("[SCTT Encrypt] Password not found."); + } + + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptConfig.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptConfig.java new file mode 100644 index 00000000..113317be --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/tsf/encrypt/EncryptConfig.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.config.tsf.encrypt; + +import org.springframework.util.StringUtils; + +public final class EncryptConfig { + + private static final String PASSWORD_KEY = "tsf_config_encrypt_password"; + /** + * 加密前缀. + */ + public static String ENCRYPT_PREFIX = "ENC("; + /** + * 加密后缀. + */ + public static String ENCRYPT_SUFFIX = ")"; + /** + * 密码. + */ + private static String password; + /** + * 加解密提供器类名. + */ + private static String providerClass = "com.tencent.cloud.tsf.config.encrypt.ConfigEncryptAESProvider"; + + static { + // 环境变量 + if (null != System.getenv(PASSWORD_KEY)) { + password = System.getenv(PASSWORD_KEY); + } + // JVM参数 + if (null != System.getProperty(PASSWORD_KEY)) { + password = System.getProperty(PASSWORD_KEY); + } + } + + private EncryptConfig() { + + } + + /** + * 是否开启配置,判断 password 是否为空. + */ + public static Boolean getEnabled() { + return !StringUtils.isEmpty(password); + } + + public static String getPassword() { + return EncryptConfig.password; + } + + public static void setPassword(String password) { + EncryptConfig.password = password; + } + + public static ConfigEncryptProvider getProvider() { + return ConfigEncryptProviderFactory.getInstance(); + } + + public static String getProviderClass() { + return providerClass; + } + + public static void setProviderClass(String providerClass) { + EncryptConfig.providerClass = providerClass; + } + + /** + * 是否需要进行解密. + * + * @param content 判断对象 + * @return true:需要解密;false:不需要解密 + */ + public static Boolean needDecrypt(Object content) { + if (null == content) { + return false; + } + else { + String stringValue = String.valueOf(content); + return stringValue.startsWith(ENCRYPT_PREFIX) && stringValue.endsWith(ENCRYPT_SUFFIX); + } + } + + /** + * 获取真实密文. + * + * @param content 原始配置值 + * @return 真实密文 + */ + public static String realContent(Object content) { + if (null != content) { + String stringValue = String.valueOf(content); + return stringValue.substring(ENCRYPT_PREFIX.length(), stringValue.length() - ENCRYPT_SUFFIX.length()); + } + return null; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeCallback.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeCallback.java new file mode 100644 index 00000000..8845ac39 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeCallback.java @@ -0,0 +1,28 @@ +/* + * 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.tsf.consul.config.watch; + +public interface ConfigChangeCallback { + + /** + * 配置变更回调函数. + * @param lastConfigProperty 旧的配置属性 + * @param newConfigProperty 新的配置属性 + */ + void callback(ConfigProperty lastConfigProperty, ConfigProperty newConfigProperty); +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeListener.java new file mode 100644 index 00000000..7f511538 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigChangeListener.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.tsf.consul.config.watch; + + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface ConfigChangeListener { + String prefix() default ""; + + String[] value() default {}; + + boolean async() default false; + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigProperty.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigProperty.java new file mode 100644 index 00000000..45418efb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/ConfigProperty.java @@ -0,0 +1,46 @@ +/* + * 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.tsf.consul.config.watch; + +public class ConfigProperty { + + private String key; + + private Object value; + + public ConfigProperty(String key, Object value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/TsfConsulConfigRefreshEventListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/TsfConsulConfigRefreshEventListener.java new file mode 100644 index 00000000..688ffbe9 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/tsf/consul/config/watch/TsfConsulConfigRefreshEventListener.java @@ -0,0 +1,122 @@ +/* + * 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.tsf.consul.config.watch; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent; +import com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext; +import com.tencent.cloud.polaris.config.listener.SyncConfigChangeListener; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.lang.NonNull; +import org.springframework.util.StringUtils; + +public class TsfConsulConfigRefreshEventListener implements BeanPostProcessor, PriorityOrdered { + private static final String DOT = "."; + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + @Override + public Object postProcessBeforeInitialization(@NonNull Object obj, @NonNull String beanName) throws BeansException { + return obj; + } + + @Override + public Object postProcessAfterInitialization(@NonNull Object obj, @NonNull String beanName) throws BeansException { + Class clz = obj.getClass(); + if (!clz.isAnnotationPresent(ConfigChangeListener.class) || !ConfigChangeCallback.class.isAssignableFrom(clz)) { + return obj; + } + + ConfigChangeListener targetAnno = clz.getAnnotation(ConfigChangeListener.class); + String watchedPrefix = targetAnno.prefix(); + String[] watchedConfirmedValue = targetAnno.value(); + boolean isAsync = targetAnno.async(); + if (watchedConfirmedValue.length == 0 && StringUtils.isEmpty(watchedPrefix)) { + return obj; + } + + ConfigChangeCallback bean = (ConfigChangeCallback) obj; + com.tencent.cloud.polaris.config.listener.ConfigChangeListener listener = new SyncConfigChangeListener() { + @Override + public void onChange(ConfigChangeEvent changeEvent) { + List paramList = parseConfigChangeEventToTsfCallbackParam(changeEvent); + for (TsfCallbackParam param : paramList) { + if (isAsync()) { + PolarisConfigListenerContext.executor() + .execute(() -> bean.callback(param.oldValue, param.newValue)); + } + else { + bean.callback(param.oldValue, param.newValue); + } + } + } + + @Override + public boolean isAsync() { + return isAsync; + } + }; + + Set interestedKeys = new HashSet<>(); + Set interestedKeyPrefixes = new HashSet<>(); + if (watchedConfirmedValue.length > 0) { + for (String value : watchedConfirmedValue) { + interestedKeys.add(StringUtils.isEmpty(watchedPrefix) ? value : watchedPrefix + DOT + value); + } + } + else { + interestedKeyPrefixes.add(watchedPrefix); + } + + PolarisConfigListenerContext.addChangeListener(listener, interestedKeys, interestedKeyPrefixes); + return bean; + } + + private List parseConfigChangeEventToTsfCallbackParam(ConfigChangeEvent event) { + List result = new ArrayList<>(); + Set changedKeys = event.changedKeys(); + for (String changedKey : changedKeys) { + ConfigProperty oldValue = new ConfigProperty(changedKey, event.getChange(changedKey).getOldValue()); + ConfigProperty newValue = new ConfigProperty(changedKey, event.getChange(changedKey).getNewValue()); + TsfCallbackParam param = new TsfCallbackParam(oldValue, newValue); + result.add(param); + } + return result; + } + + static class TsfCallbackParam { + ConfigProperty oldValue; + ConfigProperty newValue; + + TsfCallbackParam(ConfigProperty oldValue, ConfigProperty newValue) { + this.oldValue = oldValue; + this.newValue = newValue; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/services/com.tencent.cloud.polaris.config.adapter.PolarisConfigCustomExtensionLayer b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/services/com.tencent.cloud.polaris.config.adapter.PolarisConfigCustomExtensionLayer new file mode 100644 index 00000000..427220a3 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/services/com.tencent.cloud.polaris.config.adapter.PolarisConfigCustomExtensionLayer @@ -0,0 +1 @@ +com.tencent.cloud.polaris.config.tsf.adaptor.PolarisAdaptorTsfConfigExtensionLayer diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories index f7b96cfb..d53ac4b4 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories @@ -1,6 +1,6 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration - + com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration,\ + com.tencent.cloud.polaris.config.tsf.PolarisAdaptorTsfConfigBootstrapConfiguration # ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ com.tencent.cloud.polaris.config.configdata.PolarisConfigDataLocationResolver diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index c68a45bd..bad1ece4 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration com.tencent.cloud.polaris.config.endpoint.PolarisConfigEndpointAutoConfiguration com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration +com.tencent.cloud.polaris.config.tsf.PolarisAdaptorTsfConfigAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfApiMetadataGrapher.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfApiMetadataGrapher.java new file mode 100644 index 00000000..4278d709 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfApiMetadataGrapher.java @@ -0,0 +1,137 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.contract.tsf; + +import java.util.concurrent.atomic.AtomicBoolean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tencent.cloud.common.util.GzipUtil; +import io.swagger.v3.oas.models.OpenAPI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springdoc.api.AbstractOpenApiResource; +import org.springdoc.api.AbstractOpenApiResourceUtil; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springdoc.webflux.api.OpenApiWebFluxUtil; +import org.springdoc.webmvc.api.OpenApiWebMvcUtil; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.SmartLifecycle; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +public class TsfApiMetadataGrapher implements SmartLifecycle { + + private final AtomicBoolean isRunning = new AtomicBoolean(false); + private final org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource; + private final org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource; + private final ObjectMapperProvider springdocObjectMapperProvider; + private Logger logger = LoggerFactory.getLogger(TsfApiMetadataGrapher.class); + private ApplicationContext applicationContext; + private String groupName; + + public TsfApiMetadataGrapher(org.springdoc.webmvc.api.MultipleOpenApiResource multipleOpenApiWebMvcResource, + org.springdoc.webflux.api.MultipleOpenApiResource multipleOpenApiWebFluxResource, + String groupName, ApplicationContext applicationContext, ObjectMapperProvider springdocObjectMapperProvider) { + this.applicationContext = applicationContext; + this.multipleOpenApiWebMvcResource = multipleOpenApiWebMvcResource; + this.multipleOpenApiWebFluxResource = multipleOpenApiWebFluxResource; + this.groupName = groupName; + this.springdocObjectMapperProvider = springdocObjectMapperProvider; + } + + @Override + public boolean isAutoStartup() { + return true; + } + + @Override + public void stop(Runnable runnable) { + runnable.run(); + stop(); + } + + @Override + public void start() { + if (!isRunning.compareAndSet(false, true)) { + return; + } + try { + AbstractOpenApiResource openApiResource = null; + if (multipleOpenApiWebMvcResource != null) { + openApiResource = OpenApiWebMvcUtil.getOpenApiResourceOrThrow(multipleOpenApiWebMvcResource, groupName); + } + else if (multipleOpenApiWebFluxResource != null) { + openApiResource = OpenApiWebFluxUtil.getOpenApiResourceOrThrow(multipleOpenApiWebFluxResource, groupName); + } + OpenAPI openAPI = null; + if (openApiResource != null) { + openAPI = AbstractOpenApiResourceUtil.getOpenApi(openApiResource); + } + String jsonValue; + if (springdocObjectMapperProvider != null && springdocObjectMapperProvider.jsonMapper() != null) { + jsonValue = springdocObjectMapperProvider.jsonMapper().writeValueAsString(openAPI); + } + else { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + jsonValue = mapper.writeValueAsString(openAPI); + } + if (openAPI != null && !StringUtils.isEmpty(jsonValue)) { + String serviceApiMeta = GzipUtil.compressBase64Encode(jsonValue, "utf-8"); + Environment environment = applicationContext.getEnvironment(); + String tsfToken = environment.getProperty("tsf_token"); + String tsfGroupId = environment.getProperty("tsf_group_id"); + if (StringUtils.isEmpty(tsfGroupId) || StringUtils.isEmpty(tsfToken)) { + logger.info("[tsf-swagger] auto smart check application start with local consul, api registry not work"); + return; + } + logger.info("[tsf-swagger] api_meta len: {}", serviceApiMeta.length()); + String applicationName = environment.getProperty("spring.application.name"); + if (logger.isDebugEnabled()) { + logger.debug("[tsf-swagger] service: {} openApi json data: {}", applicationName, jsonValue); + logger.debug("[tsf-swagger] service: {} api_meta info: {}", applicationName, serviceApiMeta); + } + + System.setProperty(String.format("$%s", "api_metas"), serviceApiMeta); + } + else { + logger.warn("[tsf-swagger] swagger or json is null, openApiResource keys:{}, group:{}", openApiResource, groupName); + } + } + catch (Throwable t) { + logger.error("[tsf swagger] init TsfApiMetadataGrapher failed. occur exception: ", t); + } + } + + @Override + public void stop() { + isRunning.set(true); + } + + @Override + public boolean isRunning() { + return isRunning.get(); + } + + @Override + public int getPhase() { + return -2; + } +} diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractProperties.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractProperties.java new file mode 100644 index 00000000..74e48d80 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractProperties.java @@ -0,0 +1,136 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.contract.tsf; + +import com.tencent.cloud.polaris.contract.config.ExtendedContractProperties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties for TSF contract. + * + * @author Haotian Zhang + */ +@ConfigurationProperties("tsf.swagger") +public class TsfContractProperties implements ExtendedContractProperties { + + @Value("${tsf.swagger.basePackage:}") + private String basePackage; + + @Value("${tsf.swagger.excludePath:}") + private String excludePath; + + @Value("${tsf.swagger.enabled:true}") + private boolean enabled; + + @Value("${tsf.swagger.group:default}") + private String groupName; + + @Value("${tsf.swagger.basePath:/**}") + private String basePath; + + @Value("${tsf.swagger.doc.auto-startup:true}") + private boolean exposure; + + /** + * applicationId 应用Id. + */ + @Value("${tsf_application_id:}") + private String name; + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String getBasePackage() { + return basePackage; + } + + @Override + public void setBasePackage(String basePackage) { + this.basePackage = basePackage; + } + + @Override + public String getExcludePath() { + return excludePath; + } + + @Override + public void setExcludePath(String excludePath) { + this.excludePath = excludePath; + } + + @Override + public String getGroup() { + return groupName; + } + + @Override + public void setGroup(String group) { + this.groupName = group; + } + + @Override + public String getBasePath() { + return basePath; + } + + @Override + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + @Override + public boolean isExposure() { + return exposure; + } + + @Override + public void setExposure(boolean exposure) { + this.exposure = exposure; + } + + @Override + public boolean isReportEnabled() { + return enabled; + } + + @Override + public void setReportEnabled(boolean reportEnabled) { + + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesAutoConfiguration.java new file mode 100644 index 00000000..4d0d26ca --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesAutoConfiguration.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.polaris.contract.tsf; + +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for TSF contract properties. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +public class TsfContractPropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public TsfContractProperties tsfContractProperties() { + return new TsfContractProperties(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesBootstrapConfiguration.java new file mode 100644 index 00000000..3969f223 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfContractPropertiesBootstrapConfiguration.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.polaris.contract.tsf; + +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Bootstrap configuration for TSF contract properties. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +@Import(TsfContractPropertiesAutoConfiguration.class) +public class TsfContractPropertiesBootstrapConfiguration { + +} diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfSwaggerAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfSwaggerAutoConfiguration.java new file mode 100644 index 00000000..c50f6b67 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/java/com/tencent/cloud/polaris/contract/tsf/TsfSwaggerAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package com.tencent.cloud.polaris.contract.tsf; + +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; +import com.tencent.cloud.polaris.contract.config.PolarisContractProperties; +import io.swagger.v3.oas.models.OpenAPI; +import org.springdoc.core.providers.ObjectMapperProvider; +import org.springdoc.webflux.api.MultipleOpenApiWebFluxResource; +import org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +@EnableWebMvc +@Configuration +@ConditionalOnTsfEnabled +@ConditionalOnClass(name = "org.springframework.web.servlet.config.annotation.EnableWebMvc") +@ConditionalOnProperty(value = "tsf.swagger.enabled", havingValue = "true", matchIfMissing = true) +public class TsfSwaggerAutoConfiguration { + + @Bean + @ConditionalOnBean(OpenAPI.class) + public TsfApiMetadataGrapher tsfApiMetadataGrapher(@Nullable MultipleOpenApiWebMvcResource multipleOpenApiWebMvcResource, + @Nullable MultipleOpenApiWebFluxResource multipleOpenApiWebFluxResource, ApplicationContext context, + PolarisContractProperties polarisContractProperties, ObjectMapperProvider springdocObjectMapperProvider) { + return new TsfApiMetadataGrapher(multipleOpenApiWebMvcResource, multipleOpenApiWebFluxResource, + polarisContractProperties.getGroup(), context, springdocObjectMapperProvider); + } +} diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring.factories index bd639443..a796fcd9 100644 --- a/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.contract.config.PolarisContractPropertiesBootstrapConfiguration + com.tencent.cloud.polaris.contract.config.PolarisContractPropertiesBootstrapConfiguration,\ + com.tencent.cloud.polaris.contract.tsf.TsfContractPropertiesBootstrapConfiguration org.springframework.context.ApplicationListener=\ com.tencent.cloud.polaris.contract.PolarisSwaggerApplicationListener diff --git a/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 78cf3d95..300512ae 100644 --- a/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-starter-tencent-polaris-contract/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,4 @@ com.tencent.cloud.polaris.contract.config.PolarisSwaggerAutoConfiguration com.tencent.cloud.polaris.contract.config.PolarisContractPropertiesAutoConfiguration +com.tencent.cloud.polaris.contract.tsf.TsfContractPropertiesAutoConfiguration +com.tencent.cloud.polaris.contract.tsf.TsfSwaggerAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-discovery/pom.xml b/spring-cloud-starter-tencent-polaris-discovery/pom.xml index 62ff5819..6e0ec314 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/pom.xml +++ b/spring-cloud-starter-tencent-polaris-discovery/pom.xml @@ -19,18 +19,17 @@ com.tencent.cloud spring-cloud-tencent-rpc-enhancement - - - com.tencent.polaris - polaris-test-common - test + com.tencent.cloud + spring-cloud-tencent-lossless-plugin + + com.tencent.polaris - connector-consul + polaris-test-common test @@ -83,6 +82,11 @@ true + + joda-time + joda-time + + org.mockito mockito-inline 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 80b17a1c..12d444a6 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 @@ -135,8 +135,8 @@ public class PolarisServiceRegistry implements ServiceRegistry()); + } + // 删除可能存在的consul发现配置 + for (DiscoveryConfigImpl dc : configuration.getConsumer().getDiscoveries()) { + if (StringUtils.equals(dc.getServerConnectorId(), RegistrationUtil.ID)) { + configuration.getConsumer().getDiscoveries().remove(dc); + } + } + // 删除可能存在的consul注册配置 + for (RegisterConfigImpl rc : configuration.getProvider().getRegisters()) { + if (StringUtils.equals(rc.getServerConnectorId(), RegistrationUtil.ID)) { + configuration.getProvider().getRegisters().remove(rc); + } + } + + // 如果ServerConnectors为空,则把ServerConnector(如有)复制过去 + if (CollectionUtils.isEmpty(configuration.getGlobal().getServerConnectors()) + && null != configuration.getGlobal().getServerConnector()) { + configuration.getGlobal().getServerConnectors().add(configuration.getGlobal().getServerConnector()); + } + if (consulEnable) { + // enable consul + ServerConnectorConfigImpl serverConnectorConfig = new ServerConnectorConfigImpl(); + serverConnectorConfig.setId(RegistrationUtil.ID); + serverConnectorConfig.setAddresses( + Collections.singletonList(tsfConsulProperties.getHost() + ":" + tsfConsulProperties.getPort())); + LOGGER.info("Will register to consul server: [" + tsfConsulProperties.getHost() + ":" + tsfConsulProperties.getPort() + "]"); + serverConnectorConfig.setProtocol(DefaultPlugins.SERVER_CONNECTOR_CONSUL); + + Map metadata = serverConnectorConfig.getMetadata(); + String appName = RegistrationUtil.getAppName(tsfDiscoveryProperties, context.getEnvironment()); + metadata.put(ConsulConstant.MetadataMapKey.SERVICE_NAME_KEY, RegistrationUtil.normalizeForDns(appName)); + metadata.put(ConsulConstant.MetadataMapKey.INSTANCE_ID_KEY, RegistrationUtil.getInstanceId(tsfDiscoveryProperties, context)); + if (StringUtils.isNotBlank(tsfDiscoveryProperties.getAclToken())) { + serverConnectorConfig.setToken(tsfDiscoveryProperties.getAclToken()); + } + metadata.put(ConsulConstant.MetadataMapKey.TAGS_KEY, JacksonUtils.serialize2Json(RegistrationUtil.createTags(tsfDiscoveryProperties))); + if (StringUtils.isNotBlank(tsfDiscoveryProperties.getDefaultQueryTag())) { + metadata.put(ConsulConstant.MetadataMapKey.QUERY_TAG_KEY, tsfDiscoveryProperties.getDefaultQueryTag()); + } + metadata.put(ConsulConstant.MetadataMapKey.QUERY_PASSING_KEY, String.valueOf(tsfDiscoveryProperties.isQueryPassing())); + if (tsfDiscoveryProperties.isPreferIpAddress() + && StringUtils.isNotBlank(tsfDiscoveryProperties.getIpAddress())) { + metadata.put(ConsulConstant.MetadataMapKey.PREFER_IP_ADDRESS_KEY, + String.valueOf(tsfDiscoveryProperties.isPreferIpAddress())); + metadata.put(ConsulConstant.MetadataMapKey.IP_ADDRESS_KEY, tsfDiscoveryProperties.getIpAddress()); + } + if (!tsfDiscoveryProperties.isPreferAgentAddress()) { + metadata.put(ConsulConstant.MetadataMapKey.PREFER_IP_ADDRESS_KEY, + String.valueOf(tsfDiscoveryProperties.isPreferIpAddress())); + metadata.put(ConsulConstant.MetadataMapKey.IP_ADDRESS_KEY, tsfDiscoveryProperties.getHostname()); + } + configuration.getGlobal().getServerConnectors().add(serverConnectorConfig); + + // 添加发现配置 + DiscoveryConfigImpl discoveryConfig = new DiscoveryConfigImpl(); + discoveryConfig.setServerConnectorId(RegistrationUtil.ID); + discoveryConfig.setEnable(tsfDiscoveryProperties.isEnabled()); + configuration.getConsumer().getDiscoveries().add(discoveryConfig); + + // 添加注册配置 + RegisterConfigImpl registerConfig = new RegisterConfigImpl(); + registerConfig.setServerConnectorId(RegistrationUtil.ID); + registerConfig.setEnable(tsfDiscoveryProperties.isRegister()); + configuration.getProvider().getRegisters().add(registerConfig); + } + + if (polarisDiscoveryProperties != null) { + if (!polarisEnable) { + configuration.getGlobal().getAPI().setReportEnable(false); + for (DiscoveryConfigImpl dc : configuration.getConsumer().getDiscoveries()) { + if (StringUtils.equals(dc.getServerConnectorId(), "polaris")) { + dc.setEnable(false); + } + } + for (RegisterConfigImpl rc : configuration.getProvider().getRegisters()) { + if (StringUtils.equals(rc.getServerConnectorId(), "polaris")) { + rc.setEnable(false); + rc.setReportServiceContractEnable(false); + } + } + } + } + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.CONSUL_DISCOVERY_CONFIG_ORDER + 1; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryProperties.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryProperties.java new file mode 100644 index 00000000..7e2e1f92 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryProperties.java @@ -0,0 +1,744 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.tencent.cloud.common.util.inet.PolarisInetUtils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.commons.util.InetUtils; + +/** + * Defines configuration for service discovery and registration. + * + * @author Spencer Gibb + * @author Donnabell Dmello + * @author Venil Noronha + * @author Richard Kettelerij + */ +@ConfigurationProperties("tsf.discovery") +public class TsfDiscoveryProperties { + + protected static final String MANAGEMENT = "management"; + + private InetUtils.HostInfo hostInfo; + + @Value("${tsf_token:${consul.token:${CONSUL_TOKEN:${spring.cloud.consul.token:${SPRING_CLOUD_CONSUL_TOKEN:}}}}}") + private String aclToken; + + /** + * Tags to use when registering service. + */ + private List tags = new ArrayList<>(); + + /** + * If service discovery enabled. + */ + private boolean enabled = true; + + /** + * Tags to use when registering management service. + */ + private List managementTags = new ArrayList<>(); + + /** + * Alternate server path to invoke for health checking . + */ + private String healthCheckPath = "/actuator/health"; + + /** + * Custom health check url to override default. + */ + private String healthCheckUrl; + + /** + * How often to perform the health check (e.g. 10s), defaults to 10s. + */ + private String healthCheckInterval = "10s"; + + /** + * Timeout for health check (e.g. 10s). + */ + private String healthCheckTimeout; + + /** + * Timeout to deregister services critical for longer than timeout (e.g. 30m). + * Requires consul version 7.x or higher. + */ + private String healthCheckCriticalTimeout; + + /** + * IP address to use when accessing service (must also set preferIpAddress to use). + */ + private String ipAddress; + + /** + * Hostname to use when accessing server. + */ + private String hostname; + + /** + * Port to register the service under (defaults to listening port). + */ + private Integer port; + + /** + * Port to register the management service under (defaults to management port). + */ + private Integer managementPort; + + private Lifecycle lifecycle = new Lifecycle(); + + /** + * Use ip address rather than hostname during registration. + * 默认使用IP地址 + */ + private boolean preferIpAddress = true; + + /** + * Source of how we will determine the address to use. + */ + private boolean preferAgentAddress = false; + + /** + * The delay between calls to watch consul catalog in millis, default is 1000. + */ + private int catalogServicesWatchDelay = 1000; + + /** + * The number of seconds to block while watching consul catalog, default is 2. + */ + private int catalogServicesWatchTimeout = 55; + + /** + * Service name. + */ + private String serviceName; + + /** + * Unique service instance id. + */ + @Value("${tsf_instance_id:${spring.cloud.consul.discovery.instanceId:${SPRING_CLOUD_CONSUL_DISCOVERY_INSTANCEID:}}}") + private String instanceId; + + /** + * Service instance zone. + */ + private String instanceZone; + + /** + * Service instance group. + */ + private String instanceGroup; + + /** + * Service instance zone comes from metadata. + * This allows changing the metadata tag name. + */ + private String defaultZoneMetadataName = "zone"; + + /** + * Whether to register an http or https service. + */ + private String scheme = "http"; + + /** + * Suffix to use when registering management service. + */ + private String managementSuffix = MANAGEMENT; + + /** + * Map of serviceId's -> tag to query for in server list. + * This allows filtering services by a single tag. + */ + private Map serverListQueryTags = new HashMap<>(); + + /** + * Map of serviceId's -> datacenter to query for in server list. + * This allows looking up services in another datacenters. + */ + private Map datacenters = new HashMap<>(); + + /** + * Tag to query for in service list if one is not listed in serverListQueryTags. + */ + private String defaultQueryTag; + + /** + * Add the 'passing` parameter to /v1/health/service/serviceName. + * This pushes health check passing to the server. + */ + private boolean queryPassing = true; + + /** + * Register as a service in consul. + */ + private boolean register = true; + + /** + * Disable automatic de-registration of service in consul. + */ + private boolean deregister = true; + + /** + * Register health check in consul. Useful during development of a service. + */ + private boolean registerHealthCheck = true; + + /** + * Throw exceptions during service registration if true, otherwise, log + * warnings (defaults to true). + */ + private boolean failFast = true; + + /** + * Skips certificate verification during service checks if true, otherwise + * runs certificate verification. + */ + private Boolean healthCheckTlsSkipVerify; + + /** + * tsf service consul registration tags. + * + * applicationId 应用Id + */ + @Value("${tsf_application_id:}") + private String tsfApplicationId; + + /** + * tsf service consul registration tags. + * + * groupId 部署组Id + */ + @Value("${tsf_group_id:}") + private String tsfGroupId; + + /** + * 仅本地测试时使用. + */ + @Value("${tsf_namespace_id:}") + private String tsfNamespaceId; + + /** + * tsf service consul registration tags. + * + * progVersion 包版本 + */ + @Value("${tsf_prog_version:}") + private String tsfProgVersion; + + /** + * tsf service consul registration tags. + * + * 地域信息 + */ + @Value("${tsf_region:}") + private String tsfRegion; + + /** + * tsf service consul registration tags. + * + * 可用区信息 + */ + @Value("${tsf_zone:}") + private String tsfZone; + + /** + * 有状态服务回调的线程池. + */ + private int callbackPoolSize = 10; + + private long callbackInitialDelay = 10 * 1000L; + + private long callbackErrorDelay = 30 * 1000L; + + + /** + * 是否开启零实例保护,默认开启。开启时如果 consul 返回在线实例为0,用上次的缓存(正常来说线上环境不应该有provider全下线的情况). + * 定制化双发现时可能需要关闭,此时如果provider下线,则返回空列表。如果 consul 连接不上则用缓存. + */ + private boolean zeroInstanceProtect = true; + + private int testConnectivityTimeout = 5000; + + private Map serviceMeta; + + @SuppressWarnings("unused") + private TsfDiscoveryProperties() { + this.managementTags.add(MANAGEMENT); + } + + public TsfDiscoveryProperties(PolarisInetUtils polarisInetUtils) { + this(); + this.hostInfo = polarisInetUtils.findFirstNonLoopbackHostInfo(); + this.ipAddress = this.hostInfo.getIpAddress(); + this.hostname = this.hostInfo.getHostname(); + } + + /** + * @param serviceId The service who's filtering tag is being looked up + * @return The tag the given service id should be filtered by, or null. + */ + public String getQueryTagForService(String serviceId) { + String tag = serverListQueryTags.get(serviceId); + return tag != null ? tag : defaultQueryTag; + } + + public String getHostname() { + return this.preferIpAddress ? this.ipAddress : this.hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + this.hostInfo.override = true; + } + + private InetUtils.HostInfo getHostInfo() { + return hostInfo; + } + + private void setHostInfo(InetUtils.HostInfo hostInfo) { + this.hostInfo = hostInfo; + } + + public String getAclToken() { + return aclToken; + } + + public void setAclToken(String aclToken) { + this.aclToken = aclToken; + } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public List getManagementTags() { + return managementTags; + } + + public void setManagementTags(List managementTags) { + this.managementTags = managementTags; + } + + public String getHealthCheckPath() { + return healthCheckPath; + } + + public void setHealthCheckPath(String healthCheckPath) { + this.healthCheckPath = healthCheckPath; + } + + public String getHealthCheckUrl() { + return healthCheckUrl; + } + + public void setHealthCheckUrl(String healthCheckUrl) { + this.healthCheckUrl = healthCheckUrl; + } + + public String getHealthCheckInterval() { + return healthCheckInterval; + } + + public void setHealthCheckInterval(String healthCheckInterval) { + this.healthCheckInterval = healthCheckInterval; + } + + public String getHealthCheckTimeout() { + return healthCheckTimeout; + } + + public void setHealthCheckTimeout(String healthCheckTimeout) { + this.healthCheckTimeout = healthCheckTimeout; + } + + public String getHealthCheckCriticalTimeout() { + return healthCheckCriticalTimeout; + } + + public void setHealthCheckCriticalTimeout(String healthCheckCriticalTimeout) { + this.healthCheckCriticalTimeout = healthCheckCriticalTimeout; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + this.hostInfo.override = true; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getManagementPort() { + return managementPort; + } + + public void setManagementPort(Integer managementPort) { + this.managementPort = managementPort; + } + + public Lifecycle getLifecycle() { + return lifecycle; + } + + public void setLifecycle(Lifecycle lifecycle) { + this.lifecycle = lifecycle; + } + + public boolean isPreferIpAddress() { + return preferIpAddress; + } + + public void setPreferIpAddress(boolean preferIpAddress) { + this.preferIpAddress = preferIpAddress; + } + + public boolean isPreferAgentAddress() { + return preferAgentAddress; + } + + public void setPreferAgentAddress(boolean preferAgentAddress) { + this.preferAgentAddress = preferAgentAddress; + } + + public int getCatalogServicesWatchDelay() { + return catalogServicesWatchDelay; + } + + public void setCatalogServicesWatchDelay(int catalogServicesWatchDelay) { + this.catalogServicesWatchDelay = catalogServicesWatchDelay; + } + + public int getCatalogServicesWatchTimeout() { + return catalogServicesWatchTimeout; + } + + public void setCatalogServicesWatchTimeout(int catalogServicesWatchTimeout) { + this.catalogServicesWatchTimeout = catalogServicesWatchTimeout; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getInstanceId() { + return instanceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public String getInstanceZone() { + return instanceZone; + } + + public void setInstanceZone(String instanceZone) { + this.instanceZone = instanceZone; + } + + public String getInstanceGroup() { + return instanceGroup; + } + + public void setInstanceGroup(String instanceGroup) { + this.instanceGroup = instanceGroup; + } + + public String getDefaultZoneMetadataName() { + return defaultZoneMetadataName; + } + + public void setDefaultZoneMetadataName(String defaultZoneMetadataName) { + this.defaultZoneMetadataName = defaultZoneMetadataName; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getManagementSuffix() { + return managementSuffix; + } + + public void setManagementSuffix(String managementSuffix) { + this.managementSuffix = managementSuffix; + } + + public Map getServerListQueryTags() { + return serverListQueryTags; + } + + public void setServerListQueryTags(Map serverListQueryTags) { + this.serverListQueryTags = serverListQueryTags; + } + + public Map getDatacenters() { + return datacenters; + } + + public void setDatacenters(Map datacenters) { + this.datacenters = datacenters; + } + + public String getDefaultQueryTag() { + return defaultQueryTag; + } + + public void setDefaultQueryTag(String defaultQueryTag) { + this.defaultQueryTag = defaultQueryTag; + } + + public boolean isQueryPassing() { + return queryPassing; + } + + public void setQueryPassing(boolean queryPassing) { + this.queryPassing = queryPassing; + } + + public boolean isRegister() { + return register; + } + + public void setRegister(boolean register) { + this.register = register; + } + + public boolean isDeregister() { + return deregister; + } + + public void setDeregister(boolean deregister) { + this.deregister = deregister; + } + + public boolean isRegisterHealthCheck() { + return registerHealthCheck; + } + + public void setRegisterHealthCheck(boolean registerHealthCheck) { + this.registerHealthCheck = registerHealthCheck; + } + + public boolean isFailFast() { + return failFast; + } + + public void setFailFast(boolean failFast) { + this.failFast = failFast; + } + + public Boolean getHealthCheckTlsSkipVerify() { + return healthCheckTlsSkipVerify; + } + + public void setHealthCheckTlsSkipVerify(Boolean healthCheckTlsSkipVerify) { + this.healthCheckTlsSkipVerify = healthCheckTlsSkipVerify; + } + + public String getTsfApplicationId() { + return tsfApplicationId; + } + + public void setTsfApplicationId(final String tsfApplicationId) { + this.tsfApplicationId = tsfApplicationId; + } + + public String getTsfGroupId() { + return tsfGroupId; + } + + public void setTsfGroupId(final String tsfGroupId) { + this.tsfGroupId = tsfGroupId; + } + + public String getTsfNamespaceId() { + return tsfNamespaceId; + } + + public void setTsfNamespaceId(String tsfNamespaceId) { + this.tsfNamespaceId = tsfNamespaceId; + } + + public String getTsfProgVersion() { + return tsfProgVersion; + } + + public void setTsfProgVersion(final String tsfProgVersion) { + this.tsfProgVersion = tsfProgVersion; + } + + public String getTsfRegion() { + return tsfRegion; + } + + public void setTsfRegion(final String tsfRegion) { + this.tsfRegion = tsfRegion; + } + + public String getTsfZone() { + return tsfZone; + } + + public void setTsfZone(final String tsfZone) { + this.tsfZone = tsfZone; + } + + public Map getServiceMeta() { + return serviceMeta; + } + + public void setServiceMeta(final Map serviceMeta) { + this.serviceMeta = serviceMeta; + } + + public int getCallbackPoolSize() { + return callbackPoolSize; + } + + public void setCallbackPoolSize(int callbackPoolSize) { + this.callbackPoolSize = callbackPoolSize; + } + + public long getCallbackInitialDelay() { + return callbackInitialDelay; + } + + public void setCallbackInitialDelay(long callbackInitialDelay) { + this.callbackInitialDelay = callbackInitialDelay; + } + + public long getCallbackErrorDelay() { + return callbackErrorDelay; + } + + public void setCallbackErrorDelay(long callbackErrorDelay) { + this.callbackErrorDelay = callbackErrorDelay; + } + + public boolean isZeroInstanceProtect() { + return zeroInstanceProtect; + } + + public void setZeroInstanceProtect(boolean zeroInstanceProtect) { + this.zeroInstanceProtect = zeroInstanceProtect; + } + + public int getTestConnectivityTimeout() { + return testConnectivityTimeout; + } + + public void setTestConnectivityTimeout(int testConnectivityTimeout) { + this.testConnectivityTimeout = testConnectivityTimeout; + } + + @Override + public String toString() { + return "ConsulDiscoveryProperties{" + + "hostInfo=" + hostInfo + + ", aclToken='" + aclToken + '\'' + + ", tags=" + tags + + ", enabled=" + enabled + + ", managementTags=" + managementTags + + ", healthCheckPath='" + healthCheckPath + '\'' + + ", healthCheckUrl='" + healthCheckUrl + '\'' + + ", healthCheckInterval='" + healthCheckInterval + '\'' + + ", healthCheckTimeout='" + healthCheckTimeout + '\'' + + ", healthCheckCriticalTimeout='" + healthCheckCriticalTimeout + '\'' + + ", ipAddress='" + ipAddress + '\'' + + ", hostname='" + hostname + '\'' + + ", port=" + port + + ", managementPort=" + managementPort + + ", lifecycle=" + lifecycle + + ", preferIpAddress=" + preferIpAddress + + ", preferAgentAddress=" + preferAgentAddress + + ", catalogServicesWatchDelay=" + catalogServicesWatchDelay + + ", catalogServicesWatchTimeout=" + catalogServicesWatchTimeout + + ", serviceName='" + serviceName + '\'' + + ", instanceId='" + instanceId + '\'' + + ", instanceZone='" + instanceZone + '\'' + + ", instanceGroup='" + instanceGroup + '\'' + + ", defaultZoneMetadataName='" + defaultZoneMetadataName + '\'' + + ", scheme='" + scheme + '\'' + + ", managementSuffix='" + managementSuffix + '\'' + + ", serverListQueryTags=" + serverListQueryTags + + ", datacenters=" + datacenters + + ", defaultQueryTag='" + defaultQueryTag + '\'' + + ", queryPassing=" + queryPassing + + ", register=" + register + + ", deregister=" + deregister + + ", registerHealthCheck=" + registerHealthCheck + + ", failFast=" + failFast + + ", healthCheckTlsSkipVerify=" + healthCheckTlsSkipVerify + + '}'; + } + + public static class Lifecycle { + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "Lifecycle{" + + "enabled=" + enabled + + '}'; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesAutoConfiguration.java new file mode 100644 index 00000000..3502a9be --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesAutoConfiguration.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf; + +import com.tencent.cloud.common.util.inet.PolarisInetUtils; +import com.tencent.cloud.plugin.lossless.config.LosslessProperties; +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; +import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties; +import com.tencent.cloud.polaris.context.tsf.consul.TsfConsulProperties; +import com.tencent.cloud.polaris.tsf.lossless.TsfLosslessConfigModifier; +import com.tencent.cloud.polaris.tsf.lossless.TsfLosslessProperties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for TSF discovery. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +public class TsfDiscoveryPropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public TsfDiscoveryProperties tsfDiscoveryProperties(PolarisInetUtils polarisInetUtils) { + return new TsfDiscoveryProperties(polarisInetUtils); + } + + @Bean + @ConditionalOnMissingBean + public TsfHeartbeatProperties tsfHeartbeatProperties() { + return new TsfHeartbeatProperties(); + } + + @Bean + @ConditionalOnMissingBean + public TsfLosslessProperties tsfLosslessProperties() { + return new TsfLosslessProperties(); + } + + @Bean + @ConditionalOnMissingBean + public TsfDiscoveryConfigModifier tsfDiscoveryConfigModifier(TsfCoreProperties tsfCoreProperties, + TsfConsulProperties tsfConsulProperties, TsfDiscoveryProperties tsfDiscoveryProperties, + TsfHeartbeatProperties tsfHeartbeatProperties, PolarisDiscoveryProperties polarisDiscoveryProperties, + PolarisContextProperties polarisContextProperties, ApplicationContext context) { + return new TsfDiscoveryConfigModifier(tsfCoreProperties, tsfConsulProperties, tsfDiscoveryProperties, + tsfHeartbeatProperties, polarisDiscoveryProperties, polarisContextProperties, context); + } + + @Bean + @ConditionalOnMissingBean + public TsfZeroProtectionConfigModifier tsfZeroProtectionConfigModifier() { + return new TsfZeroProtectionConfigModifier(); + } + + @Bean + @ConditionalOnMissingBean + public TsfLosslessConfigModifier tsfLosslessConfigModifier(LosslessProperties losslessProperties, TsfLosslessProperties tsfLosslessProperties) { + return new TsfLosslessConfigModifier(losslessProperties, tsfLosslessProperties); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesBootstrapConfiguration.java new file mode 100644 index 00000000..11398097 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfDiscoveryPropertiesBootstrapConfiguration.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf; + +import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +import org.springframework.cloud.commons.util.UtilAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Bootstrap configuration for TSF discovery. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +@Import({TsfDiscoveryPropertiesAutoConfiguration.class, + DiscoveryPropertiesAutoConfiguration.class, + UtilAutoConfiguration.class}) +public class TsfDiscoveryPropertiesBootstrapConfiguration { + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfHeartbeatProperties.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfHeartbeatProperties.java new file mode 100644 index 00000000..895e9076 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfHeartbeatProperties.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf; + +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import org.apache.commons.logging.Log; +import org.joda.time.Period; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.style.ToStringCreator; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties(prefix = "tsf.discovery.heartbeat") +@Validated +public class TsfHeartbeatProperties { + + private static final Log log = org.apache.commons.logging.LogFactory.getLog(TsfHeartbeatProperties.class); + // TODO: change enabled to default to true when I stop seeing messages like + // [WARN] agent: Check 'service:testConsulApp:xtest:8080' missed TTL, is now critical + boolean enabled = true; + + @Min(1) + private int ttlValue = 30; + + @NotNull + private String ttlUnit = "s"; + + @DecimalMin("0.1") + @DecimalMax("0.9") + private double intervalRatio = 2.0 / 3.0; + + //TODO: did heartbeatInterval need to be a field? + + protected Period computeHearbeatInterval() { + // heartbeat rate at ratio * ttl, but no later than ttl -1s and, (under lesser + // priority), no sooner than 1s from now + double interval = ttlValue * intervalRatio; + double max = Math.max(interval, 1); + int ttlMinus1 = ttlValue - 1; + double min = Math.min(ttlMinus1, max); + Period heartbeatInterval = new Period(Math.round(1000 * min)); + log.debug("Computed heartbeatInterval: " + heartbeatInterval); + return heartbeatInterval; + } + + public String getTtl() { + return ttlValue + ttlUnit; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public @Min(1) int getTtlValue() { + return this.ttlValue; + } + + public void setTtlValue(@Min(1) int ttlValue) { + this.ttlValue = ttlValue; + } + + public @NotNull String getTtlUnit() { + return this.ttlUnit; + } + + public void setTtlUnit(@NotNull String ttlUnit) { + this.ttlUnit = ttlUnit; + } + + public @DecimalMin("0.1") @DecimalMax("0.9") double getIntervalRatio() { + return this.intervalRatio; + } + + public void setIntervalRatio(@DecimalMin("0.1") @DecimalMax("0.9") double intervalRatio) { + this.intervalRatio = intervalRatio; + } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("enabled", enabled) + .append("ttlValue", ttlValue) + .append("ttlUnit", ttlUnit) + .append("intervalRatio", intervalRatio) + .toString(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfZeroProtectionConfigModifier.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfZeroProtectionConfigModifier.java new file mode 100644 index 00000000..0a78d1a7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/TsfZeroProtectionConfigModifier.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.polaris.tsf; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.factory.config.ConfigurationImpl; + +/** + * Modifier for TSF discovery zero protection. + * + * @author Haotian Zhang + */ +public class TsfZeroProtectionConfigModifier implements PolarisConfigModifier { + @Override + public void modify(ConfigurationImpl configuration) { + configuration.getConsumer().getZeroProtection().setEnable(true); + configuration.getConsumer().getZeroProtection().setNeedTestConnectivity(true); + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.DISCOVERY_ORDER + 1; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/consts/WarmupCons.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/consts/WarmupCons.java new file mode 100644 index 00000000..53bce11c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/consts/WarmupCons.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.consts; + +/** + * 预热所需枚举. + * @author jiangfan + */ +public final class WarmupCons { + + /** + * 预热保护阈值. + */ + public static double DEFAULT_PROTECTION_THRESHOLD_KEY = 50; + /** + * TSF 启动时间。预热开始时间. + */ + public static String TSF_START_TIME = "TSF_START_TIME"; + + private WarmupCons() { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessConfigModifier.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessConfigModifier.java new file mode 100644 index 00000000..40a6fda0 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessConfigModifier.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.lossless; + +import com.tencent.cloud.common.constant.OrderConstant; +import com.tencent.cloud.plugin.lossless.config.LosslessProperties; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.factory.config.ConfigurationImpl; + +/** + * Modifier for TSF lossless online offline. + * + * @author Haotian Zhang + */ +public class TsfLosslessConfigModifier implements PolarisConfigModifier { + + private final LosslessProperties losslessProperties; + private final TsfLosslessProperties tsfLosslessProperties; + + public TsfLosslessConfigModifier(LosslessProperties losslessProperties, TsfLosslessProperties tsfLosslessProperties) { + this.losslessProperties = losslessProperties; + this.tsfLosslessProperties = tsfLosslessProperties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + losslessProperties.setEnabled(true); + losslessProperties.setPort(tsfLosslessProperties.getPort()); + } + + @Override + public int getOrder() { + return OrderConstant.Modifier.LOSSLESS_ORDER - 1; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessProperties.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessProperties.java new file mode 100644 index 00000000..0be0d597 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/lossless/TsfLosslessProperties.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.lossless; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 优雅上下线的配置. + * + * @author Haotian Zhang + */ +@ConfigurationProperties("tsf.discovery.lossless") +public class TsfLosslessProperties { + + @Value("${tsf.discovery.lossless.port:${tsf_sctt_extensions_port:11134}}") + private int port = 11134; + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + @Override + public String toString() { + return "TsfLosslessProperties{" + + "port=" + port + + '}'; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfApiPolarisRegistrationCustomizer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfApiPolarisRegistrationCustomizer.java new file mode 100644 index 00000000..c193f06c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfApiPolarisRegistrationCustomizer.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.polaris.tsf.registry; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.cloud.polaris.registry.PolarisRegistration; +import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationContext; +import org.springframework.util.StringUtils; + +import static com.tencent.polaris.api.config.plugin.DefaultPlugins.SERVER_CONNECTOR_CONSUL; + +/** + * Set API data to registration metadata. + * + * @author Haotian Zhang + */ +public class TsfApiPolarisRegistrationCustomizer implements PolarisRegistrationCustomizer { + private static final Logger LOG = LoggerFactory.getLogger(TsfApiPolarisRegistrationCustomizer.class); + + private static final String API_META_KEY = "TSF_API_METAS"; + private final ApplicationContext context; + + public TsfApiPolarisRegistrationCustomizer(ApplicationContext context) { + this.context = context; + } + + @Override + public void customize(PolarisRegistration registration) { + String apiMetaData = context.getEnvironment().getProperty("$api_metas"); + Map> metadata = registration.getExtendedMetadata(); + if (StringUtils.hasText(apiMetaData)) { + if (!metadata.containsKey(SERVER_CONNECTOR_CONSUL)) { + metadata.put(SERVER_CONNECTOR_CONSUL, new HashMap<>()); + } + metadata.get(SERVER_CONNECTOR_CONSUL).put(API_META_KEY, apiMetaData); + } + else { + LOG.warn("apiMetaData is null, service:{}", registration.getServiceId()); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfDiscoveryRegistryAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfDiscoveryRegistryAutoConfiguration.java new file mode 100644 index 00000000..461b1e05 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfDiscoveryRegistryAutoConfiguration.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.polaris.tsf.registry; + +import com.tencent.cloud.polaris.context.PolarisSDKContextManager; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; +import com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration; +import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties; +import com.tencent.cloud.polaris.tsf.TsfHeartbeatProperties; +import jakarta.servlet.ServletContext; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Auto configuration for TSF discovery. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnTsfEnabled +@AutoConfigureBefore(PolarisServiceRegistryAutoConfiguration.class) +public class TsfDiscoveryRegistryAutoConfiguration { + @Bean + @ConditionalOnMissingBean + public TsfMetadataPolarisRegistrationCustomizer tsfMetadataPolarisRegistrationCustomizer(TsfDiscoveryProperties tsfDiscoveryProperties) { + return new TsfMetadataPolarisRegistrationCustomizer(tsfDiscoveryProperties); + } + + @Bean + @ConditionalOnMissingBean + public TsfPortPolarisRegistrationCustomizer tsfPortPolarisRegistrationCustomizer( + AutoServiceRegistrationProperties autoServiceRegistrationProperties, + ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties, + TsfHeartbeatProperties tsfHeartbeatProperties, PolarisSDKContextManager polarisSDKContextManager) { + return new TsfPortPolarisRegistrationCustomizer(autoServiceRegistrationProperties, context, + tsfDiscoveryProperties, tsfHeartbeatProperties, polarisSDKContextManager.getSDKContext()); + } + + @Bean + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) + @ConditionalOnMissingBean + public TsfServletRegistrationCustomizer tsfServletConsulCustomizer(ObjectProvider servletContext) { + return new TsfServletRegistrationCustomizer(servletContext); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty(value = "tsf.swagger.enabled", havingValue = "true", matchIfMissing = true) + public TsfApiPolarisRegistrationCustomizer tsfApiPolarisRegistrationCustomizer(ApplicationContext context) { + return new TsfApiPolarisRegistrationCustomizer(context); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfMetadataPolarisRegistrationCustomizer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfMetadataPolarisRegistrationCustomizer.java new file mode 100644 index 00000000..9b698c59 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfMetadataPolarisRegistrationCustomizer.java @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.registry; + +import java.util.Map; + +import com.tencent.cloud.common.constant.SdkVersion; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.registry.PolarisRegistration; +import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer; +import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties; +import com.tencent.cloud.polaris.tsf.consts.WarmupCons; +import com.tencent.cloud.polaris.tsf.util.RegistrationUtil; + +/** + * + * + * @author Haotian Zhang + */ +public class TsfMetadataPolarisRegistrationCustomizer implements PolarisRegistrationCustomizer { + + private final TsfDiscoveryProperties tsfDiscoveryProperties; + + public TsfMetadataPolarisRegistrationCustomizer(TsfDiscoveryProperties tsfDiscoveryProperties) { + this.tsfDiscoveryProperties = tsfDiscoveryProperties; + } + + @Override + public void customize(PolarisRegistration registration) { + Map metadata = registration.getMetadata(); + + metadata.put("TSF_APPLICATION_ID", tsfDiscoveryProperties.getTsfApplicationId()); + metadata.put("TSF_PROG_VERSION", tsfDiscoveryProperties.getTsfProgVersion()); + metadata.put("TSF_GROUP_ID", tsfDiscoveryProperties.getTsfGroupId()); + metadata.put("TSF_NAMESPACE_ID", tsfDiscoveryProperties.getTsfNamespaceId()); + metadata.put("TSF_INSTNACE_ID", tsfDiscoveryProperties.getInstanceId()); + metadata.put("TSF_REGION", tsfDiscoveryProperties.getTsfRegion()); + metadata.put("TSF_ZONE", tsfDiscoveryProperties.getTsfZone()); + // 处理预热相关的参数 + metadata.put(WarmupCons.TSF_START_TIME, String.valueOf(System.currentTimeMillis())); + metadata.put("TSF_SDK_VERSION", SdkVersion.get()); + metadata.put("TSF_TAGS", JacksonUtils.serialize2Json(RegistrationUtil.createTags(tsfDiscoveryProperties))); + RegistrationUtil.appendMetaIpAddress(metadata); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfPortPolarisRegistrationCustomizer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfPortPolarisRegistrationCustomizer.java new file mode 100644 index 00000000..65372d5a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfPortPolarisRegistrationCustomizer.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.polaris.tsf.registry; + +import com.tencent.cloud.polaris.registry.PolarisRegistration; +import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer; +import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties; +import com.tencent.cloud.polaris.tsf.TsfHeartbeatProperties; +import com.tencent.cloud.polaris.tsf.util.RegistrationUtil; +import com.tencent.polaris.client.api.SDKContext; + +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.context.ApplicationContext; + +/** + * 服务注册时端口相关逻辑. + * + * @author Haotian Zhang + */ +public class TsfPortPolarisRegistrationCustomizer implements PolarisRegistrationCustomizer { + + private final AutoServiceRegistrationProperties autoServiceRegistrationProperties; + private final ApplicationContext context; + private final TsfDiscoveryProperties tsfDiscoveryProperties; + private final TsfHeartbeatProperties tsfHeartbeatProperties; + private final SDKContext sdkContext; + + public TsfPortPolarisRegistrationCustomizer(AutoServiceRegistrationProperties autoServiceRegistrationProperties, + ApplicationContext context, TsfDiscoveryProperties tsfDiscoveryProperties, + TsfHeartbeatProperties tsfHeartbeatProperties, SDKContext sdkContext) { + this.autoServiceRegistrationProperties = autoServiceRegistrationProperties; + this.context = context; + this.tsfDiscoveryProperties = tsfDiscoveryProperties; + this.tsfHeartbeatProperties = tsfHeartbeatProperties; + this.sdkContext = sdkContext; + } + + @Override + public void customize(PolarisRegistration registration) { + if (tsfDiscoveryProperties.getPort() != null) { + registration.setPort(tsfDiscoveryProperties.getPort()); + } + // we know the port and can set the check + RegistrationUtil.setCheck(autoServiceRegistrationProperties, tsfDiscoveryProperties, context, + tsfHeartbeatProperties, registration, sdkContext.getConfig()); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfServletRegistrationCustomizer.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfServletRegistrationCustomizer.java new file mode 100644 index 00000000..75b05a44 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/registry/TsfServletRegistrationCustomizer.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.registry; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.registry.PolarisRegistration; +import com.tencent.cloud.polaris.registry.PolarisRegistrationCustomizer; +import com.tencent.polaris.plugins.connector.common.constant.ConsulConstant; +import jakarta.servlet.ServletContext; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.util.StringUtils; + +import static com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey.TAGS_KEY; + +/** + * @author Piotr Wielgolaski + */ +public class TsfServletRegistrationCustomizer implements PolarisRegistrationCustomizer { + private final ObjectProvider servletContext; + + public TsfServletRegistrationCustomizer(ObjectProvider servletContext) { + this.servletContext = servletContext; + } + + @Override + public void customize(PolarisRegistration registration) { + if (servletContext == null) { + return; + } + ServletContext sc = servletContext.getIfAvailable(); + if (sc != null + && StringUtils.hasText(sc.getContextPath()) + && StringUtils.hasText(sc.getContextPath().replaceAll("/", ""))) { + Map metadata = registration.getMetadata(); + + List tags = Arrays.asList(JacksonUtils.deserialize(metadata.get(TAGS_KEY), String[].class)); + if (tags == null) { + tags = new ArrayList<>(); + } + tags.add("contextPath=" + sc.getContextPath()); + metadata.put(ConsulConstant.MetadataMapKey.TAGS_KEY, JacksonUtils.serialize2Json(tags)); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/util/RegistrationUtil.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/util/RegistrationUtil.java new file mode 100644 index 00000000..bb95c8d8 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/tsf/util/RegistrationUtil.java @@ -0,0 +1,231 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.tsf.util; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.ecwid.consul.v1.agent.model.NewService; +import com.tencent.cloud.common.util.AddressUtils; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.common.util.inet.PolarisInetUtils; +import com.tencent.cloud.polaris.tsf.TsfDiscoveryProperties; +import com.tencent.cloud.polaris.tsf.TsfHeartbeatProperties; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.factory.config.global.ServerConnectorConfigImpl; +import com.tencent.polaris.plugins.connector.common.constant.ConsulConstant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.discovery.ManagementServerPortUtils; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.cloud.commons.util.IdUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * TSF registration utils. + * + * @author Haotian Zhang + */ +public final class RegistrationUtil { + /** + * - 分隔符. + */ + public static final char SEPARATOR = '-'; + /** + * Server connector ID. + */ + public static final String ID = "consul"; + private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationUtil.class); + /** + * IPV4. + */ + public static String TSF_ADDRESS_IPV4 = "TSF_ADDRESS_IPV4"; + + /** + * IPV6. + */ + public static String TSF_ADDRESS_IPV6 = "TSF_ADDRESS_IPV6"; + + private RegistrationUtil() { + } + + public static String getAppName(TsfDiscoveryProperties properties, Environment env) { + String appName = properties.getServiceName(); + if (!StringUtils.isEmpty(appName)) { + return appName; + } + return env.getProperty("spring.application.name", "application"); + } + + public static String getInstanceId(TsfDiscoveryProperties properties, ApplicationContext context) { + // tsf consul 不支持 dns,所以这里不需要 normalize,并且由于优雅下线,readiness probe 联动都是依赖 service id 的,normalize 后两边对不上,所以需要去掉 normalize + if (!StringUtils.hasText(properties.getInstanceId())) { + return IdUtils.getDefaultInstanceId(context.getEnvironment(), false); + } + else { + return properties.getInstanceId(); + } + } + + // 更新了判断逻辑,仅判断为空的场景 + public static String normalizeForDns(String s) { + // if (s == null || !Character.isLetter(s.charAt(0)) + // || !Character.isLetterOrDigit(s.charAt(s.length()-1))) { + // throw new IllegalArgumentException("Consul service ids must not be empty, + // must start with a letter, end with a letter or digit, and have as interior + // characters only letters, digits, and hyphen"); + // } + + // tsf not check consul service instance id start with letter and end with + // letter or digit + if (s == null) { + throw new IllegalArgumentException("Consul service ids must not be empty"); + } + + StringBuilder normalized = new StringBuilder(); + Character prev = null; + for (char curr : s.toCharArray()) { + Character toAppend = null; + if (Character.isLetterOrDigit(curr)) { + toAppend = curr; + } + else if (prev == null || !(prev == SEPARATOR)) { + toAppend = SEPARATOR; + } + if (toAppend != null) { + normalized.append(toAppend); + prev = toAppend; + } + } + + return normalized.toString(); + } + + public static List createTags(TsfDiscoveryProperties properties) { + List tags = new LinkedList<>(properties.getTags()); + + if (StringUtils.hasText(properties.getInstanceZone())) { + tags.add(properties.getDefaultZoneMetadataName() + "=" + properties.getInstanceZone()); + } + if (StringUtils.hasText(properties.getInstanceGroup())) { + tags.add("group=" + properties.getInstanceGroup()); + } + + //store the secure flag in the tags so that clients will be able to figure out whether to use http or https automatically + tags.add("secure=" + properties.getScheme().equalsIgnoreCase("https")); + + return tags; + } + + public static Map appendMetaIpAddress(Map meta) { + if (meta == null) { + return null; + } + String ipv4Address = PolarisInetUtils.getIpString(false); + if (ipv4Address != null) { + meta.put(TSF_ADDRESS_IPV4, ipv4Address); + } + + String ipv6Address = PolarisInetUtils.getIpString(true); + if (ipv6Address != null) { + meta.put(TSF_ADDRESS_IPV6, ipv6Address); + } + return meta; + } + + public static void setCheck(AutoServiceRegistrationProperties autoServiceRegistrationProperties, + TsfDiscoveryProperties properties, ApplicationContext context, + TsfHeartbeatProperties tsfHeartbeatProperties, Registration registration, Configuration configuration) { + if (properties.isRegisterHealthCheck()) { + Integer checkPort; + if (shouldRegisterManagement(autoServiceRegistrationProperties, properties, context)) { + checkPort = getManagementPort(properties, context); + } + else { + checkPort = registration.getPort(); + } + Assert.notNull(checkPort, "checkPort may not be null"); + + for (ServerConnectorConfigImpl config : configuration.getGlobal().getServerConnectors()) { + if (org.apache.commons.lang.StringUtils.equals(config.getId(), ID)) { + Map metadata = config.getMetadata(); + NewService.Check check = createCheck(checkPort, tsfHeartbeatProperties, properties); + String checkJson = JacksonUtils.serialize2Json(check); + LOGGER.debug("Check is : {}", checkJson); + metadata.put(ConsulConstant.MetadataMapKey.CHECK_KEY, checkJson); + } + } + + } + } + + public static NewService.Check createCheck(Integer port, TsfHeartbeatProperties ttlConfig, + TsfDiscoveryProperties properties) { + NewService.Check check = new NewService.Check(); + if (ttlConfig.isEnabled()) { + check.setTtl(ttlConfig.getTtl()); + return check; + } + + Assert.notNull(port, "createCheck port must not be null"); + Assert.isTrue(port > 0, "createCheck port must be greater than 0"); + + if (properties.getHealthCheckUrl() != null) { + check.setHttp(properties.getHealthCheckUrl()); + } + else { + check.setHttp(String.format("%s://%s:%s%s", properties.getScheme(), + AddressUtils.getIpCompatible(properties.getHostname()), port, + properties.getHealthCheckPath())); + } + check.setInterval(properties.getHealthCheckInterval()); + check.setTimeout(properties.getHealthCheckTimeout()); + if (StringUtils.hasText(properties.getHealthCheckCriticalTimeout())) { + check.setDeregisterCriticalServiceAfter(properties.getHealthCheckCriticalTimeout()); + } + check.setTlsSkipVerify(properties.getHealthCheckTlsSkipVerify()); + return check; + } + + /** + * @return if the management service should be registered with the {@link ServiceRegistry} + */ + public static boolean shouldRegisterManagement(AutoServiceRegistrationProperties autoServiceRegistrationProperties, TsfDiscoveryProperties properties, ApplicationContext context) { + return autoServiceRegistrationProperties.isRegisterManagement() + && getManagementPort(properties, context) != null + && ManagementServerPortUtils.isDifferent(context); + } + + /** + * @return the port of the Management Service + */ + public static Integer getManagementPort(TsfDiscoveryProperties properties, ApplicationContext context) { + // If an alternate external port is specified, use it instead + if (properties.getManagementPort() != null) { + return properties.getManagementPort(); + } + return ManagementServerPortUtils.getPort(context); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories index c084e934..3217f4bf 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.DiscoveryPropertiesBootstrapAutoConfiguration + com.tencent.cloud.polaris.DiscoveryPropertiesBootstrapAutoConfiguration,\ + com.tencent.cloud.polaris.tsf.TsfDiscoveryPropertiesBootstrapConfiguration diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 41397573..d02f8536 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -3,3 +3,5 @@ com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration com.tencent.cloud.polaris.endpoint.PolarisDiscoveryEndpointAutoConfiguration com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancerAutoConfiguration +com.tencent.cloud.polaris.tsf.TsfDiscoveryPropertiesAutoConfiguration +com.tencent.cloud.polaris.tsf.registry.TsfDiscoveryRegistryAutoConfiguration 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-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java similarity index 98% rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java index 45f65652..67d0e98a 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/LosslessRegistryAspectTest.java @@ -73,6 +73,7 @@ public class LosslessRegistryAspectTest { PolarisDiscoveryClientConfiguration.class, PolarisDiscoveryAutoConfiguration.class)) .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.lossless.enabled=true") .withPropertyValues("spring.cloud.polaris.lossless.delayRegisterInterval=5000") .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=") .withPropertyValues("spring.cloud.polaris.lossless.port=" + LOSSLESS_PORT_1) @@ -93,6 +94,7 @@ public class LosslessRegistryAspectTest { PolarisDiscoveryClientConfiguration.class, PolarisDiscoveryAutoConfiguration.class)) .withPropertyValues("spring.cloud.nacos.discovery.enabled=false") + .withPropertyValues("spring.cloud.polaris.lossless.enabled=true") .withPropertyValues("spring.cloud.polaris.lossless.healthCheckInterval=1000") .withPropertyValues("spring.cloud.polaris.lossless.healthCheckPath=/test") .withPropertyValues("spring.cloud.polaris.lossless.port=28082") 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-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifierTest.java similarity index 96% rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifierTest.java index 0b0d847f..9152f04f 100644 --- a/spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/com/tencent/cloud/plugin/lossless/LosslessConfigModifierTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/plugin/lossless/config/LosslessConfigModifierTest.java @@ -15,9 +15,8 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.plugin.lossless; +package com.tencent.cloud.plugin.lossless.config; -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; 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-starter-tencent-polaris-discovery/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java similarity index 100% rename from spring-cloud-tencent-plugin-starters/spring-cloud-tencent-lossless-plugin/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/org/springframework/cloud/client/serviceregistry/AutoServiceRegistrationUtils.java diff --git a/spring-cloud-tencent-commons/pom.xml b/spring-cloud-tencent-commons/pom.xml index 8632ccdd..a667b983 100644 --- a/spring-cloud-tencent-commons/pom.xml +++ b/spring-cloud-tencent-commons/pom.xml @@ -50,6 +50,11 @@ spring-boot-starter-json + + org.springframework.boot + spring-boot-starter-validation + + org.springframework.cloud spring-cloud-starter diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java index 277a842d..3415cb79 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java @@ -19,18 +19,21 @@ package com.tencent.cloud.common.util; import java.io.IOException; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.NetworkInterface; import java.net.Socket; import java.net.URI; import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.List; +import com.tencent.polaris.api.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; /** * the utils of parse address. @@ -43,11 +46,17 @@ public final class AddressUtils { private static final String ADDRESS_SEPARATOR = ","; + private final static Boolean hasIpv6Address; + + static { + hasIpv6Address = hasIpv6Address(); + } + private AddressUtils() { } public static List parseAddressList(String addressInfo) { - if (!StringUtils.hasText(addressInfo)) { + if (StringUtils.isBlank(addressInfo)) { return Collections.emptyList(); } List addressList = new ArrayList<>(); @@ -77,4 +86,73 @@ public final class AddressUtils { } return true; } + + public static String getIpCompatible(String ip) { + if (StringUtils.isEmpty(ip)) { + return ip; + } + if (ip.contains(":") && !ip.startsWith("[") && !ip.endsWith("]")) { + return "[" + ip + "]"; + } + return ip; + } + + public static boolean preferIpv6() { + if (Boolean.FALSE.equals(hasIpv6Address)) { + LOGGER.debug("AddressUtils.preferIpv6 hasIpv6Address = false"); + return false; + } + if ("true".equalsIgnoreCase(System.getenv("tsf_prefer_ipv6"))) { + LOGGER.debug("AddressUtils.preferIpv6 System.getenv = true"); + return true; + } + if ("true".equalsIgnoreCase(System.getProperty("tsf_prefer_ipv6"))) { + LOGGER.debug("AddressUtils.preferIpv6 System.getProperty = true"); + return true; + } + if ("true".equalsIgnoreCase(BeanFactoryUtils.resolve("${tsf_prefer_ipv6}"))) { + LOGGER.debug("AddressUtils.preferIpv6 BeanFactoryUtils.resolve = true"); + return true; + } + LOGGER.debug("AddressUtils.preferIpv6 result = false"); + return false; + } + + /** + * Determine whether environment has an ipv6 address. + */ + private static boolean hasIpv6Address() { + InetAddress result = null; + try { + int lowest = Integer.MAX_VALUE; + for (Enumeration nics = NetworkInterface + .getNetworkInterfaces(); nics.hasMoreElements(); ) { + NetworkInterface ifc = nics.nextElement(); + if (ifc.isUp()) { + LOGGER.trace("Testing interface: " + ifc.getDisplayName()); + if (ifc.getIndex() < lowest || result == null) { + lowest = ifc.getIndex(); + } + else if (result != null) { + continue; + } + + for (Enumeration addrs = ifc + .getInetAddresses(); addrs.hasMoreElements(); ) { + InetAddress address = addrs.nextElement(); + if (address instanceof Inet6Address + && !address.isLinkLocalAddress() + && !address.isLoopbackAddress()) { + LOGGER.trace("Found non-loopback interface: " + ifc.getDisplayName()); + return true; + } + } + } + } + } + catch (IOException ex) { + LOGGER.error("Cannot get first non-loopback address", ex); + } + return false; + } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java index e7729cf1..cd982c68 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java @@ -13,7 +13,6 @@ * 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; @@ -22,8 +21,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.stereotype.Component; import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors; @@ -31,9 +37,30 @@ import static org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncl * the utils for bean factory. * @author lepdou 2022-05-23 */ -public final class BeanFactoryUtils { +@Component +public final class BeanFactoryUtils implements BeanFactoryAware { + + private static final Logger LOGGER = LoggerFactory.getLogger(BeanFactoryUtils.class); - private BeanFactoryUtils() { + private static BeanFactory beanFactory; + + /** + * Dynamic parsing of spring @Value. + * + * @param value something like ${} + * @return return null if the parsing fails or the object is not found. + */ + public static String resolve(String value) { + try { + if (beanFactory instanceof ConfigurableBeanFactory) { + return ((ConfigurableBeanFactory) beanFactory).resolveEmbeddedValue(value); + } + } + catch (Exception e) { + LOGGER.error("resolve {} failed.", value, e); + } + + return null; } public static List getBeans(BeanFactory beanFactory, Class requiredType) { @@ -45,4 +72,9 @@ public final class BeanFactoryUtils { Map beanMap = beansOfTypeIncludingAncestors((ListableBeanFactory) beanFactory, requiredType); return new ArrayList<>(beanMap.values()); } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + BeanFactoryUtils.beanFactory = beanFactory; + } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtils.java new file mode 100644 index 00000000..e0afb55b --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtils.java @@ -0,0 +1,320 @@ +/* + * 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.inet; + +import java.io.Closeable; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import com.tencent.cloud.common.util.AddressUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.cloud.commons.util.InetUtilsProperties; + + +/** + * Extend from {@link InetUtils}. + * + * @author Haotian Zhang + */ +public class PolarisInetUtils implements Closeable { + + private static final Logger logger = LoggerFactory.getLogger(PolarisInetUtils.class); + // TODO: maybe shutdown the thread pool if it isn't being used? + private final ExecutorService executorService; + private final InetUtilsProperties properties; + + public PolarisInetUtils(final InetUtilsProperties properties) { + this.properties = properties; + this.executorService = Executors + .newSingleThreadExecutor(r -> { + Thread thread = new Thread(r); + thread.setName(InetUtilsProperties.PREFIX); + thread.setDaemon(true); + return thread; + }); + } + + public static String getIpString(boolean _ipv6) { + InetAddress result = null; + try { + int lowest = Integer.MAX_VALUE; + for (Enumeration nics = NetworkInterface + .getNetworkInterfaces(); nics.hasMoreElements(); ) { + NetworkInterface ifc = nics.nextElement(); + if (ifc.isUp()) { + logger.trace("Testing interface: " + ifc.getDisplayName()); + if (ifc.getIndex() < lowest || result == null) { + lowest = ifc.getIndex(); + } + else if (result != null) { + continue; + } + for (Enumeration addrs = ifc + .getInetAddresses(); addrs.hasMoreElements(); ) { + InetAddress address = addrs.nextElement(); + if (_ipv6) { + if (address instanceof Inet6Address + && !address.isLinkLocalAddress() + && !address.isLoopbackAddress() + ) { + logger.trace("Found non-loopback interface: " + + ifc.getDisplayName()); + result = address; + } + } + else { + if (address instanceof Inet4Address + && !address.isLoopbackAddress() + ) { + logger.trace("Found non-loopback interface: " + + ifc.getDisplayName()); + result = address; + } + } + } + } + } + } + catch (IOException ex) { + logger.error("Cannot get first non-loopback address", ex); + } + + if (result == null) { + return null; + } + + if (result.getHostAddress().contains("%")) { + return result.getHostAddress().split("%")[0]; + } + else { + return result.getHostAddress(); + } + } + + @Override + public void close() { + executorService.shutdown(); + } + + public InetUtils.HostInfo findFirstNonLoopbackHostInfo() { + InetAddress address = findFirstNonLoopbackAddress(); + if (address != null) { + return convertAddress(address); + } + InetUtils.HostInfo hostInfo = new InetUtils.HostInfo(); + hostInfo.setHostname(this.properties.getDefaultHostname()); + hostInfo.setIpAddress(this.properties.getDefaultIpAddress()); + return hostInfo; + } + + public InetAddress findFirstNonLoopbackAddress() { + boolean preferIpv6 = AddressUtils.preferIpv6(); + InetAddress result = findFirstNonLoopbackAddressByIpType(preferIpv6); + logger.debug("ipv6 before, preferIpv6:{}, result:{}", preferIpv6, result); + if (result == null) { + result = findFirstNonLoopbackAddressByIpType(!preferIpv6); + } + logger.debug("ipv6 after, preferIpv6:{}, result:{}", preferIpv6, result); + + if (result != null) { + return result; + } + + try { + return InetAddress.getLocalHost(); + } + catch (UnknownHostException e) { + logger.warn("Unable to retrieve localhost"); + } + + return null; + } + + /** for testing. */ + boolean isPreferredAddress(InetAddress address) { + + if (this.properties.isUseOnlySiteLocalInterfaces()) { + final boolean siteLocalAddress = address.isSiteLocalAddress(); + if (!siteLocalAddress) { + logger.trace("Ignoring address: " + address.getHostAddress()); + } + return siteLocalAddress; + } + final List preferredNetworks = this.properties.getPreferredNetworks(); + if (preferredNetworks.isEmpty()) { + return true; + } + for (String regex : preferredNetworks) { + final String hostAddress = address.getHostAddress(); + if (hostAddress.matches(regex) || hostAddress.startsWith(regex)) { + return true; + } + } + logger.trace("Ignoring address: " + address.getHostAddress()); + return false; + } + + /** for testing. */ + boolean ignoreInterface(String interfaceName) { + for (String regex : this.properties.getIgnoredInterfaces()) { + if (interfaceName.matches(regex)) { + logger.trace("Ignoring interface: " + interfaceName); + return true; + } + } + return false; + } + + public InetUtils.HostInfo convertAddress(final InetAddress address) { + InetUtils.HostInfo hostInfo = new InetUtils.HostInfo(); + Future result = executorService.submit(new Callable() { + @Override + public String call() throws Exception { + return address.getHostName(); + } + }); + + String hostname; + try { + hostname = result.get(this.properties.getTimeoutSeconds(), TimeUnit.SECONDS); + } + catch (Exception e) { + logger.info("Cannot determine local hostname"); + hostname = "localhost"; + } + if (hostname.contains("%")) { + hostInfo.setHostname(hostname.split("%")[0]); + } + else { + hostInfo.setHostname(hostname); + } + if (address.getHostAddress().contains("%")) { + hostInfo.setIpAddress(address.getHostAddress().split("%")[0]); + } + else { + hostInfo.setIpAddress(address.getHostAddress()); + } + return hostInfo; + } + + public String findIpInterface() { + InetAddress address = findFirstNonLoopbackAddress(); + if (address.getHostAddress().contains("%")) { + return address.getHostAddress().split("%")[1]; + } + return ""; + } + + public String findIpAddress() { + InetAddress address = findFirstNonLoopbackAddress(); + return address.getHostAddress().split("%")[0]; + } + + /** + * + * @return "[ipv6]" + */ + public String findIpAddressWithBracket() { + InetAddress address = findFirstNonLoopbackAddress(); + if (address.getHostAddress().contains("%")) { + return address.getHostAddress().split("%")[0]; + } + return address.getHostAddress(); + } + + /** + * @return ipv6%eth0 + * + */ + public String findIpAddressAndInterface() { + InetAddress address = findFirstNonLoopbackAddress(); + return address.getHostAddress(); + } + + public InetAddress findFirstNonLoopbackAddressByIpType(boolean _ipv6) { + InetAddress result = null; + try { + int lowest = Integer.MAX_VALUE; + for (Enumeration nics = NetworkInterface + .getNetworkInterfaces(); nics.hasMoreElements(); ) { + NetworkInterface ifc = nics.nextElement(); + if (ifc.isUp()) { + logger.trace("Testing interface: " + ifc.getDisplayName()); + if (ifc.getIndex() < lowest || result == null) { + lowest = ifc.getIndex(); + } + else if (result != null) { + continue; + } + + // @formatter:off + if (!ignoreInterface(ifc.getDisplayName())) { + for (Enumeration addrs = ifc + .getInetAddresses(); addrs.hasMoreElements();) { + InetAddress address = addrs.nextElement(); + if (_ipv6) { + if (address instanceof Inet6Address + && !address.isLinkLocalAddress() + && !address.isLoopbackAddress() + && isPreferredAddress(address)) { + logger.trace("Found non-loopback interface: " + + ifc.getDisplayName()); + result = address; + } + } + else { + if (address instanceof Inet4Address + && !address.isLoopbackAddress() + && isPreferredAddress(address)) { + logger.trace("Found non-loopback interface: " + + ifc.getDisplayName()); + result = address; + } + } + } + } + // @formatter:on + } + } + } + catch (IOException ex) { + logger.error("Cannot get first non-loopback address", ex); + } + + if (result != null) { + return result; + } + + + return null; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsAutoConfiguration.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsAutoConfiguration.java new file mode 100644 index 00000000..39d48581 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsAutoConfiguration.java @@ -0,0 +1,39 @@ +/* + * 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.inet; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.commons.util.InetUtilsProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * Auto configuration for PolarisInetUtils. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +public class PolarisInetUtilsAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public PolarisInetUtils polarisInetUtils(InetUtilsProperties properties) { + return new PolarisInetUtils(properties); + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsBootstrapConfiguration.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsBootstrapConfiguration.java new file mode 100644 index 00000000..9973573d --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/inet/PolarisInetUtilsBootstrapConfiguration.java @@ -0,0 +1,30 @@ +/* + * 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.inet; + +import org.springframework.context.annotation.Import; + +/** + * Auto configuration for PolarisInetUtils. + * + * @author Haotian Zhang + */ +@Import(PolarisInetUtilsAutoConfiguration.class) +public class PolarisInetUtilsBootstrapConfiguration { + +} diff --git a/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..00be2f25 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ + com.tencent.cloud.common.util.inet.PolarisInetUtilsBootstrapConfiguration diff --git a/spring-cloud-tencent-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 1c6e627d..d61750e0 100644 --- a/spring-cloud-tencent-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,4 @@ com.tencent.cloud.common.util.ApplicationContextAwareUtils com.tencent.cloud.common.metadata.config.MetadataAutoConfiguration com.tencent.cloud.common.metadata.endpoint.PolarisMetadataEndpointAutoConfiguration +com.tencent.cloud.common.util.inet.PolarisInetUtilsAutoConfiguration diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml index d89dca90..e2a354a0 100644 --- a/spring-cloud-tencent-dependencies/pom.xml +++ b/spring-cloud-tencent-dependencies/pom.xml @@ -74,7 +74,7 @@ 1.14.0-2022.0.5-SNAPSHOT - 1.15.6 + 1.15.7-SNAPSHOT 32.0.1-jre @@ -84,6 +84,7 @@ 2.15.3 3.21.7 3.0.0 + 2.9.9 2.0.2 @@ -249,6 +250,12 @@ ${okio.version} + + joda-time + joda-time + ${joda-time.version} + + org.mockito mockito-inline diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java b/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java deleted file mode 100644 index 1d97d1fb..00000000 --- a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java +++ /dev/null @@ -1,58 +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.polaris.config.example; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.stereotype.Component; - -/** - * example property object. - * - * @author lepdou 2022-03-28 - */ -@Component -@ConfigurationProperties(prefix = "teacher") -public class Person { - - private String name; - - private int age; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - @Override - public String toString() { - return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; - } - -} diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/resources/application.yml b/spring-cloud-tencent-examples/polaris-config-data-example/src/main/resources/application.yml deleted file mode 100644 index 06a89b7b..00000000 --- a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/resources/application.yml +++ /dev/null @@ -1,26 +0,0 @@ -server: - port: 48085 -spring: - application: - name: polaris-config-data-example - cloud: - polaris: - address: grpc://119.91.66.223:8091 - namespace: default - config: - auto-refresh: true # auto refresh when config file changed - groups: - - name: ${spring.application.name} # group name - files: [ "config/application.properties", "config/bootstrap.yml" ] - config: - import: - - optional:polaris - - optional:polaris:test.yml - - optional:polaris:configdataexample:test.yml - - optional:polaris:config/bootstrap.yml -management: - endpoints: - web: - exposure: - include: - - polaris-config diff --git a/spring-cloud-tencent-examples/pom.xml b/spring-cloud-tencent-examples/pom.xml index 0bed0196..8eccfd44 100644 --- a/spring-cloud-tencent-examples/pom.xml +++ b/spring-cloud-tencent-examples/pom.xml @@ -20,10 +20,10 @@ metadata-transfer-example polaris-router-grayrelease-example polaris-router-featureenv-example - polaris-config-data-example quickstart-example lossless-example polaris-router-grayrelease-lane-example + tsf-example diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/pom.xml b/spring-cloud-tencent-examples/tsf-example/consumer-demo/pom.xml similarity index 79% rename from spring-cloud-tencent-examples/polaris-config-data-example/pom.xml rename to spring-cloud-tencent-examples/tsf-example/consumer-demo/pom.xml index 101a668a..ca0dc435 100644 --- a/spring-cloud-tencent-examples/polaris-config-data-example/pom.xml +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/pom.xml @@ -2,31 +2,29 @@ + 4.0.0 - spring-cloud-tencent-examples + tsf-example com.tencent.cloud ${revision} ../pom.xml - 4.0.0 - - polaris-config-data-example + consumer-demo - org.springframework.boot - spring-boot-starter-web + com.tencent.cloud + spring-cloud-starter-tencent-all - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-config + org.springframework.boot + spring-boot-starter-web - org.springframework.boot - spring-boot-starter-actuator + org.springframework.cloud + spring-cloud-starter-openfeign @@ -58,5 +56,4 @@ - \ No newline at end of file diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/ConsumerApplication.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/ConsumerApplication.java new file mode 100644 index 00000000..a28993aa --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/ConsumerApplication.java @@ -0,0 +1,41 @@ +/* + * 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.tsf.demo.consumer; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.tsf.annotation.EnableTsf; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +@EnableFeignClients // 使用Feign微服务调用时请启用 +@EnableTsf +public class ConsumerApplication { + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/ConsumerController.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/ConsumerController.java new file mode 100644 index 00000000..63f4f97f --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/controller/ConsumerController.java @@ -0,0 +1,53 @@ +/* + * 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.tsf.demo.consumer.controller; + +import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderDemoService; +import com.tencent.cloud.tsf.demo.consumer.proxy.ProviderService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +@RestController +public class ConsumerController { + @Autowired + private RestTemplate restTemplate; + @Autowired + private ProviderService providerService; + @Autowired + private ProviderDemoService providerDemoService; + + @RequestMapping(value = "/echo-rest/{str}", method = RequestMethod.GET) + public String restProvider(@PathVariable String str) { + return restTemplate.getForObject("http://provider-demo/echo/" + str, String.class); + } + + @RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET) + public String feignProvider(@PathVariable String str) { + return providerDemoService.echo(str); + } + + @RequestMapping(value = "/echo-feign-url/{str}", method = RequestMethod.GET) + public String feignUrlProvider(@PathVariable String str) { + return providerService.echo(str); + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/entity/CustomMetadata.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/entity/CustomMetadata.java new file mode 100644 index 00000000..47a38b47 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/entity/CustomMetadata.java @@ -0,0 +1,32 @@ +/* + * 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.tsf.demo.consumer.entity; + +/** + * 用户自定义 Metadata. + */ +public class CustomMetadata { + + private String name; + private String value; + + public CustomMetadata(String name, String value) { + this.name = name; + this.value = value; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java new file mode 100644 index 00000000..c102fc84 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderDemoService.java @@ -0,0 +1,36 @@ +/* + * 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.tsf.demo.consumer.proxy; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "provider-demo") +public interface ProviderDemoService { + @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET) + String echo(@PathVariable("str") String str); + + @RequestMapping(value = "/echo/error/{str}", method = RequestMethod.GET) + String echoError(@PathVariable("str") String str); + + @RequestMapping(value = "/echo/slow/{str}", method = RequestMethod.GET) + String echoSlow(@PathVariable("str") String str, @RequestParam("delay") int delay); +} diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderService.java similarity index 51% rename from spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java rename to spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderService.java index b0efd8b3..ac540217 100644 --- a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/java/com/tencent/cloud/tsf/demo/consumer/proxy/ProviderService.java @@ -13,37 +13,32 @@ * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. - * */ -package com.tencent.cloud.polaris.config.example; - -import java.util.Set; - -import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener; -import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent; +package com.tencent.cloud.tsf.demo.consumer.proxy; +import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; /** - * Custom Config Listener Example . - * - * @author Palmer Xu 2022-06-06 + * 测试通过URL配置FeignClient + * 使用时修改provider-ip:provider-port配置 */ -@Component -public final class PersonConfigChangeListener { +@FeignClient(name = "provider", url = "http://127.0.0.1:18081", fallback = FeignClientFallback.class) +public interface ProviderService { - /** - * PolarisConfigKVFileChangeListener Example . - * @param event instance of {@link ConfigChangeEvent} - */ - @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = "teacher") - public void onChange(ConfigChangeEvent event) { - Set changedKeys = event.changedKeys(); + @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET) + String echo(@PathVariable("str") String str); - for (String changedKey : changedKeys) { - System.out.printf("%s = %s \n", changedKey, event.getChange(changedKey)); - } - } +} +@Component +class FeignClientFallback implements ProviderService { + @Override + public String echo(String str) { + return "tsf-fault-tolerance-" + str; + } } diff --git a/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/application.yml b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/application.yml new file mode 100644 index 00000000..eb7a01b0 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/consumer-demo/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 18083 +spring: + application: + name: consumer-demo + config: + import: optional:polaris + +feign: + tsf: + enabled: true + +#本地测试时打开 +#tsf_namespace_id: default_namespace + +logging: + file: + name: /tsf-demo-logs/${spring.application.name}/root.log + level: + root: INFO diff --git a/spring-cloud-tencent-examples/tsf-example/pom.xml b/spring-cloud-tencent-examples/tsf-example/pom.xml new file mode 100644 index 00000000..41379d93 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/pom.xml @@ -0,0 +1,41 @@ + + + + + + spring-cloud-tencent-examples + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + tsf-example + Spring Cloud Tencent TSF Examples + pom + + + true + + + + provider-demo + consumer-demo + + diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml b/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml new file mode 100644 index 00000000..202b268a --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + tsf-example + com.tencent.cloud + ${revision} + ../pom.xml + + provider-demo + + + + com.tencent.cloud + spring-cloud-starter-tencent-all + + + + org.springframework.boot + spring-boot-starter-web + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PolarisConfigDataExampleApplication.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderApplication.java similarity index 82% rename from spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PolarisConfigDataExampleApplication.java rename to spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderApplication.java index ed8d8be3..accc1811 100644 --- a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/PolarisConfigDataExampleApplication.java +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderApplication.java @@ -15,18 +15,17 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.config.example; +package com.tencent.cloud.tsf.demo.provider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.tsf.annotation.EnableTsf; -/** - * @author wlx - */ @SpringBootApplication -public class PolarisConfigDataExampleApplication { +@EnableTsf +public class ProviderApplication { public static void main(String[] args) { - SpringApplication.run(PolarisConfigDataExampleApplication.class, args); + SpringApplication.run(ProviderApplication.class, args); } } diff --git a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/ConfigController.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderConfigController.java similarity index 63% rename from spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/ConfigController.java rename to spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderConfigController.java index d06a732e..bfd49ba5 100644 --- a/spring-cloud-tencent-examples/polaris-config-data-example/src/main/java/com/tencent/cloud/polaris/config/example/ConfigController.java +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderConfigController.java @@ -14,42 +14,23 @@ * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.config.example; -import org.springframework.beans.factory.annotation.Autowired; +package com.tencent.cloud.tsf.demo.provider; + import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -/** - * the endpoint for get config. - * - * @author lepdou 2022-03-10 - */ @RestController @RefreshScope -public class ConfigController { - - @Value("${timeout:1000}") - private int timeout; +public class ProviderConfigController { - @Autowired - private Person person; + @Value("${provider.name:default}") + private String name; - @Autowired - private Environment environment; - - @GetMapping("/timeout") - public int timeout() { - environment.getProperty("timeout", "1000"); - return timeout; - } - - @GetMapping("/person") - public String person() { - return person.toString(); + @GetMapping("/config/name") + public String getConfig() { + return name; } - } diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderController.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderController.java new file mode 100644 index 00000000..4d56ed3a --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/ProviderController.java @@ -0,0 +1,153 @@ +/* + * 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.tsf.demo.provider; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +import com.tencent.cloud.tsf.demo.provider.config.ProviderNameConfig; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + + +/** + * @Author leoziltong@tencent.com + * @Date: 2021/5/11 17:10 + */ +@RestController +public class ProviderController { + + private static final Logger LOG = LoggerFactory.getLogger(ProviderController.class); + + @Value("${spring.application.name:}") + private String applicationName; + + @Autowired + private ProviderNameConfig providerNameConfig; + + // 获取本机ip + public static String getInet4Address() { + Enumeration nis; + String ip = null; + try { + nis = NetworkInterface.getNetworkInterfaces(); + for (; nis.hasMoreElements(); ) { + NetworkInterface ni = nis.nextElement(); + Enumeration ias = ni.getInetAddresses(); + for (; ias.hasMoreElements(); ) { + InetAddress ia = ias.nextElement(); + if (ia instanceof Inet4Address && !ia.getHostAddress().equals("127.0.0.1")) { + ip = ia.getHostAddress(); + } + } + } + } + catch (SocketException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return ip; + } + + @RequestMapping(value = "/hello", method = RequestMethod.GET) + public String hello() { + String echoHello = "say hello"; + LOG.info(echoHello); + return echoHello; + } + + @RequestMapping(value = "/echo/{param}", method = RequestMethod.GET) + public String echo(@PathVariable String param, HttpServletResponse response) throws IOException { + switch (param) { + case "1xx": + response.setStatus(HttpServletResponse.SC_CONTINUE); + response.getWriter().write("mock 1xx return."); + response.getWriter().flush(); + return "mock 1xx return."; + case "3xx": + response.setStatus(HttpServletResponse.SC_FOUND); + response.getWriter().write("mock 3xx return."); + response.getWriter().flush(); + return "mock 3xx return."; + case "4xx": + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + response.getWriter().write("mock 4xx return."); + response.getWriter().flush(); + return "mock 4xx return."; + case "5xx": + response.setStatus(HttpServletResponse.SC_BAD_GATEWAY); + response.getWriter().write("mock 5xx return."); + response.getWriter().flush(); + return "mock 5xx return."; + default: + LOG.info("provider-demo -- request param: [" + param + "]"); + String result = "from host-ip: " + getInet4Address() + ", request param: " + param + ", response from " + providerNameConfig.getName(); + LOG.info("provider-demo -- provider config name: [" + providerNameConfig.getName() + ']'); + LOG.info("provider-demo -- response info: [" + result + "]"); + return result; + } + + } + + @RequestMapping(value = "/echo/error/{param}", method = RequestMethod.GET) + public String echoError(@PathVariable String param) { + LOG.info("Error request param: [" + param + "], throw exception"); + + throw new RuntimeException("mock-ex"); + } + + /** + * 延迟返回. + * @param param 参数 + * @param delay 延时时间,单位毫秒 + * @throws InterruptedException InterruptedException + */ + @RequestMapping(value = "/echo/slow/{param}", method = RequestMethod.GET) + public String echoSlow(@PathVariable String param, @RequestParam(required = false) Integer delay) throws InterruptedException { + int sleepTime = delay == null ? 1000 : delay; + LOG.info("slow request param: [" + param + "], Start sleep: [" + sleepTime + "]ms"); + Thread.sleep(sleepTime); + LOG.info("slow request param: [" + param + "], End sleep: [" + sleepTime + "]ms"); + + String result = "request param: " + param + + ", slow response from " + applicationName + + ", sleep: [" + sleepTime + "]ms"; + return result; + } + + @RequestMapping(value = "/echo/unit/{param}", method = RequestMethod.GET) + public String echoUnit(@PathVariable String param) { + LOG.info("provider-demo -- unit request param: [" + param + "]"); + String result = "request param: " + param + ", response from " + applicationName; + LOG.info("provider-demo -- unit provider config name: [" + applicationName + ']'); + LOG.info("provider-demo -- unit response info: [" + result + "]"); + return result; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfig.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfig.java new file mode 100644 index 00000000..45c04f22 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfig.java @@ -0,0 +1,37 @@ +/* + * 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.tsf.demo.provider.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +@Component +@RefreshScope +@ConfigurationProperties(prefix = "provider.config") +public class ProviderNameConfig { + private String name = "echo-provider-default-name"; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfigChangeListener.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfigChangeListener.java new file mode 100644 index 00000000..1493c910 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/config/ProviderNameConfigChangeListener.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.tsf.demo.provider.config; + +import com.tencent.tsf.consul.config.watch.ConfigChangeCallback; +import com.tencent.tsf.consul.config.watch.ConfigChangeListener; +import com.tencent.tsf.consul.config.watch.ConfigProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.stereotype.Component; + +@Component +@ConfigChangeListener(prefix = "provider.config", value = {"name"}) +public class ProviderNameConfigChangeListener implements ConfigChangeCallback { + + private static final Logger log = LoggerFactory.getLogger(ProviderNameConfigChangeListener.class); + + @Override + public void callback(ConfigProperty lastConfigProperty, ConfigProperty newConfigProperty) { + log.info("[TSF SDK] Configuration Change Listener: key: {}, old value: {}, new value: {}", + lastConfigProperty.getKey(), lastConfigProperty.getValue(), newConfigProperty.getValue()); + } + +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/controller/SwaggerApiController.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/controller/SwaggerApiController.java new file mode 100644 index 00000000..be6cc6cd --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/controller/SwaggerApiController.java @@ -0,0 +1,145 @@ +/* + * 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.tsf.demo.provider.swagger.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.tencent.cloud.tsf.demo.provider.swagger.model.MessageBox; +import com.tencent.cloud.tsf.demo.provider.swagger.model.MessageModel; +import com.tencent.cloud.tsf.demo.provider.swagger.model.MessageUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController("/swagger") +@Tag(description = "swagger 测试", name = "swaggerValue1") +public class SwaggerApiController { + + @RequestMapping(value = "/swagger/findMessages", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @Operation(method = "POST", + summary = "根据任务ID查询任务列表", + description = "根据任务ID查询任务列表Note") + public List findMessages(@RequestBody + @Parameter(name = "msgIds", description = "消息ID列表") List msgIds) { + List messageModels = new ArrayList<>(); + MessageModel messageModel = new MessageModel(); + messageModel.setMsgContent("test1"); + messageModel.setMsgId("1"); + messageModel.setSendTime(System.currentTimeMillis()); + MessageUser messageSender = new MessageUser(); + messageSender.setEmail("abc@xxxx.com"); + messageSender.setName("特朗普"); + messageSender.setOfficeAddress("华盛顿白宫"); + messageSender.setPhoneNum("911911911"); + messageModel.setSendUser(messageSender); + MessageUser messageReceiver = new MessageUser(); + messageReceiver.setEmail("abc@xxxx.com"); + messageReceiver.setName("拜登"); + messageReceiver.setOfficeAddress("华盛顿白宫"); + messageReceiver.setPhoneNum("911911911"); + messageModel.setReceiveUsers(Lists.newArrayList(messageReceiver)); + messageModels.add(messageModel); + return messageModels; + } + + //虽然这些@ExampleProperty和@Example属性已经在Swagger中实现,但Springfox还没有支持它们。问题仍然存在: + //https://github.com/springfox/springfox/issues/853 + //https://github.com/springfox/springfox/issues/1536 + + @Operation(summary = "获取消息内容", method = "GET", description = "获取消息内容Note") + @ApiResponse(responseCode = "200", description = "abcdef", content = @Content(schema = @Schema(implementation = String.class))) + @RequestMapping("/swagger/getMessageContent") + public String getMessageContent(@RequestParam(name = "id") + @Parameter(description = "消息ID", name = "id") String msgId) { + return "abcdefg"; + } + + @Operation(summary = "获取消息详情", method = "GET", description = "获取消息内容Note") + @ApiResponses({@ApiResponse(responseCode = "200", description = "abcdef", content = @Content(schema = @Schema(implementation = MessageModel.class)))}) + @RequestMapping(value = "/swagger/getMessageDetail", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public MessageModel getMessageDetail(@RequestParam(name = "id") + @Parameter(description = "消息ID", name = "id") String msgId) { + MessageModel messageModel = new MessageModel(); + messageModel.setMsgContent("test1"); + messageModel.setMsgId("1"); + messageModel.setSendTime(System.currentTimeMillis()); + MessageUser messageSender = new MessageUser(); + messageSender.setEmail("abc@xxxx.com"); + messageSender.setName("特朗普"); + messageSender.setOfficeAddress("华盛顿白宫"); + messageSender.setPhoneNum("911911911"); + messageModel.setSendUser(messageSender); + MessageUser messageReceiver = new MessageUser(); + messageReceiver.setEmail("abc@xxxx.com"); + messageReceiver.setName("拜登"); + messageReceiver.setOfficeAddress("华盛顿白宫"); + messageReceiver.setPhoneNum("911911911"); + messageModel.setReceiveUsers(Lists.newArrayList(messageReceiver)); + return messageModel; + } + + @Operation(summary = "获取投递箱详情", method = "GET", description = "") + @ApiResponses({@ApiResponse(responseCode = "200", description = "获取投递箱成功", content = @Content(schema = @Schema(implementation = MessageBox.class)))}) + @RequestMapping(value = "/swagger/getMessageBox", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public MessageBox getMessageBox(@RequestParam @Parameter(required = true, description = "投递箱ID") String boxId, + @RequestParam @Parameter(name = "sizeLimit", example = "10", description = "投递箱最大投递数") int maxSizeLimit) { + return new MessageBox(); + } + + @Operation(summary = "获取投递箱详情V2", method = "POST", description = "") + @ApiResponses({@ApiResponse(responseCode = "200", description = "获取投递箱成功", content = @Content(schema = @Schema(implementation = MessageBox.class)))}) + @RequestMapping(value = "/swagger/v2/getMessageBox", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public MessageBox getMessageBoxV2(@RequestBody + @Parameter(required = true, name = "messageBox", example = "投递箱信息") MessageBox messageBox) { + return new MessageBox(); + } + + @Operation(summary = "获取投递箱详情V3", method = "POST", description = "") + @ApiResponses({@ApiResponse(responseCode = "200", description = "获取投递箱成功", content = @Content(schema = @Schema(implementation = Map.class)))}) + @RequestMapping(value = "/swagger/v3/getMessageBox", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Map queryMessageBoxV3(@RequestBody + @Parameter(required = true, name = "messageBox", example = "投递箱信息") MessageBox messageBox) { + + return new HashMap<>(); + } + + + @Operation(summary = "获取投递箱地址", method = "GET", description = "") + @ApiResponses({@ApiResponse(responseCode = "200", description = "投递箱地址", content = @Content(schema = @Schema(implementation = String.class)))}) + @RequestMapping(value = "/swagger/getMessageBoxAddress", produces = MediaType.TEXT_PLAIN_VALUE) + public String queryMessageBoxAddress(@RequestParam(name = "boxId") + @Parameter(description = "投递箱ID", name = "boxId", example = "box-qvp9htm5", required = true) String id) { + return "华盛顿白宫"; + + } + +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageBox.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageBox.java new file mode 100644 index 00000000..ec0a4ff0 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageBox.java @@ -0,0 +1,102 @@ +/* + * 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.tsf.demo.provider.swagger.model; + + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "消息投递箱") +public class MessageBox { + + @Schema(title = "默认失效天数", required = false) + private int expiredDays; + + @Schema(title = "最大失效天数", required = false) + private Integer maxExpiredDays; + + @Schema(title = "容量大小", required = false) + private Float capacity; + + @Schema(title = "最大容量大小", required = false) + private float maxCapacity; + + @Schema(title = "接受的信息数量", required = false) + private Double size; + + @Schema(title = "最大接受的信息数量", required = false) + private double maxSize; + + @Schema(title = "消息(循环测试嵌套对象)", required = false) + private MessageModel messageModel; + + public int getExpiredDays() { + return expiredDays; + } + + public void setExpiredDays(int expiredDays) { + this.expiredDays = expiredDays; + } + + public Integer getMaxExpiredDays() { + return maxExpiredDays; + } + + public void setMaxExpiredDays(Integer maxExpiredDays) { + this.maxExpiredDays = maxExpiredDays; + } + + public Float getCapacity() { + return capacity; + } + + public void setCapacity(Float capacity) { + this.capacity = capacity; + } + + public float getMaxCapacity() { + return maxCapacity; + } + + public void setMaxCapacity(float maxCapacity) { + this.maxCapacity = maxCapacity; + } + + public Double getSize() { + return size; + } + + public void setSize(Double size) { + this.size = size; + } + + public double getMaxSize() { + return maxSize; + } + + public void setMaxSize(double maxSize) { + this.maxSize = maxSize; + } + + public MessageModel getMessageModel() { + return messageModel; + } + + public void setMessageModel(MessageModel messageModel) { + this.messageModel = messageModel; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageModel.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageModel.java new file mode 100644 index 00000000..08d3a528 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageModel.java @@ -0,0 +1,92 @@ +/* + * 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.tsf.demo.provider.swagger.model; + +import java.util.List; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "消息", title = "messageModel") +public class MessageModel { + + @Schema(name = "id", title = "消息ID", required = true, description = "消息ID notes") + private String msgId; + + @Schema(title = "消息内容", required = false) + private String msgContent; + + @Schema(title = "消息发送者", required = true) + private MessageUser sendUser; + + @Schema(title = "消息接收者", required = true) + private List receiveUsers; + + @Schema(title = "消息发送时间", required = true) + private long sendTime; + + @Schema(title = "消息投递箱", required = false) + private MessageBox messageBox; + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public String getMsgContent() { + return msgContent; + } + + public void setMsgContent(String msgContent) { + this.msgContent = msgContent; + } + + public MessageUser getSendUser() { + return sendUser; + } + + public void setSendUser(MessageUser sendUser) { + this.sendUser = sendUser; + } + + public List getReceiveUsers() { + return receiveUsers; + } + + public void setReceiveUsers(List receiveUsers) { + this.receiveUsers = receiveUsers; + } + + public long getSendTime() { + return sendTime; + } + + public void setSendTime(long sendTime) { + this.sendTime = sendTime; + } + + public MessageBox getMessageBox() { + return messageBox; + } + + public void setMessageBox(MessageBox messageBox) { + this.messageBox = messageBox; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageUser.java b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageUser.java new file mode 100644 index 00000000..9bd2f06e --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/java/com/tencent/cloud/tsf/demo/provider/swagger/model/MessageUser.java @@ -0,0 +1,69 @@ +/* + * 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.tsf.demo.provider.swagger.model; + + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "消息发送/接收者") +public class MessageUser { + + @Schema(title = "用户姓名", name = "name") + private String name; + + @Schema(title = "邮箱地址") + private String email; + + @Schema(title = "电话号码") + private String phoneNum; + + @Schema(title = "办公地址") + private String officeAddress; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhoneNum() { + return phoneNum; + } + + public void setPhoneNum(String phoneNum) { + this.phoneNum = phoneNum; + } + + public String getOfficeAddress() { + return officeAddress; + } + + public void setOfficeAddress(String officeAddress) { + this.officeAddress = officeAddress; + } +} diff --git a/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/application.yml b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/application.yml new file mode 100644 index 00000000..d4ae0231 --- /dev/null +++ b/spring-cloud-tencent-examples/tsf-example/provider-demo/src/main/resources/application.yml @@ -0,0 +1,26 @@ +server: + port: 18081 +spring: + application: + name: provider-demo + config: + import: optional:polaris + cloud: + polaris: + namespace: default + enabled: true + stat: + enabled: true + port: 28081 + loadbalancer: + strategy: polarisWeightedRandom + tencent: + rpc-enhancement: + reporter: + enabled: true + +logging: + file: + name: /tsf-demo-logs/${spring.application.name}/root.log + level: + root: INFO 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 index 59a313be..416b4130 100644 --- 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 @@ -1,6 +1,6 @@ - spring-cloud-tencent-plugin-starters @@ -17,12 +17,7 @@ com.tencent.cloud - spring-cloud-tencent-polaris-context - - - - com.tencent.cloud - spring-cloud-tencent-commons + spring-cloud-tencent-rpc-enhancement @@ -52,12 +47,6 @@ test - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-discovery - true - - com.tencent.polaris polaris-test-mock-discovery 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 index 3f296088..89398f42 100644 --- 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 @@ -23,7 +23,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("spring.cloud.polaris.lossless") public class LosslessProperties { - private boolean enabled = true; + private boolean enabled = false; private int port = 28080; diff --git a/spring-cloud-tencent-polaris-context/pom.xml b/spring-cloud-tencent-polaris-context/pom.xml index 0ae40202..7c940815 100644 --- a/spring-cloud-tencent-polaris-context/pom.xml +++ b/spring-cloud-tencent-polaris-context/pom.xml @@ -70,6 +70,11 @@ + + com.tencent.polaris + connector-consul + + com.tencent.polaris connector-composite diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/ConditionalOnTsfEnabled.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/ConditionalOnTsfEnabled.java new file mode 100644 index 00000000..1851f4bd --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/ConditionalOnTsfEnabled.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.tencent.polaris.api.utils.StringUtils; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * Condition that if Polaris enabled. + * + * @author Haotian Zhang + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +@Conditional(ConditionalOnTsfEnabled.OnTsfEnabledCondition.class) +public @interface ConditionalOnTsfEnabled { + + class OnTsfEnabledCondition implements Condition { + + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { + String tsfAppId = conditionContext.getEnvironment().getProperty("tsf_app_id", ""); + return StringUtils.isNotBlank(tsfAppId); + } + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCoreProperties.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCoreProperties.java new file mode 100644 index 00000000..376ae0e5 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCoreProperties.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Core properties. + * + * @author Haotian Zhang + */ +@ConfigurationProperties("tsf") +public class TsfCoreProperties { + + @Value("${tse_polaris_ip:}") + private String tsePolarisIp = ""; + + @Value("${tsf_consul_enable:false}") + private boolean tsfConsulEnable = false; + + @Value("${tse_polaris_enable:false}") + private boolean tsePolarisEnable = false; + + public String getTsePolarisIp() { + return tsePolarisIp; + } + + public void setTsePolarisIp(String tsePolarisIp) { + this.tsePolarisIp = tsePolarisIp; + } + + public boolean isTsfConsulEnable() { + return tsfConsulEnable; + } + + public void setTsfConsulEnable(boolean tsfConsulEnable) { + this.tsfConsulEnable = tsfConsulEnable; + } + + public boolean isTsePolarisEnable() { + return tsePolarisEnable; + } + + public void setTsePolarisEnable(boolean tsePolarisEnable) { + this.tsePolarisEnable = tsePolarisEnable; + } + + @Override + public String toString() { + return "TsfCoreProperties{" + + "tsePolarisIp='" + tsePolarisIp + '\'' + + ", tsfConsulEnable=" + tsfConsulEnable + + ", tsePolarisEnable=" + tsePolarisEnable + + '}'; + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesAutoConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesAutoConfiguration.java new file mode 100644 index 00000000..5afd06d0 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesAutoConfiguration.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.polaris.context.tsf.config; + +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Core properties auto configuration. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter(PolarisContextAutoConfiguration.class) +@ConditionalOnTsfEnabled +public class TsfCorePropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public TsfCoreProperties tsfCoreProperties() { + return new TsfCoreProperties(); + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesBootstrapConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesBootstrapConfiguration.java new file mode 100644 index 00000000..7b8054d6 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/config/TsfCorePropertiesBootstrapConfiguration.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Core properties bootstrap configuration. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@Import(TsfCorePropertiesAutoConfiguration.class) +public class TsfCorePropertiesBootstrapConfiguration { + +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulAutoConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulAutoConfiguration.java new file mode 100644 index 00000000..a7eea534 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulAutoConfiguration.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf.consul; + +import com.tencent.cloud.polaris.context.tsf.ConditionalOnTsfEnabled; + +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; + +@Configuration +@EnableConfigurationProperties +@ConditionalOnTsfEnabled +public class TsfConsulAutoConfiguration { + + static { + // 默认关闭对 discovery client(consul)的健康探测,避免 consul 故障时,影响监控探测 + System.setProperty("spring.cloud.discovery.client.health-indicator.enabled", "false"); + } + + @Bean + @ConditionalOnMissingBean + public TsfConsulProperties tsfConsulProperties() { + return new TsfConsulProperties(); + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulBootstrapConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulBootstrapConfiguration.java new file mode 100644 index 00000000..277b80f3 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulBootstrapConfiguration.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.polaris.context.tsf.consul; + +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration(proxyBeanMethods = false) +@Import({PolarisContextAutoConfiguration.class, TsfConsulAutoConfiguration.class}) +public class TsfConsulBootstrapConfiguration { + +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulProperties.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulProperties.java new file mode 100644 index 00000000..6cce59a4 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/consul/TsfConsulProperties.java @@ -0,0 +1,107 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf.consul; + + +import jakarta.validation.constraints.NotNull; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.validation.annotation.Validated; + +/** + * @author Spencer Gibb + */ +@Validated +public class TsfConsulProperties { + + /** Consul agent hostname. Defaults to '127.0.0.1'. */ + @Value("${tsf_consul_ip:${spring.cloud.consul.host:${SPRING_CLOUD_CONSUL_HOST:localhost}}}") + @NotNull + private String host = "localhost"; + + /** + * Consul agent scheme (HTTP/HTTPS). If there is no scheme in address - client + * will use HTTP. + */ + @Value("${spring.cloud.consul.scheme:${SPRING_CLOUD_CONSUL_SCHEME:}}") + private String scheme; + + /** Consul agent port. Defaults to '8500'. */ + @Value("${tsf_consul_port:${spring.cloud.consul.port:${SPRING_CLOUD_CONSUL_PORT:8500}}}") + @NotNull + private int port = 8500; + + /** Is spring cloud consul enabled. */ + @Value("${spring.cloud.consul.enabled:${SPRING_CLOUD_CONSUL_ENABLED:true}}") + private boolean enabled = true; + + @Value("${tsf_consul_ttl_read_timeout:5000}") + private int ttlReadTimeout = 5000; // default 5s + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public int getTtlReadTimeout() { + return ttlReadTimeout; + } + + public void setTtlReadTimeout(int ttlReadTimeout) { + this.ttlReadTimeout = ttlReadTimeout; + } + + + @Override + public String toString() { + return "ConsulProperties{" + + "host='" + host + '\'' + + ", scheme='" + scheme + '\'' + + ", port=" + port + + ", enabled=" + enabled + + ", ttlReadTimeout=" + ttlReadTimeout + + '}'; + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/env/TsfCoreEnvironmentPostProcessor.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/env/TsfCoreEnvironmentPostProcessor.java new file mode 100644 index 00000000..c12c316a --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/tsf/env/TsfCoreEnvironmentPostProcessor.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.context.tsf.env; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.polaris.api.utils.StringUtils; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +/** + * Read TSF env. + * + * @author Haotian Zhang + */ +public class TsfCoreEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + /** + * run before {@link ConfigDataEnvironmentPostProcessor}. + */ + public static final int ORDER = ConfigDataEnvironmentPostProcessor.ORDER - 1; + + @Override + public int getOrder() { + return ORDER; + } + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String tsfAppId = environment.getProperty("tsf_app_id"); + if (StringUtils.isNotBlank(tsfAppId)) { + Map defaultProperties = new HashMap<>(); + + // TODO 接入consul配置后需要改动这个选项的判断 + // tse_polaris_enable + defaultProperties.put("spring.cloud.polaris.config.enabled", environment.getProperty("tse_polaris_enable", "false")); + + // tse_polaris_ip + defaultProperties.put("spring.cloud.polaris.address", "grpc://" + environment.getProperty("tse_polaris_ip", "") + ":8091"); + + // tse_polaris_ip + defaultProperties.put("spring.cloud.polaris.stat.port", environment.getProperty("tsf_sctt_extensions_port", "11134")); + + MapPropertySource propertySource = new MapPropertySource("tsf-polaris-properties", defaultProperties); + environment.getPropertySources().addFirst(propertySource); + } + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/org/springframework/tsf/annotation/EnableTsf.java b/spring-cloud-tencent-polaris-context/src/main/java/org/springframework/tsf/annotation/EnableTsf.java new file mode 100644 index 00000000..364695e7 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/org/springframework/tsf/annotation/EnableTsf.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 org.springframework.tsf.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * Compatible with old versions TSF SDK. + * + * @author Haotian Zhang + */ + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@EnableAutoConfiguration +@EnableDiscoveryClient // 服务注册发现 +@EnableConfigurationProperties // 分布式配置 +public @interface EnableTsf { +} diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories index b5c740aa..27ed166e 100644 --- a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories @@ -1,4 +1,8 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration + com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration,\ + com.tencent.cloud.polaris.context.tsf.config.TsfCorePropertiesBootstrapConfiguration,\ + com.tencent.cloud.polaris.context.tsf.consul.TsfConsulBootstrapConfiguration org.springframework.context.ApplicationListener=\ com.tencent.cloud.polaris.context.logging.PolarisLoggingApplicationListener +org.springframework.boot.env.EnvironmentPostProcessor=\ + com.tencent.cloud.polaris.context.tsf.env.TsfCoreEnvironmentPostProcessor diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 8a05f2f3..e01f789f 100644 --- a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,2 +1,4 @@ com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration com.tencent.cloud.polaris.context.config.PolarisContextPostConfiguration +com.tencent.cloud.polaris.context.tsf.config.TsfCorePropertiesAutoConfiguration +com.tencent.cloud.polaris.context.tsf.consul.TsfConsulAutoConfiguration