feat:enhance Feign and RestTemplate and support Polaris monitor. (#447)

pull/454/head
Haotian Zhang 2 years ago committed by GitHub
parent 719235ec8b
commit 30644858ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,3 +5,4 @@
- [Optimize: add EncodeTransferMedataRestTemplateInterceptor to RestTemplate](https://github.com/Tencent/spring-cloud-tencent/pull/440) - [Optimize: add EncodeTransferMedataRestTemplateInterceptor to RestTemplate](https://github.com/Tencent/spring-cloud-tencent/pull/440)
- [Feature: add rate limit filter debug log](https://github.com/Tencent/spring-cloud-tencent/pull/437) - [Feature: add rate limit filter debug log](https://github.com/Tencent/spring-cloud-tencent/pull/437)
- [Add configurable heartbeat interval support](https://github.com/Tencent/spring-cloud-tencent/pull/444) - [Add configurable heartbeat interval support](https://github.com/Tencent/spring-cloud-tencent/pull/444)
- [feat:enhance Feign and RestTemplate and support Polaris monitor.](https://github.com/Tencent/spring-cloud-tencent/pull/447)

@ -54,6 +54,10 @@ Tencent可直接一键运行任何 example。
Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要引入依赖即可。 Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要引入依赖即可。
> 注意:
>
> Spring Cloud Tencent 的版本列表可以查看 [Spring Cloud Tencent 版本管理](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86) 。
例如: 例如:
```` xml ```` xml

@ -56,6 +56,10 @@ directly with one click.
All the components of Spring Cloud Tencent have been uploaded to the Maven central repository, just need to introduce All the components of Spring Cloud Tencent have been uploaded to the Maven central repository, just need to introduce
dependencies. dependencies.
> Notice:
>
> The version list of Spring Cloud Tencent can be found in [Spring Cloud Tencent Version Management](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86).
For example: For example:
```` xml ```` xml

@ -40,6 +40,7 @@
<modules> <modules>
<module>spring-cloud-tencent-commons</module> <module>spring-cloud-tencent-commons</module>
<module>spring-cloud-tencent-polaris-context</module> <module>spring-cloud-tencent-polaris-context</module>
<module>spring-cloud-tencent-rpc-enhancement</module>
<module>spring-cloud-tencent-polaris-loadbalancer</module> <module>spring-cloud-tencent-polaris-loadbalancer</module>
<module>spring-cloud-starter-tencent-metadata-transfer</module> <module>spring-cloud-starter-tencent-metadata-transfer</module>
<module>spring-cloud-starter-tencent-polaris-config</module> <module>spring-cloud-starter-tencent-polaris-config</module>

@ -18,36 +18,13 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency> </dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>polaris-discovery-factory</artifactId> <artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
<exclusions>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-canary</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-set</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>polaris-circuitbreaker-factory</artifactId> <artifactId>polaris-circuitbreaker-factory</artifactId>
@ -84,28 +61,10 @@
</dependency> </dependency>
<!-- Polaris dependencies end --> <!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

@ -17,84 +17,53 @@
package com.tencent.cloud.polaris.circuitbreaker.config; package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor; import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisResponseErrorHandler; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier; import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler; import com.tencent.polaris.api.config.consumer.ServiceRouterConfig;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.client.RestTemplate;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
/** /**
* Auto Configuration for Polaris {@link feign.Feign} OR {@link RestTemplate} which can automatically bring in the call * Autoconfiguration at bootstrap phase.
* results for reporting.
* *
* @author <a href="mailto:iskp.me@gmail.com">Palmer.Xu</a> 2022-06-29 * @author lepdou 2022-03-29
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
public class PolarisCircuitBreakerAutoConfiguration { public class PolarisCircuitBreakerAutoConfiguration {
/**
* Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting.
*
* @author Haotian Zhang
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
protected static class PolarisFeignClientAutoConfiguration {
@Bean @Bean
public ConsumerAPI consumerAPI(SDKContext context) { public CircuitBreakerConfigModifier circuitBreakerConfigModifier() {
return DiscoveryAPIFactory.createConsumerAPIByContext(context); return new CircuitBreakerConfigModifier();
} }
@Bean public static class CircuitBreakerConfigModifier implements PolarisConfigModifier {
@Order(HIGHEST_PRECEDENCE)
public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
return new PolarisFeignBeanPostProcessor(consumerAPI);
}
}
/** @Override
* Configuration for Polaris {@link RestTemplate} which can automatically bring in the call public void modify(ConfigurationImpl configuration) {
* results for reporting. // Turn on circuitbreaker configuration
* configuration.getConsumer().getCircuitBreaker().setEnable(true);
* @author wh 2022/6/21
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
protected static class PolarisRestTemplateAutoConfiguration {
@Bean // Set excludeCircuitBreakInstances to false
public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler( RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter()
ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) { .getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class);
return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler);
recoverRouterConfig.setExcludeCircuitBreakInstances(true);
// Update modified config to source properties
configuration.getConsumer().getServiceRouter()
.setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig);
} }
@Bean @Override
public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor( public int getOrder() {
PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) { return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER;
return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler);
} }
} }
} }

@ -17,53 +17,18 @@
package com.tencent.cloud.polaris.circuitbreaker.config; package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.polaris.api.config.consumer.ServiceRouterConfig;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/** /**
* Auto configuration at bootstrap phase. * Autoconfiguration at bootstrap phase.
* *
* @author lepdou 2022-03-29 * @author lepdou 2022-03-29
*/ */
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled @ConditionalOnProperty("spring.cloud.polaris.enabled")
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true) @Import(PolarisCircuitBreakerAutoConfiguration.class)
public class PolarisCircuitBreakerBootstrapConfiguration { public class PolarisCircuitBreakerBootstrapConfiguration {
@Bean
public CircuitBreakerConfigModifier circuitBreakerConfigModifier() {
return new CircuitBreakerConfigModifier();
}
public static class CircuitBreakerConfigModifier implements PolarisConfigModifier {
@Override
public void modify(ConfigurationImpl configuration) {
// Turn on circuitbreaker configuration
configuration.getConsumer().getCircuitBreaker().setEnable(true);
// Set excludeCircuitBreakInstances to false
RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter()
.getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class);
recoverRouterConfig.setExcludeCircuitBreakInstances(true);
// Update modified config to source properties
configuration.getConsumer().getServiceRouter()
.setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig);
}
@Override
public int getOrder() {
return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER;
}
}
} }

@ -1,4 +1,4 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration

@ -15,12 +15,8 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.polaris.api.core.ConsumerAPI;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -33,20 +29,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignClientAutoConfigurationTest { public class PolarisCircuitBreakerAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(PolarisCircuitBreakerAutoConfiguration.class))
AutoConfigurations.of(
PolarisContextAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test @Test
public void testDefaultInitialization() { public void testDefaultInitialization() {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(ConsumerAPI.class); assertThat(context).hasSingleBean(
assertThat(context).hasSingleBean(PolarisFeignBeanPostProcessor.class); PolarisCircuitBreakerAutoConfiguration.CircuitBreakerConfigModifier.class);
}); });
} }
} }

