Merge branch 'main' into i/gh-189

pull/237/head
VOPEN.XYZ 3 years ago committed by GitHub
commit 9b1eddb6e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,3 +1,7 @@
# Change Log
---
[UT: Add config module unit test](https://github.com/Tencent/spring-cloud-tencent/pull/229)
[Feature: Add config change listener feature support](https://github.com/Tencent/spring-cloud-tencent/pull/220)
[Feature: Support spring cloud gateway routers](https://github.com/Tencent/spring-cloud-tencent/pull/230)
[Feature: Add instance metadata spi for registration](https://github.com/Tencent/spring-cloud-tencent/pull/244)
[Bugfix: fix guava version conflict bug & fix router strong dependency on LoadBalancerClientFilter](https://github.com/Tencent/spring-cloud-tencent/pull/236)

@ -38,9 +38,11 @@
</scm>
<modules>
<module>spring-cloud-tencent-polaris-context</module>
<module>spring-cloud-tencent-commons</module>
<module>spring-cloud-tencent-polaris-context</module>
<module>spring-cloud-tencent-polaris-loadbalancer</module>
<module>spring-cloud-starter-tencent-metadata-transfer</module>
<module>spring-cloud-starter-tencent-polaris-config</module>
<module>spring-cloud-starter-tencent-polaris-discovery</module>
<module>spring-cloud-starter-tencent-polaris-ratelimit</module>
<module>spring-cloud-starter-tencent-polaris-circuitbreaker</module>
@ -48,8 +50,6 @@
<module>spring-cloud-tencent-dependencies</module>
<module>spring-cloud-tencent-examples</module>
<module>spring-cloud-tencent-coverage</module>
<module>spring-cloud-starter-tencent-polaris-config</module>
<module>spring-cloud-tencent-polaris-loadbalancer</module>
</modules>
<developers>

@ -19,9 +19,10 @@
package com.tencent.cloud.metadata.core.intercepter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor;
import feign.RequestInterceptor;
@ -64,10 +65,9 @@ public class EncodeTransferMedataFeignInterceptorTest {
private TestApplication.TestFeign testFeign;
@Test
public void test1() {
public void testTransitiveMetadataFromApplicationConfig() {
String metadata = testFeign.test();
Assertions.assertThat(metadata)
.isEqualTo("{\"b\":\"2\"}");
Assertions.assertThat(metadata).isEqualTo("2");
Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
.isEqualTo("1");
Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
@ -83,16 +83,13 @@ public class EncodeTransferMedataFeignInterceptorTest {
public String test(
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
return URLDecoder.decode(customMetadataStr, "UTF-8");
return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
}
@FeignClient(name = "test-feign", url = "http://localhost:8081")
public interface TestFeign {
@RequestMapping(value = "/test",
headers = {"X-SCT-Metadata-Transitive-a=11",
"X-SCT-Metadata-Transitive-b=22",
"X-SCT-Metadata-Transitive-c=33"})
@RequestMapping("/test")
String test();
}

@ -19,11 +19,12 @@
package com.tencent.cloud.metadata.core.intercepter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -32,6 +33,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
@ -51,9 +55,6 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" })
public class EncodeTransferMedataRestTemplateInterceptorTest {
@Autowired
private MetadataLocalProperties metadataLocalProperties;
@Autowired
private RestTemplate restTemplate;
@ -61,30 +62,14 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
private int localServerPort;
@Test
public void test1() {
// HttpHeaders httpHeaders = new HttpHeaders();
// httpHeaders.set(MetadataConstant.HeaderName.CUSTOM_METADATA,
// "{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
// HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
// String metadata = restTemplate
// .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
// httpEntity, String.class)
// .getBody();
// Assertions.assertThat(metadata)
// .isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}");
// Assertions.assertThat(metadataLocalProperties.getContent().get("a"))
// .isEqualTo("1");
// Assertions.assertThat(metadataLocalProperties.getContent().get("b"))
// .isEqualTo("2");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "a"))
// .isEqualTo("11");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"))
// .isEqualTo("22");
// Assertions
// .assertThat(MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "c"))
// .isEqualTo("33");
public void testTransitiveMetadataFromApplicationConfig() {
HttpHeaders httpHeaders = new HttpHeaders();
HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders);
String metadata = restTemplate
.exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET,
httpEntity, String.class)
.getBody();
Assertions.assertThat(metadata).isEqualTo("2");
}
@SpringBootApplication
@ -100,7 +85,7 @@ public class EncodeTransferMedataRestTemplateInterceptorTest {
public String test(
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr)
throws UnsupportedEncodingException {
return URLDecoder.decode(customMetadataStr, "UTF-8");
return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b");
}
}

@ -11,6 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-tencent-polaris-config</artifactId>
<name>Spring Cloud Starter Tencent Polaris Config</name>
<dependencies>
<!-- Spring Cloud Tencent dependencies start -->
@ -64,5 +65,28 @@
</dependency>
<!-- Spring cloud dependencies start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -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.polaris.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Whether to connect to a remote server, suitable for local development mode.
*
* @author lepdou 2022-06-11
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "spring.cloud.polaris.config.connect-remote-server", matchIfMissing = true)
public @interface ConditionalOnConnectRemoteServerEnabled {
}

@ -51,16 +51,18 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
public ConfigFileService configFileService(SDKContext sdkContext) {
return ConfigFileServiceFactory.createConfigFileService(sdkContext);
public PolarisPropertySourceManager polarisPropertySourceManager() {
return new PolarisPropertySourceManager();
}
@Bean
public PolarisPropertySourceManager polarisPropertySourceManager() {
return new PolarisPropertySourceManager();
@ConditionalOnConnectRemoteServerEnabled
public ConfigFileService configFileService(SDKContext sdkContext) {
return ConfigFileServiceFactory.createConfigFileService(sdkContext);
}
@Bean
@ConditionalOnConnectRemoteServerEnabled
public PolarisConfigFileLocator polarisConfigFileLocator(
PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties,
@ -73,6 +75,7 @@ public class PolarisConfigBootstrapAutoConfiguration {
}
@Bean
@ConditionalOnConnectRemoteServerEnabled
public ConfigurationModifier configurationModifier(PolarisConfigProperties polarisConfigProperties,
PolarisContextProperties polarisContextProperties) {
return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties);

@ -84,13 +84,14 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
CompositePropertySource compositePropertySource = new CompositePropertySource(
POLARIS_CONFIG_PROPERTY_SOURCE_NAME);
// load spring boot default config files
initInternalConfigFiles(compositePropertySource);
// load custom config files
List<ConfigFileGroup> configFileGroups = polarisConfigProperties.getGroups();
if (CollectionUtils.isEmpty(configFileGroups)) {
return compositePropertySource;
}
initInternalConfigFiles(compositePropertySource);
initCustomPolarisConfigFiles(compositePropertySource, configFileGroups);
return compositePropertySource;
@ -190,12 +191,10 @@ public class PolarisConfigFileLocator implements PropertySourceLocator {
// unknown extension is resolved as properties file
if (ConfigFileFormat.isPropertyFile(fileName)
|| ConfigFileFormat.isUnknownFile(fileName)) {
configKVFile = configFileService.getConfigPropertiesFile(namespace, group,
fileName);
configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName);
}
else if (ConfigFileFormat.isYamlFile(fileName)) {
configKVFile = configFileService.getConfigYamlFile(namespace, group,
fileName);
configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName);
}
else {
LOGGER.warn(

@ -34,6 +34,13 @@
"defaultValue": "",
"description": "List of imported config files.",
"sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties"
},
{
"name": "spring.cloud.polaris.config.connect-remote-server",
"type": "java.lang.Boolean",
"defaultValue": "true",
"description": "Whether to connect to a remote server, suitable for local development mode.",
"sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties"
}
]
}

@ -0,0 +1,170 @@
/*
* 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.adapter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.polaris.configuration.api.core.ConfigFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener;
/**
* Mock config kv file for test.
*@author lepdou 2022-06-11
*/
public class MockedConfigKVFile implements ConfigKVFile {
private final Map<String, Object> properties;
private final List<ConfigKVFileChangeListener> listeners = new ArrayList<>();
public MockedConfigKVFile(Map<String, Object> properties) {
this.properties = properties;
}
@Override
public String getProperty(String s, String s1) {
return String.valueOf(properties.get(s));
}
@Override
public Integer getIntProperty(String s, Integer integer) {
return null;
}
@Override
public Long getLongProperty(String s, Long aLong) {
return null;
}
@Override
public Short getShortProperty(String s, Short aShort) {
return null;
}
@Override
public Float getFloatProperty(String s, Float aFloat) {
return null;
}
@Override
public Double getDoubleProperty(String s, Double aDouble) {
return null;
}
@Override
public Byte getByteProperty(String s, Byte aByte) {
return null;
}
@Override
public Boolean getBooleanProperty(String s, Boolean aBoolean) {
return null;
}
@Override
public String[] getArrayProperty(String s, String s1, String[] strings) {
return new String[0];
}
@Override
public <T extends Enum<T>> T getEnumProperty(String s, Class<T> aClass, T t) {
return null;
}
@Override
public <T> T getJsonProperty(String s, Class<T> aClass, T t) {
return null;
}
@Override
public <T> T getJsonProperty(String s, Type type, T t) {
return null;
}
@Override
public Set<String> getPropertyNames() {
return properties.keySet();
}
@Override
public void addChangeListener(ConfigKVFileChangeListener configKVFileChangeListener) {
listeners.add(configKVFileChangeListener);
}
@Override
public void removeChangeListener(ConfigKVFileChangeListener configKVFileChangeListener) {
}
@Override
public String getContent() {
return null;
}
@Override
public <T> T asJson(Class<T> aClass, T t) {
return null;
}
@Override
public <T> T asJson(Type type, T t) {
return null;
}
@Override
public boolean hasContent() {
return false;
}
@Override
public void addChangeListener(ConfigFileChangeListener configFileChangeListener) {
}
@Override
public void removeChangeListener(ConfigFileChangeListener configFileChangeListener) {
}
public void fireChangeListener(ConfigKVFileChangeEvent event) {
for (ConfigKVFileChangeListener listener : listeners) {
listener.onChange(event);
}
}
@Override
public String getNamespace() {
return null;
}
@Override
public String getFileGroup() {
return null;
}
@Override
public String getFileName() {
return null;
}
}

@ -0,0 +1,188 @@
/*
* 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.adapter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.config.ConfigFileGroup;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.context.PolarisContextProperties;
import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import static org.mockito.Mockito.when;
/**
* test for {@link PolarisConfigFileLocator}
*@author lepdou 2022-06-11
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisConfigFileLocatorTest {
@Mock
private PolarisConfigProperties polarisConfigProperties;
@Mock
private PolarisContextProperties polarisContextProperties;
@Mock
private ConfigFileService configFileService;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@Mock
private Environment environment;
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
@Test
public void testLoadApplicationPropertiesFile() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
// application.properties
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(environment.getActiveProfiles()).thenReturn(new String[] {});
PropertySource<?> propertySource = locator.locate(environment);
Assert.assertEquals("v1", propertySource.getProperty("k1"));
Assert.assertEquals("v2", propertySource.getProperty("k2"));
Assert.assertEquals("v3", propertySource.getProperty("k3"));
}
@Test
public void testActiveProfileFilesPriorityBiggerThanDefault() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
// application.properties
Map<String, Object> applicationProperties = new HashMap<>();
applicationProperties.put("k1", "v1");
applicationProperties.put("k2", "v2");
applicationProperties.put("k3", "v3");
ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties"))
.thenReturn(propertiesFile);
// application-dev.properties
Map<String, Object> devProperties = new HashMap<>();
devProperties.put("k1", "v11");
ConfigKVFile devFile = new MockedConfigKVFile(devProperties);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application-dev.properties"))
.thenReturn(devFile);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application-dev.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap-dev.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yml")).thenReturn(emptyConfigFile);
when(polarisConfigProperties.getGroups()).thenReturn(null);
when(environment.getActiveProfiles()).thenReturn(new String[] {"dev"});
PropertySource<?> propertySource = locator.locate(environment);
Assert.assertEquals("v11", propertySource.getProperty("k1"));
Assert.assertEquals("v2", propertySource.getProperty("k2"));
Assert.assertEquals("v3", propertySource.getProperty("k3"));
}
@Test
public void testGetCustomFiles() {
PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties,
configFileService, polarisPropertySourceManager, environment);
when(polarisContextProperties.getNamespace()).thenReturn(testNamespace);
when(polarisContextProperties.getService()).thenReturn(testServiceName);
Map<String, Object> emptyMap = new HashMap<>();
ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile);
when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile);
when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile);
List<ConfigFileGroup> customFiles = new LinkedList<>();
ConfigFileGroup configFileGroup = new ConfigFileGroup();
String customGroup = "group1";
configFileGroup.setName(customGroup);
String customFile1 = "file1.properties";
String customFile2 = "file2.properties";
configFileGroup.setFiles(Lists.newArrayList(customFile1, customFile2));
customFiles.add(configFileGroup);
when(polarisConfigProperties.getGroups()).thenReturn(customFiles);
when(environment.getActiveProfiles()).thenReturn(new String[] {});
// file1.properties
Map<String, Object> file1Map = new HashMap<>();
file1Map.put("k1", "v1");
file1Map.put("k2", "v2");
ConfigKVFile file1 = new MockedConfigKVFile(file1Map);
when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile1)).thenReturn(file1);
// file2.properties
Map<String, Object> file2Map = new HashMap<>();
file2Map.put("k1", "v11");
file2Map.put("k3", "v3");
ConfigKVFile file2 = new MockedConfigKVFile(file2Map);
when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile2)).thenReturn(file2);
PropertySource<?> propertySource = locator.locate(environment);
Assert.assertEquals("v1", propertySource.getProperty("k1"));
Assert.assertEquals("v2", propertySource.getProperty("k2"));
Assert.assertEquals("v3", propertySource.getProperty("k3"));
}
}

@ -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.cloud.polaris.config.adapter;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.Lists;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.context.refresh.ContextRefresher;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* test for {@link PolarisPropertySourceAutoRefresher}
*@author lepdou 2022-06-11
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisPropertiesSourceAutoRefresherTest {
@Mock
private PolarisConfigProperties polarisConfigProperties;
@Mock
private PolarisPropertySourceManager polarisPropertySourceManager;
@Mock
private ContextRefresher contextRefresher;
private final String testNamespace = "testNamespace";
private final String testServiceName = "testServiceName";
private final String testFileName = "application.properties";
@Test
public void testConfigFileChanged() {
PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties,
polarisPropertySourceManager, contextRefresher);
when(polarisConfigProperties.isAutoRefresh()).thenReturn(true);
Map<String, Object> content = new HashMap<>();
content.put("k1", "v1");
content.put("k2", "v2");
content.put("k3", "v3");
MockedConfigKVFile file = new MockedConfigKVFile(content);
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName,
file, content);
when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED);
ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED);
ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k2", "v2", null, ChangeType.DELETED);
Map<String, ConfigPropertyChangeInfo> changeInfos = new HashMap<>();
changeInfos.put("k1", changeInfo);
changeInfos.put("k2", changeInfo3);
changeInfos.put("k4", changeInfo2);
ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
refresher.onApplicationEvent(null);
file.fireChangeListener(event);
Assert.assertEquals("v11", polarisPropertySource.getProperty("k1"));
Assert.assertEquals("v3", polarisPropertySource.getProperty("k3"));
Assert.assertNull(polarisPropertySource.getProperty("k2"));
Assert.assertEquals("v4", polarisPropertySource.getProperty("k4"));
verify(contextRefresher).refresh();
}
@Test
public void testNewConfigFile() {
PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties,
polarisPropertySourceManager, contextRefresher);
when(polarisConfigProperties.isAutoRefresh()).thenReturn(true);
Map<String, Object> emptyContent = new HashMap<>();
MockedConfigKVFile file = new MockedConfigKVFile(emptyContent);
PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName,
file, emptyContent);
when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource));
ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", null, "v1", ChangeType.ADDED);
Map<String, ConfigPropertyChangeInfo> changeInfos = new HashMap<>();
changeInfos.put("k1", changeInfo);
ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos);
refresher.onApplicationEvent(null);
file.fireChangeListener(event);
Assert.assertEquals("v1", polarisPropertySource.getProperty("k1"));
verify(contextRefresher).refresh();
}
}

@ -0,0 +1,124 @@
/*
* 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.listener;
import com.google.common.collect.Sets;
import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
/**
* Integration testing for change listener.
*@author lepdou 2022-06-11
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = DEFINED_PORT,
classes = ConfigChangeListenerTest.TestApplication.class,
properties = {"server.port=8081",
"spring.config.location = classpath:application-test.yml"})
public class ConfigChangeListenerTest {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private ConfigurableApplicationContext applicationContext;
@Autowired
private TestApplication.TestConfig testConfig;
@Test
public void test() throws InterruptedException {
//before change
Assert.assertEquals(1000, testConfig.getTimeout());
//submit change event
System.setProperty("timeout", "2000");
EnvironmentChangeEvent event = new EnvironmentChangeEvent(applicationContext,
Sets.newHashSet("timeout"));
applicationEventPublisher.publishEvent(event);
//after change
Assert.assertEquals(2, testConfig.getChangeCnt());
Assert.assertEquals(2000, testConfig.getTimeout());
}
@SpringBootApplication
protected static class TestApplication {
@Component
protected static class TestConfig {
@Value("${timeout:1000}")
private int timeout;
private int changeCnt;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getChangeCnt() {
return changeCnt;
}
@PolarisConfigKVFileChangeListener(interestedKeys = {"timeout"})
public void configChangedListener(ConfigChangeEvent event) {
ConfigPropertyChangeInfo changeInfo = event.getChange("timeout");
timeout = Integer.parseInt(changeInfo.getNewValue());
changeCnt++;
}
@PolarisConfigKVFileChangeListener(interestedKeyPrefixes = {"timeout"})
public void configChangedListener2(ConfigChangeEvent event) {
ConfigPropertyChangeInfo changeInfo = event.getChange("timeout");
timeout = Integer.parseInt(changeInfo.getNewValue());
changeCnt++;
}
}
@Component
protected static class EventPublisher implements ApplicationEventPublisher {
@Override
public void publishEvent(Object o) {
}
}
}
}

@ -0,0 +1,9 @@
spring:
application:
name: test
cloud:
polaris:
address: grpc://127.0.0.1:8091
namespace: default
config:
connect-remote-server: false

@ -19,12 +19,14 @@
package com.tencent.cloud.polaris.registry;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.spi.InstanceMetadataProvider;
import com.tencent.polaris.client.api.SDKContext;
import org.apache.commons.lang.StringUtils;
@ -40,6 +42,9 @@ import org.springframework.util.CollectionUtils;
*/
public class PolarisRegistration implements Registration, ServiceInstance {
private final static String METADATA_KEY_IP = "internal-ip";
private final static String METADATA_KEY_ADDRESS = "internal-address";
private final DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration;
private final PolarisDiscoveryProperties polarisDiscoveryProperties;
@ -48,15 +53,24 @@ public class PolarisRegistration implements Registration, ServiceInstance {
private final StaticMetadataManager staticMetadataManager;
private final InstanceMetadataProvider instanceMetadataProvider;
private Map<String, String> metadata;
private final String host;
public PolarisRegistration(
DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration,
PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context, StaticMetadataManager staticMetadataManager) {
PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context,
StaticMetadataManager staticMetadataManager,
InstanceMetadataProvider instanceMetadataProvider) {
this.discoveryPropertiesAutoConfiguration = discoveryPropertiesAutoConfiguration;
this.polarisDiscoveryProperties = polarisDiscoveryProperties;
this.polarisContext = context;
this.staticMetadataManager = staticMetadataManager;
this.instanceMetadataProvider = instanceMetadataProvider;
host = polarisContext.getConfig().getGlobal().getAPI().getBindIP();
}
@Override
@ -66,7 +80,7 @@ public class PolarisRegistration implements Registration, ServiceInstance {
@Override
public String getHost() {
return polarisContext.getConfig().getGlobal().getAPI().getBindIP();
return host;
}
@Override
@ -92,10 +106,35 @@ public class PolarisRegistration implements Registration, ServiceInstance {
@Override
public Map<String, String> getMetadata() {
if (CollectionUtils.isEmpty(metadata)) {
metadata = new HashMap<>();
metadata.putAll(staticMetadataManager.getMergedStaticMetadata());
Map<String, String> instanceMetadata = new HashMap<>();
// put internal metadata
instanceMetadata.put(METADATA_KEY_IP, host);
instanceMetadata.put(METADATA_KEY_ADDRESS, host + ":" + polarisDiscoveryProperties.getPort());
instanceMetadata.putAll(staticMetadataManager.getMergedStaticMetadata());
// location info will be putted both in metadata and instance's field
metadata.putAll(staticMetadataManager.getLocationMetadata());
instanceMetadata.putAll(staticMetadataManager.getLocationMetadata());
// custom metadata from spi
if (instanceMetadataProvider != null) {
if (StringUtils.isNotBlank(instanceMetadataProvider.getRegion())) {
instanceMetadata.put(StaticMetadataManager.LOCATION_KEY_ZONE, instanceMetadataProvider.getRegion());
}
if (StringUtils.isNotBlank(instanceMetadataProvider.getZone())) {
instanceMetadata.put(StaticMetadataManager.LOCATION_KEY_ZONE, instanceMetadataProvider.getZone());
}
if (StringUtils.isNotBlank(instanceMetadataProvider.getCampus())) {
instanceMetadata.put(StaticMetadataManager.LOCATION_KEY_ZONE, instanceMetadataProvider.getCampus());
}
if (!CollectionUtils.isEmpty(instanceMetadataProvider.getMetadata())) {
instanceMetadata.putAll(instanceMetadataProvider.getMetadata());
}
}
this.metadata = Collections.unmodifiableMap(instanceMetadata);
}
return metadata;
}

@ -21,6 +21,7 @@ package com.tencent.cloud.polaris.registry;
import com.tencent.cloud.common.metadata.StaticMetadataManager;
import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.cloud.polaris.context.spi.InstanceMetadataProvider;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.polaris.client.api.SDKContext;
@ -34,6 +35,7 @@ import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationC
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
/**
* Autoconfiguration of service registry of Polaris.
@ -62,9 +64,9 @@ public class PolarisServiceRegistryAutoConfiguration {
public PolarisRegistration polarisRegistration(
DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration,
PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context,
StaticMetadataManager staticMetadataManager) {
StaticMetadataManager staticMetadataManager, @Nullable InstanceMetadataProvider instanceMetadataProvider) {
return new PolarisRegistration(discoveryPropertiesAutoConfiguration,
polarisDiscoveryProperties, context, staticMetadataManager);
polarisDiscoveryProperties, context, staticMetadataManager, instanceMetadataProvider);
}
@Bean

@ -78,7 +78,7 @@ public class PolarisRegistrationTest {
doReturn(Collections.singletonMap("key2", "value2")).when(staticMetadataManager).getLocationMetadata();
polarisRegistration = new PolarisRegistration(
discoveryPropertiesAutoConfiguration, polarisDiscoveryProperties, polarisContext, staticMetadataManager);
discoveryPropertiesAutoConfiguration, polarisDiscoveryProperties, polarisContext, staticMetadataManager, null);
}
@Test
@ -111,7 +111,7 @@ public class PolarisRegistrationTest {
Map<String, String> metadata = polarisRegistration.getMetadata();
assertThat(metadata).isNotNull();
assertThat(metadata).isNotEmpty();
assertThat(metadata.size()).isEqualTo(2);
assertThat(metadata.size()).isEqualTo(4);
assertThat(metadata.get("key1")).isEqualTo("value1");
assertThat(metadata.get("key2")).isEqualTo("value2");
}

@ -52,6 +52,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-server</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>

@ -40,9 +40,9 @@ import com.tencent.cloud.common.pojo.PolarisServer;
import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils;
import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule;
import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.PolarisRuleBasedRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import com.tencent.polaris.api.pojo.Instance;
import com.tencent.polaris.api.pojo.ServiceInfo;
import com.tencent.polaris.api.pojo.ServiceInstances;

@ -0,0 +1,66 @@
/*
* 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.router.beanprocessor;
import java.util.List;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.scg.PolarisLoadBalancerClientFilter;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
/**
* Replace LoadBalancerClientFilter with PolarisLoadBalancerClientFilter.
*@author lepdou 2022-06-15
*/
public class LoadBalancerClientFilterBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory factory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof LoadBalancerClientFilter) {
// Support spring cloud gateway router.
// Replaces the default LoadBalancerClientFilter implementation and returns a custom PolarisLoadBalancerClientFilter
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
LoadBalancerProperties loadBalancerProperties = this.factory.getBean(LoadBalancerProperties.class);
List<RouterLabelResolver> routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class);
MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class);
RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class);
return new PolarisLoadBalancerClientFilter(loadBalancerClient, loadBalancerProperties,
metadataLocalProperties, routerRuleLabelResolver, routerLabelResolvers);
}
return bean;
}
}

@ -16,13 +16,14 @@
*
*/
package com.tencent.cloud.polaris.router.resttemplate;
package com.tencent.cloud.polaris.router.beanprocessor;
import java.util.List;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.springframework.beans.BeansException;
@ -36,10 +37,9 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
/**
* Replace LoadBalancerInterceptor with PolarisLoadBalancerInterceptor.
* PolarisLoadBalancerInterceptor can pass routing context information.
*
*@author lepdou 2022-05-18
*/
public class PolarisLoadBalancerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory factory;
@ -51,6 +51,8 @@ public class PolarisLoadBalancerBeanPostProcessor implements BeanPostProcessor,
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof LoadBalancerInterceptor) {
// Support rest template router.
// Replaces the default LoadBalancerInterceptor implementation and returns a custom PolarisLoadBalancerInterceptor
LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class);
LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class);
List<RouterLabelResolver> routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class);

@ -0,0 +1,58 @@
/*
* 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.router.config;
import java.util.List;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory;
import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
/**
* configuration for feign singleton components.
* Feign-related components need to be loaded only in the feign environment.
*@author lepdou 2022-06-10
*/
@Configuration
@ConditionalOnClass(name = {"org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer"})
@RibbonClients(defaultConfiguration = {FeignLoadBalancerConfiguration.class})
public class FeignAutoConfiguration {
@Bean
public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List<RouterLabelResolver> routerLabelResolvers,
MetadataLocalProperties metadataLocalProperties,
RouterRuleLabelResolver routerRuleLabelResolver) {
return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
}
@Bean
public PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) {
return new PolarisCachingSpringLoadBalanceFactory(factory);
}
}

@ -25,15 +25,12 @@ import com.tencent.cloud.polaris.router.feign.PolarisFeignLoadBalancer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* configuration for feign component.
*
* configuration for feign load balance components. PolarisFeignLoadBalancer is not singleton bean, Each service corresponds to a PolarisFeignLoadBalancer.
*@author lepdou 2022-05-16
*/
@Configuration
public class FeignConfiguration {
public class FeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean

@ -22,16 +22,17 @@ import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IRule;
import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
import com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import com.tencent.polaris.router.api.core.RouterAPI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Auto configuration for ribbon components.
* Configuration for ribbon components. IRule is not singleton bean, Each service corresponds to an IRule.
* @author lepdou 2022-05-17
*/
@Configuration
public class RibbonConfiguration {
@Bean

@ -18,52 +18,47 @@
package com.tencent.cloud.polaris.router.config;
import java.util.List;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory;
import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor;
import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerBeanPostProcessor;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerClientFilterBeanPostProcessor;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
/**
* router module auto configuration.
* configuration for router module singleton beans.
*
*@author lepdou 2022-05-11
*/
@Configuration
@RibbonClients(defaultConfiguration = {FeignConfiguration.class, RibbonConfiguration.class})
@RibbonClients(defaultConfiguration = {RibbonConfiguration.class})
@Import({PolarisNearByRouterProperties.class, PolarisMetadataRouterProperties.class, PolarisRuleBasedRouterProperties.class})
public class RouterAutoConfiguration {
@Bean
public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List<RouterLabelResolver> routerLabelResolvers,
MetadataLocalProperties metadataLocalProperties,
RouterRuleLabelResolver routerRuleLabelResolver) {
return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver);
}
@Bean
public PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) {
return new PolarisCachingSpringLoadBalanceFactory(factory);
@Order(HIGHEST_PRECEDENCE)
@ConditionalOnClass(LoadBalancerInterceptor.class)
public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() {
return new LoadBalancerInterceptorBeanPostProcessor();
}
@Bean
@Order(HIGHEST_PRECEDENCE)
public PolarisLoadBalancerBeanPostProcessor polarisLoadBalancerBeanPostProcessor() {
return new PolarisLoadBalancerBeanPostProcessor();
@ConditionalOnClass(LoadBalancerClientFilter.class)
public LoadBalancerClientFilterBeanPostProcessor loadBalancerClientFilterBeanPostProcessor() {
return new LoadBalancerClientFilterBeanPostProcessor();
}
@Bean

@ -16,7 +16,7 @@
*
*/
package com.tencent.cloud.polaris.router.config;
package com.tencent.cloud.polaris.router.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ -16,7 +16,7 @@
*
*/
package com.tencent.cloud.polaris.router.config;
package com.tencent.cloud.polaris.router.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ -16,7 +16,7 @@
*
*/
package com.tencent.cloud.polaris.router.config;
package com.tencent.cloud.polaris.router.config.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ -0,0 +1,144 @@
/*
* 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.router.scg;
import java.net.URI;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ExpressionLabelUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
/**
* Replaces the default LoadBalancerClientFilter implementation.
*@author lepdou 2022-06-10
*/
public class PolarisLoadBalancerClientFilter extends LoadBalancerClientFilter {
private final static Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerClientFilter.class);
private final MetadataLocalProperties metadataLocalProperties;
private final RouterRuleLabelResolver routerRuleLabelResolver;
private final List<RouterLabelResolver> routerLabelResolvers;
private final boolean isRibbonLoadBalanceClient;
public PolarisLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties,
MetadataLocalProperties metadataLocalProperties,
RouterRuleLabelResolver routerRuleLabelResolver,
List<RouterLabelResolver> routerLabelResolvers) {
super(loadBalancer, properties);
this.metadataLocalProperties = metadataLocalProperties;
this.routerRuleLabelResolver = routerRuleLabelResolver;
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder));
this.routerLabelResolvers = routerLabelResolvers;
}
else {
this.routerLabelResolvers = null;
}
this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient;
}
@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
String peerServiceName = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
if (isRibbonLoadBalanceClient) {
// Pass routing context to ribbon load balancer
PolarisRouterContext routerContext = genRouterContext(exchange, peerServiceName);
return ((RibbonLoadBalancerClient) loadBalancer).choose(peerServiceName, routerContext);
}
else {
return loadBalancer.choose(peerServiceName);
}
}
PolarisRouterContext genRouterContext(ServerWebExchange exchange, String peerServiceName) {
// local service labels
Map<String, String> labels = new HashMap<>(metadataLocalProperties.getContent());
// labels from rule expression
Map<String, String> ruleExpressionLabels = getExpressionLabels(exchange, peerServiceName);
if (!CollectionUtils.isEmpty(ruleExpressionLabels)) {
labels.putAll(ruleExpressionLabels);
}
// labels from request
if (!CollectionUtils.isEmpty(routerLabelResolvers)) {
routerLabelResolvers.forEach(resolver -> {
try {
Map<String, String> customResolvedLabels = resolver.resolve(exchange);
if (!CollectionUtils.isEmpty(customResolvedLabels)) {
labels.putAll(customResolvedLabels);
}
}
catch (Throwable t) {
LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t);
}
});
}
// labels from downstream
Map<String, String> transitiveLabels = MetadataContextHolder.get()
.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE);
labels.putAll(transitiveLabels);
PolarisRouterContext routerContext = new PolarisRouterContext();
routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels);
routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels);
return routerContext;
}
private Map<String, String> getExpressionLabels(ServerWebExchange exchange, String peerServiceName) {
Set<String> labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE,
MetadataContext.LOCAL_SERVICE, peerServiceName);
if (CollectionUtils.isEmpty(labelKeys)) {
return Collections.emptyMap();
}
return ExpressionLabelUtils.resolve(exchange, labelKeys);
}
}

