feat:add Tencent Cloud TSF support. (#1317)

pull/1318/head
Haotian Zhang 4 weeks ago committed by GitHub
parent e99dbe8788
commit 3d057398fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

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

@ -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<PolarisConfigCustomExtensionLayer> polarisConfigCustomExtensionLayerLoader = ServiceLoader.load(PolarisConfigCustomExtensionLayer.class);
Iterator<PolarisConfigCustomExtensionLayer> 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;
}

@ -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();
}
/**
*
* 1Spring Cloud Consul ConfigConsul ConfigtsfConfigController
* 2@ConditionalOnPolarisConfigEnabled
* 3tsf.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);
}
}

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

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

@ -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";
/**
* TSFID.
*/
private static final String TSF_APPLICATION_ID = "tsf_application_id";
/**
* TSFID.
*/
private static final String TSF_GROUP_ID = "tsf_group_id";
/**
* TSFID.
*/
private static final String TSF_NAMESPACE_ID = "tsf_namespace_id";
/**
* TSFID.
*/
private static final String POLARIS_ADAPTOR_TSF_CONFIG_FORMAT = "spring.cloud.polaris.config.format";
// 最近一次的全量PolarisPropertySource集合PolarisPropertySource按 namespace + fileGroup + fileName 确保唯一)
private static final Set<String> registedPolarisPropertySets = Sets.newConcurrentHashSet();
// 命名空间分组namespace + fileGroup的去重Set集合如果这个分组已添加了ConfigFileGroupListener
private static final Set<String> 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<String, Object> 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<ConfigFileMetadata> 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<ConfigFileMetadata> 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;
}
}

@ -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 202388 4:56:18
*/
public final class PolarisPropertyCache {
private static final PolarisPropertyCache instance = new PolarisPropertyCache();
private final Set<String> cache = new HashSet<>();
private PolarisPropertyCache() {
}
public static PolarisPropertyCache getInstance() {
return instance;
}
public Set<String> getCache() {
return cache;
}
public void clear() {
cache.clear();
}
}

@ -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 202382 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");
}
/**
* TSFSDK.
*/
@RequestMapping("/tsf/innerApi/config/findAllConfig")
public Map<String, Object> findAllConfig() {
Set<String> keys = PolarisPropertyCache.getInstance().getCache();
return keys.stream()
.collect(HashMap::new, (map, key) -> map.put(key, environment.getProperty(key)), HashMap::putAll);
}
}

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

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

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

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

@ -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 truefalse
*/
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;
}
}

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

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

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

@ -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<TsfCallbackParam> 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<String> interestedKeys = new HashSet<>();
Set<String> 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<TsfCallbackParam> parseConfigChangeEventToTsfCallbackParam(ConfigChangeEvent event) {
List<TsfCallbackParam> result = new ArrayList<>();
Set<String> 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;
}
}
}

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

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

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

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

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

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

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

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

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

@ -19,18 +19,17 @@
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId>
<scope>test</scope>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-lossless-plugin</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>connector-consul</artifactId>
<artifactId>polaris-test-common</artifactId>
<scope>test</scope>
</dependency>
@ -83,6 +82,11 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>