@ -15,9 +15,8 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -32,14 +31,15 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class PolarisCircuitBreakerBootstrapConfigurationTest { public class PolarisCircuitBreakerBootstrapConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class))
AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class)) .withPropertyValues("spring.cloud.polaris.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test @Test
public void testDefaultInitialization() { public void testDefaultInitialization() {
this.contextRunner.run(context -> { this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(PolarisCircuitBreakerBootstrapConfiguration.CircuitBreakerConfigModifier.class); assertThat(context).hasSingleBean(
PolarisCircuitBreakerAutoConfiguration.CircuitBreakerConfigModifier.class);
}); });
} }
} }

@ -1,15 +0,0 @@
spring:
cloud:
polaris:
address: grpc://127.0.0.1:8091
feign:
polaris:
enable: true
compression:
request:
enabled: false
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: false

@ -19,44 +19,13 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency> </dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>polaris-discovery-factory</artifactId> <artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
<exclusions>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-canary</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-set</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-isolated</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-healthy</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId> <artifactId>polaris-test-common</artifactId>

@ -20,11 +20,6 @@ package com.tencent.cloud.polaris;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
import com.tencent.cloud.polaris.extend.consul.ConsulContextProperties; import com.tencent.cloud.polaris.extend.consul.ConsulContextProperties;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -41,18 +36,6 @@ import org.springframework.context.annotation.Import;
@Import({PolarisDiscoveryProperties.class, ConsulContextProperties.class}) @Import({PolarisDiscoveryProperties.class, ConsulContextProperties.class})
public class DiscoveryPropertiesAutoConfiguration { public class DiscoveryPropertiesAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ProviderAPI polarisProvider(SDKContext polarisContext) throws PolarisException {
return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext);
}
@Bean
@ConditionalOnMissingBean
public ConsumerAPI polarisConsumer(SDKContext polarisContext) throws PolarisException {
return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext);
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public PolarisDiscoveryHandler polarisDiscoveryHandler() { public PolarisDiscoveryHandler polarisDiscoveryHandler() {

@ -19,6 +19,10 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId> <artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>

@ -73,5 +73,10 @@ public final class ContextConstant {
* Order of configuration modifier. * Order of configuration modifier.
*/ */
public static Integer CONFIG_ORDER = 1; public static Integer CONFIG_ORDER = 1;
/**
* Order of stat reporter configuration modifier.
*/
public static Integer STAT_REPORTER_ORDER = 1;
} }
} }

@ -29,6 +29,11 @@
<artifactId>spring-cloud-tencent-polaris-context</artifactId> <artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>

@ -73,7 +73,7 @@
<revision>1.7.0-2021.0.3-SNAPSHOT</revision> <revision>1.7.0-2021.0.3-SNAPSHOT</revision>
<!-- Dependencies --> <!-- Dependencies -->
<polaris.version>1.7.0</polaris.version> <polaris.version>1.7.1</polaris.version>
<guava.version>31.0.1-jre</guava.version> <guava.version>31.0.1-jre</guava.version>
<logback.version>1.2.11</logback.version> <logback.version>1.2.11</logback.version>
<mocktio.version>4.5.1</mocktio.version> <mocktio.version>4.5.1</mocktio.version>
@ -109,6 +109,12 @@
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
<version>${revision}</version>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>

@ -10,6 +10,9 @@ spring:
enabled: true enabled: true
circuitbreaker: circuitbreaker:
enabled: true enabled: true
stat:
enabled: true
port: 28081
loadbalancer: loadbalancer:
configurations: polaris configurations: polaris
feign: feign:

@ -8,3 +8,6 @@ spring:
address: grpc://183.47.111.80:8091 address: grpc://183.47.111.80:8091
namespace: default namespace: default
enabled: true enabled: true
stat:
enabled: true
port: 28082

@ -8,3 +8,6 @@ spring:
address: grpc://183.47.111.80:8091 address: grpc://183.47.111.80:8091
namespace: default namespace: default
enabled: true enabled: true
stat:
enabled: true
port: 28083

@ -11,6 +11,9 @@ spring:
discovery: discovery:
enabled: true enabled: true
register: true register: true
stat:
enabled: true
port: 28082
tencent: tencent:
metadata: metadata:
content: content:

@ -19,6 +19,9 @@ spring:
heartbeat: heartbeat:
enabled: true enabled: true
health-check-url: /discovery/service/caller/healthCheck health-check-url: /discovery/service/caller/healthCheck
stat:
enabled: true
port: 28081
# consul: # consul:
# port: 8500 # port: 8500
# host: 127.0.0.1 # host: 127.0.0.1

@ -27,6 +27,33 @@
</dependency> </dependency>
<!-- Polaris dependencies start --> <!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-discovery-factory</artifactId>
<exclusions>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-canary</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-set</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>polaris-client</artifactId> <artifactId>polaris-client</artifactId>
@ -62,17 +89,6 @@
<artifactId>flow-cache-expired</artifactId> <artifactId>flow-cache-expired</artifactId>
</dependency> </dependency>
<!-- Discovery depended on base routers -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-isolated</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-healthy</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>loadbalancer-random</artifactId> <artifactId>loadbalancer-random</artifactId>

@ -24,8 +24,11 @@ import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.ModifyAddress; import com.tencent.cloud.polaris.context.ModifyAddress;
import com.tencent.cloud.polaris.context.PolarisConfigModifier; import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -48,6 +51,18 @@ public class PolarisContextAutoConfiguration {
return SDKContext.initContextByConfig(properties.configuration(environment, modifierList)); return SDKContext.initContextByConfig(properties.configuration(environment, modifierList));
} }
@Bean
@ConditionalOnMissingBean
public ProviderAPI polarisProvider(SDKContext polarisContext) throws PolarisException {
return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext);
}
@Bean
@ConditionalOnMissingBean
public ConsumerAPI polarisConsumer(SDKContext polarisContext) throws PolarisException {
return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext);
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ModifyAddress polarisConfigModifier() { public ModifyAddress polarisConfigModifier() {

@ -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.context.logging;
import com.tencent.polaris.logging.PolarisLogging;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
/**
* Reload of Polaris logging configuration.
*
* @author Haotian Zhang
*/
public class PolarisLoggingApplicationListener implements GenericApplicationListener {
private static final int ORDER = LoggingApplicationListener.DEFAULT_ORDER + 2;
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type == null) {
return false;
}
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type)
|| ApplicationFailedEvent.class.isAssignableFrom(type);
}
@Override
public int getOrder() {
return ORDER;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
PolarisLogging.getInstance().loadConfiguration();
}
}

@ -3,3 +3,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextPostConfiguration com.tencent.cloud.polaris.context.config.PolarisContextPostConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration
org.springframework.context.ApplicationListener=\
com.tencent.cloud.polaris.context.logging.PolarisLoggingApplicationListener

@ -0,0 +1,62 @@
<?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">
<parent>
<artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
<name>Spring Cloud Starter Tencent RPC Enhancement</name>
<dependencies>
<!-- Spring Cloud Tencent dependencies start -->
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>stat-prometheus</artifactId>
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<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>
</dependencies>
</project>