@ -24,6 +24,7 @@ import feign.RequestTemplate;
import org.springframework.core.Ordered;
import org.springframework.http.HttpRequest;
import org.springframework.web.server.ServerWebExchange;
/**
* The spi for resolving labels from request.
@ -46,4 +47,12 @@ public interface RouterLabelResolver extends Ordered {
* @return resolved labels
*/
Map<String, String> resolve(HttpRequest request, byte[] body);
/**
* resolve labels from server web exchange.
* @param exchange the server web exchange.
* @return resolved labels
*/
Map<String, String> resolve(ServerWebExchange exchange);
}

@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.router.config.RouterAutoConfiguration
com.tencent.cloud.polaris.router.config.RouterAutoConfiguration,\
com.tencent.cloud.polaris.router.config.FeignAutoConfiguration

@ -41,9 +41,9 @@ import com.tencent.cloud.common.pojo.PolarisServer;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule;
import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties;
import com.tencent.cloud.polaris.router.config.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.PolarisRuleBasedRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties;
import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties;
import com.tencent.polaris.api.pojo.DefaultInstance;
import com.tencent.polaris.api.pojo.DefaultServiceInstances;
import com.tencent.polaris.api.pojo.Instance;

@ -21,6 +21,7 @@ package com.tencent.cloud.polaris.router.resttemplate;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.BeanFactoryUtils;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.junit.Assert;
import org.junit.Test;
@ -38,7 +39,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory;
import static org.mockito.Mockito.when;
/**
* Test for ${@link PolarisLoadBalancerBeanPostProcessor}
* Test for ${@link LoadBalancerInterceptorBeanPostProcessor}
* @author lepdou 2022-05-26
*/
@RunWith(MockitoJUnitRunner.class)
@ -67,7 +68,7 @@ public class PolarisLoadBalancerBeanPostProcessorTest {
.thenReturn(null);
LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(loadBalancerClient, loadBalancerRequestFactory);
PolarisLoadBalancerBeanPostProcessor processor = new PolarisLoadBalancerBeanPostProcessor();
LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor();
processor.setBeanFactory(beanFactory);
Object bean = processor.postProcessBeforeInitialization(loadBalancerInterceptor, "");
@ -78,7 +79,7 @@ public class PolarisLoadBalancerBeanPostProcessorTest {
@Test
public void testNotWrapperLoadBalancerInterceptor() {
PolarisLoadBalancerBeanPostProcessor processor = new PolarisLoadBalancerBeanPostProcessor();
LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor();
processor.setBeanFactory(beanFactory);
OtherBean otherBean = new OtherBean();

@ -0,0 +1,192 @@
/*
* 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.router.scg;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.metadata.MetadataContextHolder;
import com.tencent.cloud.common.metadata.config.MetadataLocalProperties;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.router.PolarisRouterContext;
import com.tencent.cloud.polaris.router.RouterRuleLabelResolver;
import com.tencent.cloud.polaris.router.spi.RouterLabelResolver;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
/**
* test for ${@link PolarisLoadBalancerClientFilter}
*@author lepdou 2022-06-13
*/
@RunWith(MockitoJUnitRunner.class)
public class PolarisLoadBalancerClientFilterTest {
@Mock
private MetadataLocalProperties metadataLocalProperties;
@Mock
private RouterRuleLabelResolver routerRuleLabelResolver;
@Mock
private RouterLabelResolver routerLabelResolver;
@Mock
private LoadBalancerClient loadBalancerClient;
@Mock
private LoadBalancerProperties loadBalancerProperties;
private static final String callerService = "callerService";
private static final String calleeService = "calleeService";
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
private static MockedStatic<MetadataContextHolder> mockedMetadataContextHolder;
@BeforeClass
public static void beforeClass() {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn(callerService);
MetadataContext metadataContext = Mockito.mock(MetadataContext.class);
// mock transitive metadata
Map<String, String> transitiveLabels = new HashMap<>();
transitiveLabels.put("t1", "v1");
transitiveLabels.put("t2", "v2");
when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels);
mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class);
mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext);
}
@AfterClass
public static void afterClass() {
mockedApplicationContextAwareUtils.close();
mockedMetadataContextHolder.close();
}
@Test
public void testGenRouterContext() {
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
Lists.newArrayList(routerLabelResolver));
Map<String, String> localMetadata = new HashMap<>();
localMetadata.put("env", "blue");
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
Set<String> expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}");
when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys);
MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users")
.header("k1", "v1")
.queryParam("userid", "zhangsan")
.build();
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
Map<String, String> customMetadata = new HashMap<>();
customMetadata.put("k2", "v2");
when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata);
PolarisRouterContext routerContext = polarisLoadBalancerClientFilter.genRouterContext(webExchange, calleeService);
Map<String, String> routerLabels = routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS);
Assert.assertEquals("v1", routerLabels.get("${http.header.k1}"));
Assert.assertEquals("zhangsan", routerLabels.get("${http.query.userid}"));
Assert.assertEquals("blue", routerLabels.get("env"));
Assert.assertEquals("v1", routerLabels.get("t1"));
Assert.assertEquals("v2", routerLabels.get("t2"));
}
@Test
public void testChooseInstanceWithoutRibbon() {
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
Lists.newArrayList(routerLabelResolver));
String url = "/" + calleeService + "/users";
MockServerHttpRequest request = MockServerHttpRequest.get(url)
.header("k1", "v1")
.queryParam("userid", "zhangsan")
.build();
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users"));
polarisLoadBalancerClientFilter.choose(webExchange);
verify(loadBalancerClient).choose(calleeService);
verify(metadataLocalProperties, times(0)).getContent();
}
@Test
public void testChooseInstanceWithRibbon() {
RibbonLoadBalancerClient ribbonLoadBalancerClient = Mockito.mock(RibbonLoadBalancerClient.class);
PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter(
ribbonLoadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver,
Lists.newArrayList(routerLabelResolver));
Map<String, String> localMetadata = new HashMap<>();
localMetadata.put("env", "blue");
when(metadataLocalProperties.getContent()).thenReturn(localMetadata);
Set<String> expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}");
when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys);
String url = "/" + calleeService + "/users";
MockServerHttpRequest request = MockServerHttpRequest.get(url)
.header("k1", "v1")
.queryParam("userid", "zhangsan")
.build();
MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build();
webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users"));
Map<String, String> customMetadata = new HashMap<>();
customMetadata.put("k2", "v2");
when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata);
polarisLoadBalancerClientFilter.choose(webExchange);
verify(ribbonLoadBalancerClient).choose(anyString(), any());
verify(metadataLocalProperties, times(1)).getContent();
}
}