@ -135,8 +135,8 @@ public class PolarisServiceRegistry implements ServiceRegistry<PolarisRegistrati
heartbeat(heartbeatRequest);
}
registration.setInstanceId(instanceRegisterResponse.getInstanceId());
LOGGER.info("polaris registry, {} {} {}:{} {} register finished", polarisDiscoveryProperties.getNamespace(),
registration.getServiceId(), registration.getHost(), registration.getPort(),
LOGGER.info("polaris registry, {} {} {} {}:{} {} register finished", polarisDiscoveryProperties.getNamespace(),
registration.getServiceId(), registration.getInstanceId(), registration.getHost(), registration.getPort(),
staticMetadataManager.getMergedStaticMetadata());
if (Objects.nonNull(polarisStatProperties) && polarisStatProperties.isEnabled()) {
try {

@ -0,0 +1,193 @@
/*
* 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.Collections;
import java.util.Map;
import com.tencent.cloud.common.constant.OrderConstant;
import com.tencent.cloud.common.util.JacksonUtils;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.config.PolarisContextProperties;
import com.tencent.cloud.polaris.context.tsf.config.TsfCoreProperties;
import com.tencent.cloud.polaris.context.tsf.consul.TsfConsulProperties;
import com.tencent.cloud.polaris.tsf.util.RegistrationUtil;
import com.tencent.polaris.api.config.plugin.DefaultPlugins;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.factory.config.consumer.DiscoveryConfigImpl;
import com.tencent.polaris.factory.config.global.ServerConnectorConfigImpl;
import com.tencent.polaris.factory.config.provider.RegisterConfigImpl;
import com.tencent.polaris.plugins.connector.common.constant.ConsulConstant;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.CollectionUtils;
/**
* Modifier for TSF discovery.
*
* @author Haotian Zhang
*/
public class TsfDiscoveryConfigModifier implements PolarisConfigModifier {
private static final Logger LOGGER = LoggerFactory.getLogger(TsfDiscoveryConfigModifier.class);
private final TsfCoreProperties tsfCoreProperties;
private final TsfConsulProperties tsfConsulProperties;
private final TsfDiscoveryProperties tsfDiscoveryProperties;
private final TsfHeartbeatProperties tsfHeartbeatProperties;
private final PolarisDiscoveryProperties polarisDiscoveryProperties;
private final PolarisContextProperties polarisContextProperties;
private final ApplicationContext context;
public TsfDiscoveryConfigModifier(TsfCoreProperties tsfCoreProperties, TsfConsulProperties tsfConsulProperties,
TsfDiscoveryProperties tsfDiscoveryProperties, TsfHeartbeatProperties tsfHeartbeatProperties,
PolarisDiscoveryProperties polarisDiscoveryProperties, PolarisContextProperties polarisContextProperties, ApplicationContext context) {
this.tsfCoreProperties = tsfCoreProperties;
this.tsfConsulProperties = tsfConsulProperties;
this.tsfDiscoveryProperties = tsfDiscoveryProperties;
this.tsfHeartbeatProperties = tsfHeartbeatProperties;
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
this.polarisContextProperties = polarisContextProperties;
this.context = context;
}
@Override
public void modify(ConfigurationImpl configuration) {
// namespace id
polarisDiscoveryProperties.setHeartbeatInterval(Long.valueOf(tsfHeartbeatProperties.computeHearbeatInterval()
.toStandardDuration().getMillis()).intValue());
polarisContextProperties.setNamespace(tsfDiscoveryProperties.getTsfNamespaceId());
polarisDiscoveryProperties.setNamespace(tsfDiscoveryProperties.getTsfNamespaceId());
System.setProperty("spring.cloud.polaris.namespace", tsfDiscoveryProperties.getTsfNamespaceId());
// application id
polarisDiscoveryProperties.setVersion(tsfDiscoveryProperties.getTsfProgVersion());
// instance id
polarisDiscoveryProperties.setInstanceId(tsfDiscoveryProperties.getInstanceId());
boolean consulEnable = tsfCoreProperties.isTsfConsulEnable();
boolean polarisEnable = tsfCoreProperties.isTsePolarisEnable();
// 删除可能存在的consul connector配置
if (!CollectionUtils.isEmpty(configuration.getGlobal().getServerConnectors())) {
for (ServerConnectorConfigImpl config : configuration.getGlobal().getServerConnectors()) {
if (StringUtils.equals(config.getId(), RegistrationUtil.ID)) {
configuration.getGlobal().getServerConnectors().remove(config);
}
}
}
else {
configuration.getGlobal().setServerConnectors(new ArrayList<>());
}
// 删除可能存在的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<String, String> 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;
}
}

@ -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<String> tags = new ArrayList<>();
/**
* If service discovery enabled.
*/
private boolean enabled = true;
/**
* Tags to use when registering management service.
*/
private List<String> 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<String, String> serverListQueryTags = new HashMap<>();
/**
* Map of serviceId's -> datacenter to query for in server list.
* This allows looking up services in another datacenters.
*/
private Map<String, String> 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<String, String> 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<String> getTags() {
return tags;
}
public void setTags(List<String> tags) {
this.tags = tags;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public List<String> getManagementTags() {
return managementTags;
}
public void setManagementTags(List<String> 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<String, String> getServerListQueryTags() {
return serverListQueryTags;
}
public void setServerListQueryTags(Map<String, String> serverListQueryTags) {
this.serverListQueryTags = serverListQueryTags;
}
public Map<String, String> getDatacenters() {
return datacenters;
}
public void setDatacenters(Map<String, String> 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<String, String> getServiceMeta() {
return serviceMeta;
}
public void setServiceMeta(final Map<String, String> 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 +
'}';
}
}
}

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

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

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

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

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

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

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

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

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

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

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

@ -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> servletContext;
public TsfServletRegistrationCustomizer(ObjectProvider<ServletContext> 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<String, String> metadata = registration.getMetadata();
List<String> 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));
}
}
}