@ -0,0 +1,110 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.config;
import java.util.List;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignBeanPostProcessor;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateModifier;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.PolarisResponseErrorHandler;
import com.tencent.polaris.api.core.ConsumerAPI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.client.RestTemplate;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
/**
* Auto Configuration for Polaris {@link feign.Feign} OR {@link RestTemplate} which can automatically bring in the call
* results for reporting.
*
* @author <a href="mailto:iskp.me@gmail.com">Palmer.Xu</a> 2022-06-29
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = "spring.cloud.tencent.rpc-enhancement.enabled", havingValue = "true", matchIfMissing = true)
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
public class RpcEnhancementAutoConfiguration {
/**
* Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting.
*
* @author Haotian Zhang
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
@AutoConfigureBefore(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
protected static class PolarisFeignClientAutoConfiguration {
@Bean
@Order(HIGHEST_PRECEDENCE)
public EnhancedFeignBeanPostProcessor polarisFeignBeanPostProcessor(
@Autowired(required = false) List<EnhancedFeignPlugin> enhancedFeignPlugins) {
return new EnhancedFeignBeanPostProcessor(enhancedFeignPlugins);
}
@Configuration
static class PolarisReporterConfig {
@Bean
public SuccessPolarisReporter successPolarisReporter() {
return new SuccessPolarisReporter();
}
@Bean
public ExceptionPolarisReporter exceptionPolarisReporter() {
return new ExceptionPolarisReporter();
}
}
}
/**
* Configuration for Polaris {@link RestTemplate} which can automatically bring in the call
* results for reporting.
*
* @author wh 2022/6/21
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
protected static class PolarisRestTemplateAutoConfiguration {
@Bean
public EnhancedRestTemplateReporter polarisRestTemplateResponseErrorHandler(
ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) {
return new EnhancedRestTemplateReporter(consumerAPI, polarisResponseErrorHandler);
}
@Bean
public EnhancedRestTemplateModifier polarisRestTemplateBeanPostProcessor(
EnhancedRestTemplateReporter enhancedRestTemplateReporter) {
return new EnhancedRestTemplateModifier(enhancedRestTemplateReporter);
}
}
}

@ -15,9 +15,11 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import com.tencent.polaris.api.core.ConsumerAPI; import java.util.List;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import feign.Client; import feign.Client;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -34,14 +36,14 @@ import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLo
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { public class EnhancedFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final ConsumerAPI consumerAPI; private final List<EnhancedFeignPlugin> enhancedFeignPlugins;
private BeanFactory factory; private BeanFactory factory;
public PolarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) { public EnhancedFeignBeanPostProcessor(List<EnhancedFeignPlugin> enhancedFeignPlugins) {
this.consumerAPI = consumerAPI; this.enhancedFeignPlugins = enhancedFeignPlugins;
} }
@Override @Override
@ -61,7 +63,7 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
delegate = ((FeignBlockingLoadBalancerClient) bean).getDelegate(); delegate = ((FeignBlockingLoadBalancerClient) bean).getDelegate();
} }
if (delegate != null) { if (delegate != null) {
return new PolarisFeignBlockingLoadBalancerClient(createPolarisFeignClient(delegate), return new EnhancedFeignBlockingLoadBalancerClient(createPolarisFeignClient(delegate),
factory.getBean(BlockingLoadBalancerClient.class), factory.getBean(BlockingLoadBalancerClient.class),
factory.getBean(LoadBalancerClientFactory.class)); factory.getBean(LoadBalancerClientFactory.class));
} }
@ -72,12 +74,12 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
} }
private boolean isNeedWrap(Object bean) { private boolean isNeedWrap(Object bean) {
return bean instanceof Client && !(bean instanceof PolarisFeignClient) return bean instanceof Client && !(bean instanceof EnhancedFeignClient)
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient); && !(bean instanceof EnhancedFeignBlockingLoadBalancerClient);
} }
private PolarisFeignClient createPolarisFeignClient(Client delegate) { private EnhancedFeignClient createPolarisFeignClient(Client delegate) {
return new PolarisFeignClient(delegate, consumerAPI); return new EnhancedFeignClient(delegate, enhancedFeignPlugins);
} }
@Override @Override

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import feign.Client; import feign.Client;
@ -28,9 +28,9 @@ import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalance
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient { public class EnhancedFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
public PolarisFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient, public EnhancedFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
LoadBalancerClientFactory loadBalancerClientFactory) { LoadBalancerClientFactory loadBalancerClientFactory) {
super(delegate, loadBalancerClient, loadBalancerClientFactory); super(delegate, loadBalancerClient, loadBalancerClientFactory);
} }

@ -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.rpc.enhancement.feign;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import feign.Client;
import feign.Request;
import feign.Request.Options;
import feign.Response;
import org.springframework.util.CollectionUtils;
import static feign.Util.checkNotNull;
/**
* Wrap for {@link Client}.
*
* @author Haotian Zhang
*/
public class EnhancedFeignClient implements Client {
private final Client delegate;
private List<EnhancedFeignPlugin> preEnhancedFeignPlugins;
private List<EnhancedFeignPlugin> postEnhancedFeignPlugins;
private List<EnhancedFeignPlugin> exceptionEnhancedFeignPlugins;
private List<EnhancedFeignPlugin> finallyEnhancedFeignPlugins;
public EnhancedFeignClient(Client target, List<EnhancedFeignPlugin> enhancedFeignPlugins) {
this.delegate = checkNotNull(target, "target");
// Init the EnhancedFeignPlugins list.
this.preEnhancedFeignPlugins = new ArrayList<>();
this.postEnhancedFeignPlugins = new ArrayList<>();
this.exceptionEnhancedFeignPlugins = new ArrayList<>();
this.finallyEnhancedFeignPlugins = new ArrayList<>();
if (!CollectionUtils.isEmpty(enhancedFeignPlugins)) {
for (EnhancedFeignPlugin feignPlugin : enhancedFeignPlugins) {
if (feignPlugin.getType().equals(EnhancedFeignPluginType.PRE)) {
this.preEnhancedFeignPlugins.add(feignPlugin);
}
else if (feignPlugin.getType().equals(EnhancedFeignPluginType.POST)) {
this.postEnhancedFeignPlugins.add(feignPlugin);
}
else if (feignPlugin.getType().equals(EnhancedFeignPluginType.EXCEPTION)) {
this.exceptionEnhancedFeignPlugins.add(feignPlugin);
}
else if (feignPlugin.getType().equals(EnhancedFeignPluginType.FINALLY)) {
this.finallyEnhancedFeignPlugins.add(feignPlugin);
}
}
}
// Set the ordered enhanced feign plugins.
this.preEnhancedFeignPlugins = getSortedEnhancedFeignPlugin(this.preEnhancedFeignPlugins);
this.postEnhancedFeignPlugins = getSortedEnhancedFeignPlugin(this.postEnhancedFeignPlugins);
this.exceptionEnhancedFeignPlugins = getSortedEnhancedFeignPlugin(this.exceptionEnhancedFeignPlugins);
this.finallyEnhancedFeignPlugins = getSortedEnhancedFeignPlugin(this.finallyEnhancedFeignPlugins);
}
@Override
public Response execute(Request request, Options options) throws IOException {
EnhancedFeignContext enhancedFeignContext = new EnhancedFeignContext();
enhancedFeignContext.setRequest(request);
enhancedFeignContext.setOptions(options);
// Run pre enhanced feign plugins.
for (EnhancedFeignPlugin plugin : preEnhancedFeignPlugins) {
try {
plugin.run(enhancedFeignContext);
}
catch (Throwable throwable) {
plugin.handlerThrowable(enhancedFeignContext, throwable);
}
}
try {
Response response = delegate.execute(request, options);
enhancedFeignContext.setResponse(response);
// Run post enhanced feign plugins.
for (EnhancedFeignPlugin plugin : postEnhancedFeignPlugins) {
try {
plugin.run(enhancedFeignContext);
}
catch (Throwable throwable) {
plugin.handlerThrowable(enhancedFeignContext, throwable);
}
}
return response;
}
catch (IOException origin) {
enhancedFeignContext.setException(origin);
// Run exception enhanced feign plugins.
for (EnhancedFeignPlugin plugin : exceptionEnhancedFeignPlugins) {
try {
plugin.run(enhancedFeignContext);
}
catch (Throwable throwable) {
plugin.handlerThrowable(enhancedFeignContext, throwable);
}
}
throw origin;
}
finally {
// Run finally enhanced feign plugins.
for (EnhancedFeignPlugin plugin : finallyEnhancedFeignPlugins) {
try {
plugin.run(enhancedFeignContext);
}
catch (Throwable throwable) {
plugin.handlerThrowable(enhancedFeignContext, throwable);
}
}
}
}
/**
* Ascending, which means the lower order number, the earlier executing enhanced feign plugin.
*
* @return sorted feign pre plugin list
*/
private List<EnhancedFeignPlugin> getSortedEnhancedFeignPlugin(List<EnhancedFeignPlugin> preEnhancedFeignPlugins) {
return new ArrayList<>(preEnhancedFeignPlugins)
.stream()
.sorted(Comparator.comparing(EnhancedFeignPlugin::getOrder))
.collect(Collectors.toList());
}
}