@ -43,9 +43,18 @@ public class StaticMetadataManager {
private static final String ENV_METADATA_REGION = "SCT_METADATA_REGION";
private static final String ENV_METADATA_CAMPUS = "SCT_METADATA_CAMPUS";
private static final String LOCATION_KEY_REGION = "region";
private static final String LOCATION_KEY_ZONE = "zone";
private static final String LOCATION_KEY_CAMPUS = "campus";
/**
* the metadata key of region.
*/
public static final String LOCATION_KEY_REGION = "region";
/**
* the metadata key of zone.
*/
public static final String LOCATION_KEY_ZONE = "zone";
/**
* the metadata key of campus/datacenter.
*/
public static final String LOCATION_KEY_CAMPUS = "campus";
private Map<String, String> envMetadata;
private Map<String, String> envTransitiveMetadata;

@ -24,6 +24,16 @@
<artifactId>spring-cloud-tencent-commons</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
@ -49,10 +59,10 @@
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-config</artifactId>
</dependency>
</dependencies>
<build>
@ -75,4 +85,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

@ -78,6 +78,7 @@
<jackson.version>2.12.7</jackson.version>
<protobuf-java.version>3.16.1</protobuf-java.version>
<bcprov-jdk15on.version>1.69</bcprov-jdk15on.version>
<guava.version>31.0.1-jre</guava.version>
<!-- Maven Plugin Versions -->
<maven-source-plugin.version>3.2.0</maven-source-plugin.version>
@ -150,6 +151,27 @@
<version>${revision}</version>
</dependency>
<!-- third part framework dependencies -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
<exclusions>
<exclusion>
<artifactId>jsr305</artifactId>
<groupId>com.google.code.findbugs</groupId>
</exclusion>
<exclusion>
<artifactId>animal-sniffer-annotations</artifactId>
<groupId>org.codehaus.mojo</groupId>
</exclusion>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>

@ -0,0 +1,56 @@
/*
* 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.discovery.service.callee;
import java.util.HashMap;
import java.util.Map;
import com.tencent.cloud.polaris.context.spi.InstanceMetadataProvider;
import org.springframework.stereotype.Component;
/**
* custom metadata for instance.
*@author lepdou 2022-06-16
*/
@Component
public class CustomMetadata implements InstanceMetadataProvider {
@Override
public Map<String, String> getMetadata() {
Map<String, String> metadata = new HashMap<>();
metadata.put("k1", "v1");
return metadata;
}
@Override
public String getRegion() {
return "shanghai";
}
@Override
public String getZone() {
return null;
}
@Override
public String getCampus() {
return null;
}
}

@ -5,6 +5,10 @@ spring:
application:
name: GatewayCalleeService
cloud:
tencent:
metadata:
content:
env: blue
polaris:
address: grpc://183.47.111.80:8091
namespace: default

@ -0,0 +1,26 @@
<?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"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>polaris-gateway-example</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-callee-service2</artifactId>
<dependencies>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

@ -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.gateway.example.callee;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Gateway callee application.
*
* @author Haotian Zhang
*/
@SpringBootApplication
public class GatewayCalleeApplication2 {
public static void main(String[] args) {
SpringApplication.run(GatewayCalleeApplication2.class, args);
}
}

@ -0,0 +1,71 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.gateway.example.callee;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import com.tencent.cloud.common.constant.MetadataConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Gateway callee controller.
*
* @author Haotian Zhang
*/
@RestController
@RequestMapping("/gateway/example/callee")
public class GatewayCalleeController {
private static Logger LOG = LoggerFactory.getLogger(GatewayCalleeController.class);
@Value("${server.port:0}")
private int port;
/**
* Get information of callee.
* @return information of callee
*/
@RequestMapping("/info")
public String info() {
LOG.info("Gateway Example Callee [{}] is called.", port);
return String.format("Gateway Example Callee [%s] is called.", port);
}
/**
* Get metadata in HTTP header.
*
* @param metadataStr metadata string
* @return metadata in HTTP header
* @throws UnsupportedEncodingException encoding exception
*/
@RequestMapping("/echo")
public String echoHeader(
@RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr)
throws UnsupportedEncodingException {
LOG.info(URLDecoder.decode(metadataStr, "UTF-8"));
return URLDecoder.decode(metadataStr, "UTF-8");
}
}

@ -0,0 +1,14 @@
server:
session-timeout: 1800
port: 48082
spring:
application:
name: GatewayCalleeService
cloud:
tencent:
metadata:
content:
env: green
polaris:
address: grpc://183.47.111.80:8091
namespace: default

@ -26,7 +26,7 @@
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
</dependency>
<dependency>
@ -34,4 +34,4 @@
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
</project>

@ -8,15 +8,13 @@ spring:
tencent:
metadata:
content:
a: 1
env: blue
transitive:
- a
- env
polaris:
address: grpc://183.47.111.80:8091
namespace: default
enabled: true
discovery:
service-list-refresh-interval: 1000
gateway:
discovery:
locator:
@ -52,13 +50,13 @@ spring:
maxBackoff: '''500ms'''
factor: 2
basedOnPreviousValue: false
# routes:
# - id: GatewayCalleeService
# uri: lb://GatewayCalleeService
# predicates:
# - Path=/GatewayCalleeService/**
# filters:
# - StripPrefix=1
routes:
- id: GatewayCalleeService
uri: lb://GatewayCalleeService
predicates:
- Path=/GatewayCalleeService/**
filters:
- StripPrefix=1
logging:
level:

@ -18,6 +18,7 @@
<module>gateway-zuul-service</module>
<module>gateway-scg-service</module>
<module>gateway-callee-service</module>
<module>gateway-callee-service2</module>
</modules>
<build>
@ -35,4 +36,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

@ -27,6 +27,7 @@ import feign.RequestTemplate;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
/**
*
@ -58,6 +59,11 @@ public class CustomRouterLabelResolver implements RouterLabelResolver {
return labels;
}
@Override
public Map<String, String> resolve(ServerWebExchange exchange) {
return null;
}
@Override
public int getOrder() {
return 0;

@ -0,0 +1,55 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
*/
package com.tencent.cloud.polaris.context.spi;
import java.util.Map;
/**
*
* Instance's custom metadata, metadata will be register to polaris server.
* @author lepdou 2022-06-16
*/
public interface InstanceMetadataProvider {
/**
* @return the metadata of instance.
*/
Map<String, String> getMetadata();
/**
* The region of current instance.
*
* @return the region info.
*/
String getRegion();
/**
* The zone of current instance.
*
* @return the zone info.
*/
String getZone();
/**
* The campus/datacenter of current instance.
*
* @return the campus or datacenter info.
*/
String getCampus();
}
Loading…
Cancel
Save