@ -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<String> createTags(TsfDiscoveryProperties properties) {
List<String> 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<String, String> appendMetaIpAddress(Map<String, String> 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<String, String> 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);
}
}

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

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

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

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

@ -50,6 +50,11 @@
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>

@ -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<String> parseAddressList(String addressInfo) {
if (!StringUtils.hasText(addressInfo)) {
if (StringUtils.isBlank(addressInfo)) {
return Collections.emptyList();
}
List<String> 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<NetworkInterface> 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<InetAddress> 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;
}
}

@ -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 <T> List<T> getBeans(BeanFactory beanFactory, Class<T> requiredType) {
@ -45,4 +72,9 @@ public final class BeanFactoryUtils {
Map<String, T> beanMap = beansOfTypeIncludingAncestors((ListableBeanFactory) beanFactory, requiredType);
return new ArrayList<>(beanMap.values());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
BeanFactoryUtils.beanFactory = beanFactory;
}
}

@ -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<NetworkInterface> 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<InetAddress> 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<String> 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<String> result = executorService.submit(new Callable<String>() {
@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<NetworkInterface> 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<InetAddress> 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;
}
}

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

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

@ -0,0 +1,2 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.common.util.inet.PolarisInetUtilsBootstrapConfiguration

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

@ -74,7 +74,7 @@
<revision>1.14.0-2022.0.5-SNAPSHOT</revision>
<!-- Polaris SDK version -->
<polaris.version>1.15.6</polaris.version>
<polaris.version>1.15.7-SNAPSHOT</polaris.version>
<!-- Dependencies -->
<guava.version>32.0.1-jre</guava.version>
@ -84,6 +84,7 @@
<jackson.version>2.15.3</jackson.version>
<protobuf-java.version>3.21.7</protobuf-java.version>
<okio.version>3.0.0</okio.version>
<joda-time.version>2.9.9</joda-time.version>
<system-stubs-jupiter.version>2.0.2</system-stubs-jupiter.version>
<!-- Maven Plugin Versions -->
@ -249,6 +250,12 @@
<version>${okio.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${joda-time.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>

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

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

@ -20,10 +20,10 @@
<module>metadata-transfer-example</module>
<module>polaris-router-grayrelease-example</module>
<module>polaris-router-featureenv-example</module>
<module>polaris-config-data-example</module>
<module>quickstart-example</module>
<module>lossless-example</module>
<module>polaris-router-grayrelease-lane-example</module>
<module>tsf-example</module>
</modules>
<properties>

@ -2,31 +2,29 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-tencent-examples</artifactId>
<artifactId>tsf-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>polaris-config-data-example</artifactId>
<artifactId>consumer-demo</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-all</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependency -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-config</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
@ -58,5 +56,4 @@
</plugin>
</plugins>
</build>
</project>

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

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

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

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

@ -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
* URLFeignClient
* 使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<String> 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;
}
}

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

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent-examples</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>tsf-example</artifactId>
<name>Spring Cloud Tencent TSF Examples</name>
<packaging>pom</packaging>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<modules>
<module>provider-demo</module>
<module>consumer-demo</module>
</modules>
</project>

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>tsf-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>provider-demo</artifactId>
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

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

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

@ -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<NetworkInterface> nis;
String ip = null;
try {
nis = NetworkInterface.getNetworkInterfaces();
for (; nis.hasMoreElements(); ) {
NetworkInterface ni = nis.nextElement();
Enumeration<InetAddress> 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;
}
}

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

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

@ -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<MessageModel> findMessages(@RequestBody
@Parameter(name = "msgIds", description = "消息ID列表") List<String> msgIds) {
List<MessageModel> 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<String, Object> 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 "华盛顿白宫";
}
}

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

@ -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<MessageUser> 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<MessageUser> getReceiveUsers() {
return receiveUsers;
}
public void setReceiveUsers(List<MessageUser> 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;
}
}

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

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

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-tencent-plugin-starters</artifactId>
@ -17,12 +17,7 @@
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<dependency>
@ -52,12 +47,6 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-mock-discovery</artifactId>

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

@ -70,6 +70,11 @@
</exclusions>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>connector-consul</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>connector-composite</artifactId>

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

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

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

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

@ -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 clientconsul的健康探测避免 consul 故障时,影响监控探测
System.setProperty("spring.cloud.discovery.client.health-indicator.enabled", "false");
}
@Bean
@ConditionalOnMissingBean
public TsfConsulProperties tsfConsulProperties() {
return new TsfConsulProperties();
}
}

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

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

@ -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<String, Object> 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);
}
}
}

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

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

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

Loading…
Cancel
Save