@ -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.rpc.enhancement.feign.plugin;
import feign.Request;
import feign.Response;
/**
* Context used by EnhancedFeignPlugin.
*
* @author Haotian Zhang
*/
public class EnhancedFeignContext {
private Request request;
private Request.Options options;
private Response response;
private Exception exception;
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public Request.Options getOptions() {
return options;
}
public void setOptions(Request.Options options) {
this.options = options;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
public Exception getException() {
return exception;
}
public void setException(Exception exception) {
this.exception = exception;
}
}

@ -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.rpc.enhancement.feign.plugin;
import org.springframework.core.Ordered;
/**
* Pre plugin used by EnhancedFeignClient.
*
* @author Haotian Zhang
*/
public interface EnhancedFeignPlugin extends Ordered {
/**
* Get name of plugin.
*
* @return name
*/
default String getName() {
return this.getClass().getName();
}
/**
* Get type of plugin.
*
* @return {@link EnhancedFeignPluginType}
*/
EnhancedFeignPluginType getType();
/**
* Run the plugin.
*
* @param context context in enhanced feign client.
* @throws Throwable throwable thrown from run method.
*/
void run(EnhancedFeignContext context) throws Throwable;
/**
* Handler throwable from {@link EnhancedFeignPlugin#run(EnhancedFeignContext)}.
*
* @param context context in enhanced feign client.
* @param throwable throwable thrown from run method.
*/
default void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
}
}

@ -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.cloud.rpc.enhancement.feign.plugin;
/**
* Type of EnhancedFeignPlugin.
*
* @author Haotian Zhang
*/
public enum EnhancedFeignPluginType {
/**
* Pre feign plugin.
*/
PRE,
/**
* Post feign plugin.
*/
POST,
/**
* Exception feign plugin.
*/
EXCEPTION,
/**
* Finally feign plugin.
*/
FINALLY
}

@ -0,0 +1,85 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.net.SocketTimeoutException;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
/**
* Polaris reporter when feign call is successful.
*
* @author Haotian Zhang
*/
public class ExceptionPolarisReporter implements EnhancedFeignPlugin {
private static final Logger LOG = LoggerFactory.getLogger(ExceptionPolarisReporter.class);
@Autowired(required = false)
private ConsumerAPI consumerAPI;
@Override
public String getName() {
return ExceptionPolarisReporter.class.getName();
}
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.EXCEPTION;
}
@Override
public void run(EnhancedFeignContext context) {
if (consumerAPI != null) {
Request request = context.getRequest();
Response response = context.getResponse();
Exception exception = context.getException();
RetStatus retStatus = RetStatus.RetFail;
if (exception instanceof SocketTimeoutException) {
retStatus = RetStatus.RetTimeout;
}
LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].", retStatus.name(), request, response);
ServiceCallResult resultRequest = ReporterUtils.createServiceCallResult(request, retStatus);
consumerAPI.updateServiceCallResult(resultRequest);
}
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
Request request = context.getRequest();
Response response = context.getResponse();
LOG.error("ExceptionPolarisReporter runs failed. Request=[{}]. Response=[{}].", request, response, throwable);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}

@ -15,68 +15,28 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Client;
import feign.Request; import feign.Request;
import feign.Request.Options;
import feign.Response;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static feign.Util.checkNotNull;
/** /**
* Wrap for {@link Client}. * Util for polaris reporter.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignClient implements Client { public final class ReporterUtils {
private static final Logger LOG = LoggerFactory.getLogger(PolarisFeignClient.class);
private final Client delegate;
private final ConsumerAPI consumerAPI;
public PolarisFeignClient(Client target, ConsumerAPI consumerAPI) {
this.delegate = checkNotNull(target, "target");
this.consumerAPI = checkNotNull(consumerAPI, "CircuitBreakAPI");
}
@Override private ReporterUtils() {
public Response execute(Request request, Options options) throws IOException {
final ServiceCallResult resultRequest = createServiceCallResult(request);
try {
Response response = delegate.execute(request, options);
// HTTP code greater than 500 is an exception
if (response.status() > 500) {
resultRequest.setRetStatus(RetStatus.RetFail);
}
LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].",
resultRequest.getRetStatus().name(), request, response);
return response;
}
catch (IOException origin) {
resultRequest.setRetStatus(RetStatus.RetFail);
LOG.debug("Will report result of {}. Request=[{}].", resultRequest.getRetStatus().name(), request, origin);
throw origin;
}
finally {
consumerAPI.updateServiceCallResult(resultRequest);
}
} }
private ServiceCallResult createServiceCallResult(final Request request) { public static ServiceCallResult createServiceCallResult(final Request request, RetStatus retStatus) {
ServiceCallResult resultRequest = new ServiceCallResult(); ServiceCallResult resultRequest = new ServiceCallResult();
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
@ -84,7 +44,7 @@ public class PolarisFeignClient implements Client {
resultRequest.setService(serviceName); resultRequest.setService(serviceName);
URI uri = URI.create(request.url()); URI uri = URI.create(request.url());
resultRequest.setMethod(uri.getPath()); resultRequest.setMethod(uri.getPath());
resultRequest.setRetStatus(RetStatus.RetSuccess); resultRequest.setRetStatus(retStatus);
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
String sourceService = MetadataContext.LOCAL_SERVICE; String sourceService = MetadataContext.LOCAL_SERVICE;
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {

@ -0,0 +1,82 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
/**
* Polaris reporter when feign call is successful.
*
* @author Haotian Zhang
*/
public class SuccessPolarisReporter implements EnhancedFeignPlugin {
private static final Logger LOG = LoggerFactory.getLogger(SuccessPolarisReporter.class);
@Autowired(required = false)
private ConsumerAPI consumerAPI;
@Override
public String getName() {
return SuccessPolarisReporter.class.getName();
}
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.POST;
}
@Override
public void run(EnhancedFeignContext context) {
if (consumerAPI != null) {
Request request = context.getRequest();
Response response = context.getResponse();
RetStatus retStatus = RetStatus.RetSuccess;
if (response.status() > 500) {
retStatus = RetStatus.RetFail;
}
LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].", retStatus.name(), request, response);
ServiceCallResult resultRequest = ReporterUtils.createServiceCallResult(request, retStatus);
consumerAPI.updateServiceCallResult(resultRequest);
}
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
Request request = context.getRequest();
Response response = context.getResponse();
LOG.error("SuccessPolarisReporter runs failed. Request=[{}]. Response=[{}].", request, response, throwable);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.util.Map; import java.util.Map;
@ -28,19 +28,19 @@ import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
* Auto configuration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced}, * Autoconfiguration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced},
* then replace {@link org.springframework.web.client.ResponseErrorHandler} * then replace {@link org.springframework.web.client.ResponseErrorHandler}
* with {@link PolarisRestTemplateResponseErrorHandler} . * with {@link EnhancedRestTemplateReporter} .
* *
* @author wh 2022/6/21 * @author wh 2022/6/21
*/ */
public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton { public class EnhancedRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton {
private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler; private final EnhancedRestTemplateReporter enhancedRestTemplateReporter;
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) { public EnhancedRestTemplateModifier(EnhancedRestTemplateReporter enhancedRestTemplateReporter) {
this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler; this.enhancedRestTemplateReporter = enhancedRestTemplateReporter;
} }
@Override @Override
@ -54,7 +54,7 @@ public class PolarisRestTemplateModifier implements ApplicationContextAware, Sma
private void initRestTemplate(String beanName, Object bean) { private void initRestTemplate(String beanName, Object bean) {
if (bean instanceof RestTemplate) { if (bean instanceof RestTemplate) {
RestTemplate restTemplate = (RestTemplate) bean; RestTemplate restTemplate = (RestTemplate) bean;
restTemplate.setErrorHandler(polarisRestTemplateResponseErrorHandler); restTemplate.setErrorHandler(enhancedRestTemplateReporter);
} }
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -43,9 +43,9 @@ import org.springframework.web.client.ResponseErrorHandler;
* *
* @author wh 2022/6/21 * @author wh 2022/6/21
*/ */
public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler { public class EnhancedRestTemplateReporter implements ResponseErrorHandler {
private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class); private static final Logger LOG = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class);
private static final String FILE_NAME = "connection"; private static final String FILE_NAME = "connection";
@ -53,7 +53,7 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
private final PolarisResponseErrorHandler polarisResponseErrorHandler; private final PolarisResponseErrorHandler polarisResponseErrorHandler;
public PolarisRestTemplateResponseErrorHandler( public EnhancedRestTemplateReporter(
ConsumerAPI consumerAPI, PolarisResponseErrorHandler polarisResponseErrorHandler) { ConsumerAPI consumerAPI, PolarisResponseErrorHandler polarisResponseErrorHandler) {
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.polarisResponseErrorHandler = polarisResponseErrorHandler; this.polarisResponseErrorHandler = polarisResponseErrorHandler;
@ -91,9 +91,12 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
} }
catch (Exception e) { catch (Exception e) {
LOG.error("Will report response of {} url {}", response, url, e); LOG.error("Will report response of {} url {}", response, url, e);
resultRequest.setRetStatus(RetStatus.RetFail);
throw e; throw e;
} }
finally { finally {
LOG.debug("Will report result of {}. URL=[{}]. Response=[{}].", resultRequest.getRetStatus().name(),
url, response);
consumerAPI.updateServiceCallResult(resultRequest); consumerAPI.updateServiceCallResult(resultRequest);
} }
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.ResponseErrorHandler;

@ -0,0 +1,82 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* The properties for stat reporter.
*
* @author Haotian Zhang
*/
@ConfigurationProperties("spring.cloud.polaris.stat")
public class PolarisStatProperties {
/**
* If state reporter enabled.
*/
private boolean enabled = false;
/**
* Local host for prometheus to pull.
*/
private String host;
/**
* Port for prometheus to pull.
*/
private int port = 28080;
/**
* Path for prometheus to pull.
*/
private String path = "/metrics";
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
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 String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}

@ -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.rpc.enhancement.stat.config;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
/**
* Autoconfiguration of stat reporter.
*
* @author Haotian Zhang
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled
@Import({PolarisStatProperties.class})
public class PolarisStatPropertiesAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public StatConfigModifier statReporterConfigModifier(PolarisStatProperties polarisStatProperties, Environment environment) {
return new StatConfigModifier(polarisStatProperties, environment);
}
}

@ -0,0 +1,34 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Autoconfiguration of stat reporter at bootstrap phase.
*
* @author lepdou 2022-03-29
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty("spring.cloud.polaris.enabled")
@Import(PolarisStatPropertiesAutoConfiguration.class)
public class PolarisStatPropertiesBootstrapConfiguration {
}

@ -0,0 +1,70 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import com.tencent.cloud.common.constant.ContextConstant;
import com.tencent.cloud.polaris.context.PolarisConfigModifier;
import com.tencent.polaris.factory.config.ConfigurationImpl;
import com.tencent.polaris.plugins.stat.prometheus.handler.PrometheusHandlerConfig;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import static com.tencent.polaris.api.config.global.StatReporterConfig.DEFAULT_REPORTER_PROMETHEUS;
/**
* Config modifier for stat reporter.
*
* @author Haotian Zhang
*/
public class StatConfigModifier implements PolarisConfigModifier {
private final PolarisStatProperties polarisStatProperties;
private final Environment environment;
public StatConfigModifier(PolarisStatProperties polarisStatProperties, Environment environment) {
this.polarisStatProperties = polarisStatProperties;
this.environment = environment;
}
@Override
public void modify(ConfigurationImpl configuration) {
// Turn on stat reporter configuration.
configuration.getGlobal().getStatReporter().setEnable(polarisStatProperties.isEnabled());
// Set prometheus plugin.
if (polarisStatProperties.isEnabled()) {
PrometheusHandlerConfig prometheusHandlerConfig = configuration.getGlobal().getStatReporter()
.getPluginConfig(DEFAULT_REPORTER_PROMETHEUS, PrometheusHandlerConfig.class);
if (!StringUtils.hasText(polarisStatProperties.getHost())) {
polarisStatProperties.setHost(environment.getProperty("spring.cloud.client.ip-address"));
}
prometheusHandlerConfig.setHost(polarisStatProperties.getHost());
prometheusHandlerConfig.setPort(polarisStatProperties.getPort());
prometheusHandlerConfig.setPath(polarisStatProperties.getPath());
configuration.getGlobal().getStatReporter()
.setPluginConfig(DEFAULT_REPORTER_PROMETHEUS, prometheusHandlerConfig);
}
}
@Override
public int getOrder() {
return ContextConstant.ModifierOrder.STAT_REPORTER_ORDER;
}
}

@ -0,0 +1,27 @@
{
"properties": [
{
"name": "spring.cloud.polaris.stat.enabled",
"type": "java.lang.Boolean",
"defaultValue": false,
"description": "Enable polaris stat reporter or not."
},
{
"name": "spring.cloud.polaris.stat.host",
"type": "java.lang.String",
"description": "Local host for prometheus to pull."
},
{
"name": "spring.cloud.polaris.stat.port",
"type": "java.lang.Integer",
"defaultValue": "28080",
"description": "Port for prometheus to pull."
},
{
"name": "spring.cloud.polaris.stat.path",
"type": "java.lang.String",
"defaultValue": "/metrics",
"description": "Path for prometheus to pull."
}
]
}

@ -0,0 +1,5 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.rpc.enhancement.stat.config.PolarisStatPropertiesBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration,\
com.tencent.cloud.rpc.enhancement.stat.config.PolarisStatPropertiesAutoConfiguration

@ -13,19 +13,20 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * 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 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.polaris.circuitbreaker; package com.tencent.cloud.rpc.enhancement.config;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.feign.EnhancedFeignBeanPostProcessor;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.ExceptionPolarisReporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.reporter.SuccessPolarisReporter;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateModifier;
import com.tencent.cloud.rpc.enhancement.resttemplate.EnhancedRestTemplateReporter;
import com.tencent.polaris.api.core.ConsumerAPI;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -35,32 +36,33 @@ import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Test For {@link PolarisCircuitBreakerAutoConfiguration} . * Test For {@link RpcEnhancementAutoConfiguration}.
* *
* @author <a href="mailto:iskp.me@gmail.com">Palmer Xu</a> 2022-06-28 * @author Haotian Zhang, wh, Palmer Xu
*/ */
public class PolarisRestTemplateAutoConfigurationTest { public class RpcEnhancementAutoConfigurationTest {
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration( .withConfiguration(AutoConfigurations.of(
AutoConfigurations.of(
PolarisRestTemplateAutoConfigurationTester.class,
PolarisContextAutoConfiguration.class, PolarisContextAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class)) RpcEnhancementAutoConfiguration.class,
PolarisRestTemplateAutoConfigurationTester.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test @Test
public void testInitialization() { public void testDefaultInitialization() {
this.contextRunner this.contextRunner.run(context -> {
.run(context -> { assertThat(context).hasSingleBean(ConsumerAPI.class);
assertThat(context).hasSingleBean(PolarisRestTemplateModifier.class); assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class);
assertThat(context).hasSingleBean(PolarisRestTemplateResponseErrorHandler.class); assertThat(context).hasSingleBean(SuccessPolarisReporter.class);
assertThat(context).hasSingleBean(ExceptionPolarisReporter.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateModifier.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class);
}); });
} }
@Configuration @Configuration
@EnableAutoConfiguration @EnableAutoConfiguration
@AutoConfigureBefore(PolarisCircuitBreakerAutoConfiguration.class)
static class PolarisRestTemplateAutoConfigurationTester { static class PolarisRestTemplateAutoConfigurationTester {
@Bean @Bean

@ -15,9 +15,8 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import com.tencent.polaris.api.core.ConsumerAPI;
import feign.Client; import feign.Client;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -35,19 +34,17 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Test for {@link PolarisFeignBeanPostProcessor}. * Test for {@link EnhancedFeignBeanPostProcessor}.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBeanPostProcessorTest { public class EnhancedFeignBeanPostProcessorTest {
private PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor; private EnhancedFeignBeanPostProcessor enhancedFeignBeanPostProcessor;
@Before @Before
public void setUp() { public void setUp() {
ConsumerAPI consumerAPI = mock(ConsumerAPI.class); enhancedFeignBeanPostProcessor = new EnhancedFeignBeanPostProcessor(null);
polarisFeignBeanPostProcessor = new PolarisFeignBeanPostProcessor(consumerAPI);
} }
@Test @Test
@ -66,22 +63,24 @@ public class PolarisFeignBeanPostProcessorTest {
} }
return null; return null;
}).when(beanFactory).getBean(any(Class.class)); }).when(beanFactory).getBean(any(Class.class));
polarisFeignBeanPostProcessor.setBeanFactory(beanFactory); enhancedFeignBeanPostProcessor.setBeanFactory(beanFactory);
// isNeedWrap(bean) == false // isNeedWrap(bean) == false
Object bean1 = new Object(); Object bean1 = new Object();
Object bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean1, "bean1"); Object bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean1, "bean1");
assertThat(bean).isNotInstanceOfAny(PolarisFeignClient.class, PolarisFeignBlockingLoadBalancerClient.class); assertThat(bean).isNotInstanceOfAny(
EnhancedFeignClient.class,
EnhancedFeignBlockingLoadBalancerClient.class);
// bean instanceOf Client.class // bean instanceOf Client.class
Client bean2 = mock(Client.class); Client bean2 = mock(Client.class);
bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean2, "bean2"); bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean2, "bean2");
assertThat(bean).isInstanceOf(PolarisFeignClient.class); assertThat(bean).isInstanceOf(EnhancedFeignClient.class);
// bean instanceOf FeignBlockingLoadBalancerClient.class // bean instanceOf FeignBlockingLoadBalancerClient.class
FeignBlockingLoadBalancerClient bean3 = mock(FeignBlockingLoadBalancerClient.class); FeignBlockingLoadBalancerClient bean4 = mock(FeignBlockingLoadBalancerClient.class);
doReturn(mock(Client.class)).when(bean3).getDelegate(); doReturn(mock(Client.class)).when(bean4).getDelegate();
bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean3, "bean3"); bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean4, "bean4");
assertThat(bean).isInstanceOf(PolarisFeignBlockingLoadBalancerClient.class); assertThat(bean).isInstanceOf(EnhancedFeignBlockingLoadBalancerClient.class);
} }
} }

@ -15,22 +15,22 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.Test; import org.junit.Test;
/** /**
* Test for {@link PolarisFeignBlockingLoadBalancerClient}. * Test for {@link EnhancedFeignBlockingLoadBalancerClient}.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBlockingLoadBalancerClientTest { public class EnhancedFeignBlockingLoadBalancerClientTest {
@Test @Test
public void testConstructor() { public void testConstructor() {
try { try {
new PolarisFeignBlockingLoadBalancerClient(null, null, null); new EnhancedFeignBlockingLoadBalancerClient(null, null, null);
} }
catch (Exception e) { catch (Exception e) {
Assertions.fail("Exception encountered.", e); Assertions.fail("Exception encountered.", e);

@ -15,13 +15,16 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import feign.Client; import feign.Client;
import feign.Request; import feign.Request;
import feign.RequestTemplate; import feign.RequestTemplate;
@ -39,23 +42,22 @@ import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Test for {@link PolarisFeignClient}. * Test for {@link EnhancedFeignClient}.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = PolarisFeignClientTest.TestApplication.class, @SpringBootTest(classes = EnhancedFeignClientTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"}) properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"})
public class PolarisFeignClientTest { public class EnhancedFeignClientTest {
@Test @Test
public void testConstructor() { public void testConstructor() {
try { try {
new PolarisFeignClient(null, null); new EnhancedFeignClient(null, null);
fail("NullPointerException should be thrown."); fail("NullPointerException should be thrown.");
} }
catch (Throwable e) { catch (Throwable e) {
@ -64,16 +66,15 @@ public class PolarisFeignClientTest {
} }
try { try {
new PolarisFeignClient(mock(Client.class), null); new EnhancedFeignClient(mock(Client.class), null);
fail("NullPointerException should be thrown.");
} }
catch (Throwable e) { catch (Throwable e) {
assertThat(e).isInstanceOf(NullPointerException.class); fail("Exception encountered.", e);
assertThat(e.getMessage()).isEqualTo("CircuitBreakAPI");
} }
List<EnhancedFeignPlugin> enhancedFeignPlugins = getMockEnhancedFeignPlugins();
try { try {
assertThat(new PolarisFeignClient(mock(Client.class), mock(ConsumerAPI.class))).isInstanceOf(PolarisFeignClient.class); new EnhancedFeignClient(mock(Client.class), enhancedFeignPlugins);
} }
catch (Throwable e) { catch (Throwable e) {
fail("Exception encountered.", e); fail("Exception encountered.", e);
@ -95,10 +96,6 @@ public class PolarisFeignClientTest {
throw new IOException("Mock exception."); throw new IOException("Mock exception.");
}).when(delegate).execute(any(Request.class), nullable(Request.Options.class)); }).when(delegate).execute(any(Request.class), nullable(Request.Options.class));
// mock ConsumerAPI.class
ConsumerAPI consumerAPI = mock(ConsumerAPI.class);
doNothing().when(consumerAPI).updateServiceCallResult(any(ServiceCallResult.class));
// mock target // mock target
Target<Object> target = Target.EmptyTarget.create(Object.class); Target<Object> target = Target.EmptyTarget.create(Object.class);
@ -106,7 +103,7 @@ public class PolarisFeignClientTest {
RequestTemplate requestTemplate = new RequestTemplate(); RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.feignTarget(target); requestTemplate.feignTarget(target);
PolarisFeignClient polarisFeignClient = new PolarisFeignClient(delegate, consumerAPI); EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, getMockEnhancedFeignPlugins());
// 200 // 200
Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test",
@ -130,6 +127,101 @@ public class PolarisFeignClientTest {
} }
} }
private List<EnhancedFeignPlugin> getMockEnhancedFeignPlugins() {
List<EnhancedFeignPlugin> enhancedFeignPlugins = new ArrayList<>();
enhancedFeignPlugins.add(new EnhancedFeignPlugin() {
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.PRE;
}
@Override
public void run(EnhancedFeignContext context) {
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
}
@Override
public int getOrder() {
return 0;
}
});
enhancedFeignPlugins.add(new EnhancedFeignPlugin() {
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.POST;
}
@Override
public void run(EnhancedFeignContext context) {
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
}
@Override
public int getOrder() {
return 0;
}
});
enhancedFeignPlugins.add(new EnhancedFeignPlugin() {
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.EXCEPTION;
}
@Override
public void run(EnhancedFeignContext context) {
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
}
@Override
public int getOrder() {
return 0;
}
});
enhancedFeignPlugins.add(new EnhancedFeignPlugin() {
@Override
public EnhancedFeignPluginType getType() {
return EnhancedFeignPluginType.FINALLY;
}
@Override
public void run(EnhancedFeignContext context) {
}
@Override
public void handlerThrowable(EnhancedFeignContext context, Throwable throwable) {
}
@Override
public int getOrder() {
return 0;
}
});
return enhancedFeignPlugins;
}
@SpringBootApplication @SpringBootApplication
protected static class TestApplication { protected static class TestApplication {

@ -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.cloud.rpc.enhancement.feign.plugin;
import feign.Request;
import feign.Response;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Test for {@link EnhancedFeignContext}.
*
* @author Haotian Zhang
*/
public class EnhancedFeignContextTest {
@Test
public void testGetAndSet() {
EnhancedFeignContext enhancedFeignContext = new EnhancedFeignContext();
enhancedFeignContext.setRequest(mock(Request.class));
enhancedFeignContext.setOptions(mock(Request.Options.class));
enhancedFeignContext.setResponse(mock(Response.class));
enhancedFeignContext.setException(mock(Exception.class));
assertThat(enhancedFeignContext.getRequest()).isNotNull();
assertThat(enhancedFeignContext.getOptions()).isNotNull();
assertThat(enhancedFeignContext.getResponse()).isNotNull();
assertThat(enhancedFeignContext.getException()).isNotNull();
}
}

@ -0,0 +1,101 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus;
import feign.Request;
import feign.Response;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
/**
* Test for {@link ExceptionPolarisReporter}.
*
* @author Haotian Zhang
*/
@RunWith(MockitoJUnitRunner.class)
public class ExceptionPolarisReporterTest {
private static MockedStatic<ReporterUtils> mockedReporterUtils;
@Mock
private ConsumerAPI consumerAPI;
@InjectMocks
private ExceptionPolarisReporter exceptionPolarisReporter;
@BeforeClass
public static void beforeClass() {
mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class);
mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(Request.class), any(RetStatus.class)))
.thenReturn(null);
}
@AfterClass
public static void afterClass() {
mockedReporterUtils.close();
}
@Test
public void testGetName() {
assertThat(exceptionPolarisReporter.getName()).isEqualTo(ExceptionPolarisReporter.class.getName());
}
@Test
public void testType() {
assertThat(exceptionPolarisReporter.getType()).isEqualTo(EnhancedFeignPluginType.EXCEPTION);
}
@Test
public void testRun() {
// mock request
Request request = mock(Request.class);
// mock response
Response response = mock(Response.class);
EnhancedFeignContext context = new EnhancedFeignContext();
context.setRequest(request);
context.setResponse(response);
exceptionPolarisReporter.run(context);
}
@Test
public void testHandlerThrowable() {
// mock request
Request request = mock(Request.class);
// mock response
Response response = mock(Response.class);
EnhancedFeignContext context = new EnhancedFeignContext();
context.setRequest(request);
context.setResponse(response);
exceptionPolarisReporter.handlerThrowable(context, new RuntimeException("Mock exception."));
}
}

@ -0,0 +1,96 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Request;
import feign.RequestTemplate;
import feign.Target;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* Test for {@link ReporterUtils}.
*
* @author Haotian Zhang
*/
@RunWith(MockitoJUnitRunner.class)
public class ReporterUtilsTest {
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
@BeforeClass
public static void beforeClass() {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn("unit-test");
}
@AfterClass
public static void afterClass() {
mockedApplicationContextAwareUtils.close();
}
@Before
public void setUp() {
MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST;
MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER;
}
@Test
public void testCreateServiceCallResult() {
// mock target
Target<?> target = mock(Target.class);
doReturn(SERVICE_PROVIDER).when(target).name();
// mock RequestTemplate.class
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.feignTarget(target);
// mock request
Request request = mock(Request.class);
doReturn(requestTemplate).when(request).requestTemplate();
doReturn("http://1.1.1.1:2345/path").when(request).url();
ServiceCallResult serviceCallResult = ReporterUtils.createServiceCallResult(request, RetStatus.RetSuccess);
assertThat(serviceCallResult.getNamespace()).isEqualTo(NAMESPACE_TEST);
assertThat(serviceCallResult.getService()).isEqualTo(SERVICE_PROVIDER);
assertThat(serviceCallResult.getHost()).isEqualTo("1.1.1.1");
assertThat(serviceCallResult.getPort()).isEqualTo(2345);
assertThat(serviceCallResult.getRetStatus()).isEqualTo(RetStatus.RetSuccess);
assertThat(serviceCallResult.getMethod()).isEqualTo("/path");
assertThat(serviceCallResult.getCallerService().getNamespace()).isEqualTo(NAMESPACE_TEST);
assertThat(serviceCallResult.getCallerService().getService()).isEqualTo(SERVICE_PROVIDER);
}
}

@ -0,0 +1,103 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignContext;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPluginType;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus;
import feign.Request;
import feign.Response;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* Test for {@link SuccessPolarisReporter}.
*
* @author Haotian Zhang
*/
@RunWith(MockitoJUnitRunner.class)
public class SuccessPolarisReporterTest {
private static MockedStatic<ReporterUtils> mockedReporterUtils;
@Mock
private ConsumerAPI consumerAPI;
@InjectMocks
private SuccessPolarisReporter successPolarisReporter;
@BeforeClass
public static void beforeClass() {
mockedReporterUtils = Mockito.mockStatic(ReporterUtils.class);
mockedReporterUtils.when(() -> ReporterUtils.createServiceCallResult(any(Request.class), any(RetStatus.class)))
.thenReturn(null);
}
@AfterClass
public static void afterClass() {
mockedReporterUtils.close();
}
@Test
public void testGetName() {
assertThat(successPolarisReporter.getName()).isEqualTo(SuccessPolarisReporter.class.getName());
}
@Test
public void testType() {
assertThat(successPolarisReporter.getType()).isEqualTo(EnhancedFeignPluginType.POST);
}
@Test
public void testRun() {
// mock request
Request request = mock(Request.class);
// mock response
Response response = mock(Response.class);
doReturn(502).when(response).status();
EnhancedFeignContext context = new EnhancedFeignContext();
context.setRequest(request);
context.setResponse(response);
successPolarisReporter.run(context);
}
@Test
public void testHandlerThrowable() {
// mock request
Request request = mock(Request.class);
// mock response
Response response = mock(Response.class);
EnhancedFeignContext context = new EnhancedFeignContext();
context.setRequest(request);
context.setResponse(response);
successPolarisReporter.handlerThrowable(context, new RuntimeException("Mock exception."));
}
}

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -35,19 +35,19 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* Test For {@link PolarisRestTemplateResponseErrorHandler}. * Test For {@link EnhancedRestTemplateReporter}.
* *
* @author wh 2022/6/22 * @author wh 2022/6/22
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = PolarisRestTemplateResponseErrorHandlerTest.TestApplication.class, @SpringBootTest(classes = EnhancedRestTemplateReporterTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"}) properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"})
public class PolarisRestTemplateResponseErrorHandlerTest { public class EnhancedRestTemplateReporterTest {
@Test @Test
public void handleError() throws Exception { public void handleError() throws Exception {
ConsumerAPI consumerAPI = mock(ConsumerAPI.class); ConsumerAPI consumerAPI = mock(ConsumerAPI.class);
PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler = new PolarisRestTemplateResponseErrorHandler(consumerAPI, null); EnhancedRestTemplateReporter enhancedRestTemplateReporter = new EnhancedRestTemplateReporter(consumerAPI, null);
URI uri = mock(URI.class); URI uri = mock(URI.class);
when(uri.getPath()).thenReturn("/test"); when(uri.getPath()).thenReturn("/test");
when(uri.getHost()).thenReturn("host"); when(uri.getHost()).thenReturn("host");
@ -58,7 +58,7 @@ public class PolarisRestTemplateResponseErrorHandlerTest {
when(url.getPort()).thenReturn(8080); when(url.getPort()).thenReturn(8080);
when(httpURLConnection.getResponseCode()).thenReturn(200); when(httpURLConnection.getResponseCode()).thenReturn(200);
SimpleClientHttpResponseTest clientHttpResponse = new SimpleClientHttpResponseTest(httpURLConnection); SimpleClientHttpResponseTest clientHttpResponse = new SimpleClientHttpResponseTest(httpURLConnection);
polarisRestTemplateResponseErrorHandler.handleError(uri, HttpMethod.GET, clientHttpResponse); enhancedRestTemplateReporter.handleError(uri, HttpMethod.GET, clientHttpResponse);
when(consumerAPI.unWatchService(null)).thenReturn(true); when(consumerAPI.unWatchService(null)).thenReturn(true);
} }

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

@ -0,0 +1,45 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisStatPropertiesAutoConfiguration}.
*
* @author Haotian Zhang
*/
public class PolarisStatPropertiesAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisStatPropertiesAutoConfiguration.class));
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(PolarisStatPropertiesAutoConfiguration.class);
assertThat(context).hasSingleBean(PolarisStatProperties.class);
assertThat(context).hasSingleBean(StatConfigModifier.class);
});
}
}

@ -0,0 +1,47 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisStatPropertiesBootstrapConfiguration}.
*
* @author Haotian Zhang
*/
public class PolarisStatPropertiesBootstrapConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisStatPropertiesBootstrapConfiguration.class))
.withPropertyValues("spring.cloud.polaris.enabled=true");
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(PolarisStatPropertiesBootstrapConfiguration.class);
assertThat(context).hasSingleBean(PolarisStatPropertiesAutoConfiguration.class);
assertThat(context).hasSingleBean(PolarisStatProperties.class);
assertThat(context).hasSingleBean(StatConfigModifier.class);
});
}
}

@ -0,0 +1,57 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.rpc.enhancement.stat.config;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisStatProperties}.
*
* @author Haotian Zhang
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PolarisStatPropertiesTest.TestApplication.class)
@ActiveProfiles("test")
public class PolarisStatPropertiesTest {
@Autowired
private PolarisStatProperties polarisStatProperties;
@Test
public void testDefaultInitialization() {
assertThat(polarisStatProperties).isNotNull();
assertThat(polarisStatProperties.isEnabled()).isTrue();
assertThat(polarisStatProperties.getHost()).isNotBlank();
assertThat(polarisStatProperties.getPort()).isEqualTo(20000);
assertThat(polarisStatProperties.getPath()).isEqualTo("/xxx");
}
@SpringBootApplication
protected static class TestApplication {
}
}

@ -0,0 +1,3 @@
spring.cloud.polaris.stat.enabled=true
spring.cloud.polaris.stat.port=20000
spring.cloud.polaris.stat.path=/xxx
Loading…
Cancel
Save