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

Co-authored-by: VOPEN.XYZ <x_vivi@yeah.net>
pull/441/merge
Haotian Zhang 2 years ago committed by GitHub
parent 3e1d79d033
commit 3c4bddd6d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,6 +4,8 @@
- [Bugfix: optimize ratelimit actuator](https://github.com/Tencent/spring-cloud-tencent/pull/413)
- [Feature: add rate limit filter debug log](https://github.com/Tencent/spring-cloud-tencent/pull/417)
- [Feature: Optimized configuration update](https://github.com/Tencent/spring-cloud-tencent/pull/423)
- [[Optimize] improve load-balance ut and support configurable heartbeat interval](https://github.com/Tencent/spring-cloud-tencent/pull/427)
- [Feature: add feature-env plugin & add spring cloud gateway staining plugin](https://github.com/Tencent/spring-cloud-tencent/pull/428)
- [Optimize: add EncodeTransferMedataRestTemplateInterceptor to RestTemplate](https://github.com/Tencent/spring-cloud-tencent/pull/434)
- [feat:enhance Feign and RestTemplate and support Polaris monitor.](https://github.com/Tencent/spring-cloud-tencent/pull/435)
- [Optimize: Specification apollo code reference notes](https://github.com/Tencent/spring-cloud-tencent/pull/442)

@ -42,7 +42,10 @@ Spring Cloud Tencent提供的能力包括但不限于
- 密码polaris
- 控制面地址: `grpc://183.47.111.80:8091`
-
`spring-cloud-tencent-example` 下 example 地址都默认指向了体验服务地址(`grpc://183.47.111.80:8091`),如果您只是体验 Spring Cloud Tencent可直接一键运行任何 example。
`spring-cloud-tencent-example` 下 example 地址都默认指向了体验服务地址(`grpc://183.47.111.80:8091`),如果您只是体验 Spring Cloud
Tencent可直接一键运行任何 example。
## 管控台
<img width="1792" alt="image" src="https://user-images.githubusercontent.com/4991116/163402268-48493802-4555-4b93-8e31-011410f2166b.png">
@ -51,6 +54,10 @@ Spring Cloud Tencent提供的能力包括但不限于
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
@ -97,9 +104,10 @@ Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要
<img src="https://user-images.githubusercontent.com/24446200/169198148-d4cc3494-3485-4515-9897-c8cb5504f706.png" width="30%" height="30%" />
## License
The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE)
The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in
the file [LICENSE](LICENSE)
## Stargazers over time

@ -14,9 +14,11 @@ English | [简体中文](./README-zh.md)
Spring Cloud Tencent is a open source one-stop microservice solution from Tencent.
Spring Cloud Tencent implements the Spring Cloud standard microservice SPI, so developers can quickly develop Spring Cloud cloud-native distributed applications based on Spring Cloud Tencent.
Spring Cloud Tencent implements the Spring Cloud standard microservice SPI, so developers can quickly develop Spring
Cloud cloud-native distributed applications based on Spring Cloud Tencent.
The core of Spring Cloud Tencent relies on Tencent's open-source one-stop service discovery and governance platform [Polaris](https://github.com/polarismesh/polaris) to realize various distributed microservice scenarios.
The core of Spring Cloud Tencent relies on Tencent's open-source one-stop service discovery and governance
platform [Polaris](https://github.com/polarismesh/polaris) to realize various distributed microservice scenarios.
- [Polaris Github home page](https://github.com/polarismesh/polaris)
- [Polaris official website](https://polarismesh.cn/)
@ -41,8 +43,9 @@ The capabilities provided by Spring Cloud Tencent include but are not limited to
- Password: polaris
- Server Address: `grpc://183.47.111.80:8091`
The example addresses under `spring-cloud-tencent-example` all point to the experience service address (`grpc://183.47.111.80:8091`) by default.
If you only experience Spring Cloud Tencent, you can run any example directly with one click.
The example addresses under `spring-cloud-tencent-example` all point to the experience service
address (`grpc://183.47.111.80:8091`) by default. If you only experience Spring Cloud Tencent, you can run any example
directly with one click.
## Screenshots
@ -50,7 +53,12 @@ If you only experience Spring Cloud Tencent, you can run any example directly wi
## Use Guide
All the components of Spring Cloud Tencent have been uploaded to the Maven central repository, just need to introduce dependencies.
All the components of Spring Cloud Tencent have been uploaded to the Maven central repository, just need to introduce
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:
@ -99,8 +107,9 @@ Please scan the QR code to join the chat group.
<img src="https://user-images.githubusercontent.com/24446200/169198148-d4cc3494-3485-4515-9897-c8cb5504f706.png" width="30%" height="30%" />
## License
The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE)
The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in
the file [LICENSE](LICENSE)
## Stargazers over time

@ -40,6 +40,7 @@
<modules>
<module>spring-cloud-tencent-commons</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-starter-tencent-metadata-transfer</module>
<module>spring-cloud-starter-tencent-polaris-config</module>

@ -18,36 +18,13 @@
<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>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>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-circuitbreaker-factory</artifactId>
@ -84,40 +61,10 @@
</dependency>
<!-- Polaris dependencies end -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</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>

@ -17,84 +17,53 @@
package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisResponseErrorHandler;
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.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
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.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.cloud.openfeign.FeignAutoConfiguration;
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)
public class PolarisCircuitBreakerAutoConfiguration {
/**
* Configuration for Polaris {@link feign.Feign} which can automatically bring in the call
* results for reporting.
* Autoconfiguration at bootstrap phase.
*
* @author Haotian Zhang
* @author lepdou 2022-03-29
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration")
@AutoConfigureAfter(PolarisContextAutoConfiguration.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@ConditionalOnPolarisEnabled
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
protected static class PolarisFeignClientAutoConfiguration {
public class PolarisCircuitBreakerAutoConfiguration {
@Bean
public ConsumerAPI consumerAPI(SDKContext context) {
return DiscoveryAPIFactory.createConsumerAPIByContext(context);
public CircuitBreakerConfigModifier circuitBreakerConfigModifier() {
return new CircuitBreakerConfigModifier();
}
@Bean
@Order(HIGHEST_PRECEDENCE)
public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
return new PolarisFeignBeanPostProcessor(consumerAPI);
}
}
public static class CircuitBreakerConfigModifier implements PolarisConfigModifier {
/**
* Configuration for Polaris {@link RestTemplate} which can automatically bring in the call
* results for reporting.
*
* @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 {
@Override
public void modify(ConfigurationImpl configuration) {
// Turn on circuitbreaker configuration
configuration.getConsumer().getCircuitBreaker().setEnable(true);
@Bean
public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler(
ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) {
return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler);
// 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);
}
@Bean
public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor(
PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) {
return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler);
@Override
public int getOrder() {
return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER;
}
}
}

@ -17,16 +17,9 @@
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Autoconfiguration at bootstrap phase.
@ -34,36 +27,8 @@ import org.springframework.context.annotation.Configuration;
* @author lepdou 2022-03-29
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisEnabled
@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty("spring.cloud.polaris.enabled")
@Import(PolarisCircuitBreakerAutoConfiguration.class)
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,5 +1,4 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
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.
*/
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.springframework.boot.autoconfigure.AutoConfigurations;
@ -33,20 +29,16 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Haotian Zhang
*/
public class PolarisFeignClientAutoConfigurationTest {
public class PolarisCircuitBreakerAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
PolarisContextAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(PolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(ConsumerAPI.class);
assertThat(context).hasSingleBean(PolarisFeignBeanPostProcessor.class);
assertThat(context).hasSingleBean(
PolarisCircuitBreakerAutoConfiguration.CircuitBreakerConfigModifier.class);
});
}
}

@ -15,9 +15,8 @@
* 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.springframework.boot.autoconfigure.AutoConfigurations;
@ -31,15 +30,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Haotian Zhang
*/
public class PolarisCircuitBreakerBootstrapConfigurationTest {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class))
.withPropertyValues("spring.cloud.polaris.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(
PolarisCircuitBreakerBootstrapConfiguration.CircuitBreakerConfigModifier.class);
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>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- 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>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-isolated</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-healthy</artifactId>
</exclusion>
</exclusions>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-test-common</artifactId>

@ -21,11 +21,6 @@ package com.tencent.cloud.polaris;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler;
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.context.annotation.Bean;
@ -42,18 +37,6 @@ import org.springframework.context.annotation.Import;
@Import({PolarisDiscoveryProperties.class, ConsulContextProperties.class})
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
@ConditionalOnMissingBean
public PolarisDiscoveryHandler polarisDiscoveryHandler() {

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

@ -73,5 +73,10 @@ public final class ContextConstant {
* Order of configuration modifier.
*/
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>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>

@ -108,6 +108,12 @@
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>

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

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

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

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

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

@ -22,6 +22,41 @@
<!-- Spring Cloud Tencent dependencies end -->
<!-- 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>
<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>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-client</artifactId>

@ -21,8 +21,11 @@ package com.tencent.cloud.polaris.context.config;
import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.ModifyAddress;
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.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -46,6 +49,18 @@ public class PolarisContextAutoConfiguration {
return SDKContext.initContextByConfig(properties.configuration());
}
@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
@ConditionalOnMissingBean
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
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration
org.springframework.context.ApplicationListener=\
com.tencent.cloud.polaris.context.logging.PolarisLoggingApplicationListener

@ -0,0 +1,74 @@
<?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-loadbalancer</artifactId>
<optional>true</optional>
</dependency>
<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.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</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,109 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.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.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) {
return new EnhancedRestTemplateReporter(consumerAPI);
}
@Bean
public EnhancedRestTemplateModifier polarisRestTemplateBeanPostProcessor(
EnhancedRestTemplateReporter restTemplateResponseErrorHandler) {
return new EnhancedRestTemplateModifier(restTemplateResponseErrorHandler);
}
}
}

@ -15,9 +15,11 @@
* 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 org.springframework.beans.BeansException;
@ -35,14 +37,14 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
*
* @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;
public PolarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) {
this.consumerAPI = consumerAPI;
public EnhancedFeignBeanPostProcessor(List<EnhancedFeignPlugin> enhancedFeignPlugins) {
this.enhancedFeignPlugins = enhancedFeignPlugins;
}
@Override
@ -54,14 +56,14 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
if (isNeedWrap(bean)) {
if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new PolarisLoadBalancerFeignClient(
return new EnhancedLoadBalancerFeignClient(
createPolarisFeignClient(client.getDelegate()),
factory(),
clientFactory());
}
if (bean instanceof FeignBlockingLoadBalancerClient) {
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean;
return new PolarisFeignBlockingLoadBalancerClient(
return new EnhancedFeignBlockingLoadBalancerClient(
createPolarisFeignClient(client.getDelegate()),
factory.getBean(BlockingLoadBalancerClient.class));
}
@ -71,13 +73,13 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
}
private boolean isNeedWrap(Object bean) {
return bean instanceof Client && !(bean instanceof PolarisFeignClient)
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient)
&& !(bean instanceof PolarisLoadBalancerFeignClient);
return bean instanceof Client && !(bean instanceof EnhancedFeignClient)
&& !(bean instanceof EnhancedFeignBlockingLoadBalancerClient)
&& !(bean instanceof EnhancedLoadBalancerFeignClient);
}
private PolarisFeignClient createPolarisFeignClient(Client delegate) {
return new PolarisFeignClient(delegate, consumerAPI);
private EnhancedFeignClient createPolarisFeignClient(Client delegate) {
return new EnhancedFeignClient(delegate, enhancedFeignPlugins);
}
@Override

@ -15,7 +15,7 @@
* 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;
@ -27,9 +27,9 @@ import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalance
*
* @author Haotian Zhang
*/
public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
public class EnhancedFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
public PolarisFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) {
public EnhancedFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) {
super(delegate, loadBalancerClient);
}
}

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

@ -15,7 +15,7 @@
* 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;
@ -28,9 +28,9 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
*
* @author Haotian Zhang
*/
public class PolarisLoadBalancerFeignClient extends LoadBalancerFeignClient {
public class EnhancedLoadBalancerFeignClient extends LoadBalancerFeignClient {
public PolarisLoadBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory,
public EnhancedLoadBalancerFeignClient(Client delegate, CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory) {
super(delegate, lbClientFactory, clientFactory);
}

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

@ -15,15 +15,32 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import org.springframework.web.client.ResponseErrorHandler;
package com.tencent.cloud.rpc.enhancement.feign.plugin;
/**
* Polaris Response Error Handler Definition Of {@link ResponseErrorHandler}.
* Type of EnhancedFeignPlugin.
*
* @author wh 2022/6/21
* @author Haotian Zhang
*/
public enum EnhancedFeignPluginType {
/**
* Pre feign plugin.
*/
PRE,
/**
* Post feign plugin.
*/
POST,
/**
* Exception feign plugin.
*/
public interface PolarisResponseErrorHandler extends ResponseErrorHandler {
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;
}
}

@ -13,71 +13,30 @@
* 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.circuitbreaker.feign;
package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.io.IOException;
import java.net.URI;
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.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Client;
import feign.Request;
import feign.Request.Options;
import feign.Response;
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
*/
public class PolarisFeignClient implements Client {
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");
}
public final class ReporterUtils {
@Override
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 ReporterUtils() {
}
private ServiceCallResult createServiceCallResult(final Request request) {
public static ServiceCallResult createServiceCallResult(final Request request, RetStatus retStatus) {
ServiceCallResult resultRequest = new ServiceCallResult();
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
@ -85,7 +44,7 @@ public class PolarisFeignClient implements Client {
resultRequest.setService(serviceName);
URI uri = URI.create(request.url());
resultRequest.setMethod(uri.getPath());
resultRequest.setRetStatus(RetStatus.RetSuccess);
resultRequest.setRetStatus(retStatus);
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
String sourceService = MetadataContext.LOCAL_SERVICE;
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
@ -96,5 +55,4 @@ public class PolarisFeignClient implements Client {
return resultRequest;
}
}

@ -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.
*/
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.util.Map;
@ -30,17 +30,17 @@ import org.springframework.web.client.RestTemplate;
/**
* Autoconfiguration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced},
* then replace {@link org.springframework.web.client.ResponseErrorHandler}
* with {@link PolarisRestTemplateResponseErrorHandler} .
* with {@link EnhancedRestTemplateReporter} .
*
* @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;
public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) {
this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler;
public EnhancedRestTemplateModifier(EnhancedRestTemplateReporter enhancedRestTemplateReporter) {
this.enhancedRestTemplateReporter = enhancedRestTemplateReporter;
}
@Override
@ -54,7 +54,7 @@ public class PolarisRestTemplateModifier implements ApplicationContextAware, Sma
private void initRestTemplate(String beanName, Object bean) {
if (bean instanceof RestTemplate) {
RestTemplate restTemplate = (RestTemplate) bean;
restTemplate.setErrorHandler(polarisRestTemplateResponseErrorHandler);
restTemplate.setErrorHandler(enhancedRestTemplateReporter);
}
}

@ -15,13 +15,12 @@
* 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.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Objects;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ReflectionUtils;
@ -43,21 +42,16 @@ import org.springframework.web.client.ResponseErrorHandler;
*
* @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 FIELD_NAME = "connection";
private final ConsumerAPI consumerAPI;
private final PolarisResponseErrorHandler polarisResponseErrorHandler;
public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI,
PolarisResponseErrorHandler polarisResponseErrorHandler) {
public EnhancedRestTemplateReporter(ConsumerAPI consumerAPI) {
this.consumerAPI = consumerAPI;
this.polarisResponseErrorHandler = polarisResponseErrorHandler;
}
@Override
@ -66,12 +60,8 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
}
@Override
public void handleError(@NonNull ClientHttpResponse response) throws IOException {
if (Objects.nonNull(polarisResponseErrorHandler)) {
if (polarisResponseErrorHandler.hasError(response)) {
polarisResponseErrorHandler.handleError(response);
}
}
public void handleError(@NonNull ClientHttpResponse response) {
}
@Override
@ -92,9 +82,12 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
}
catch (Exception e) {
LOG.error("Will report response of {} url {}", response, url, e);
resultRequest.setRetStatus(RetStatus.RetFail);
throw e;
}
finally {
LOG.debug("Will report result of {}. URL=[{}]. Response=[{}].", resultRequest.getRetStatus().name(),
url, response);
consumerAPI.updateServiceCallResult(resultRequest);
}
}

@ -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
* 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.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.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.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.context.annotation.Bean;
@ -35,31 +36,33 @@ import org.springframework.web.client.RestTemplate;
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()
.withConfiguration(AutoConfigurations.of(
PolarisRestTemplateAutoConfigurationTester.class,
PolarisContextAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
RpcEnhancementAutoConfiguration.class,
PolarisRestTemplateAutoConfigurationTester.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@Test
public void testInitialization() {
this.contextRunner
.run(context -> {
assertThat(context).hasSingleBean(PolarisRestTemplateModifier.class);
assertThat(context).hasSingleBean(PolarisRestTemplateResponseErrorHandler.class);
public void testDefaultInitialization() {
this.contextRunner.run(context -> {
assertThat(context).hasSingleBean(ConsumerAPI.class);
assertThat(context).hasSingleBean(EnhancedFeignBeanPostProcessor.class);
assertThat(context).hasSingleBean(SuccessPolarisReporter.class);
assertThat(context).hasSingleBean(ExceptionPolarisReporter.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateModifier.class);
assertThat(context).hasSingleBean(EnhancedRestTemplateReporter.class);
});
}
@Configuration
@EnableAutoConfiguration
@AutoConfigureBefore(PolarisCircuitBreakerAutoConfiguration.class)
static class PolarisRestTemplateAutoConfigurationTester {
@Bean

@ -15,9 +15,8 @@
* 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 org.junit.Before;
import org.junit.Test;
@ -36,19 +35,17 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* Test for {@link PolarisFeignBeanPostProcessor}.
* Test for {@link EnhancedFeignBeanPostProcessor}.
*
* @author Haotian Zhang
*/
public class PolarisFeignBeanPostProcessorTest {
public class EnhancedFeignBeanPostProcessorTest {
private PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor;
private EnhancedFeignBeanPostProcessor enhancedFeignBeanPostProcessor;
@Before
public void setUp() {
ConsumerAPI consumerAPI = mock(ConsumerAPI.class);
polarisFeignBeanPostProcessor = new PolarisFeignBeanPostProcessor(consumerAPI);
enhancedFeignBeanPostProcessor = new EnhancedFeignBeanPostProcessor(null);
}
@Test
@ -67,31 +64,31 @@ public class PolarisFeignBeanPostProcessorTest {
}
return null;
}).when(beanFactory).getBean(any(Class.class));
polarisFeignBeanPostProcessor.setBeanFactory(beanFactory);
enhancedFeignBeanPostProcessor.setBeanFactory(beanFactory);
// isNeedWrap(bean) == false
Object bean1 = new Object();
Object bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean1, "bean1");
Object bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean1, "bean1");
assertThat(bean).isNotInstanceOfAny(
PolarisFeignClient.class,
PolarisLoadBalancerFeignClient.class,
PolarisFeignBlockingLoadBalancerClient.class);
EnhancedFeignClient.class,
EnhancedLoadBalancerFeignClient.class,
EnhancedFeignBlockingLoadBalancerClient.class);
// bean instanceOf Client.class
Client bean2 = mock(Client.class);
bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean2, "bean2");
assertThat(bean).isInstanceOf(PolarisFeignClient.class);
bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean2, "bean2");
assertThat(bean).isInstanceOf(EnhancedFeignClient.class);
// bean instanceOf LoadBalancerFeignClient.class
LoadBalancerFeignClient bean3 = mock(LoadBalancerFeignClient.class);
doReturn(mock(Client.class)).when(bean3).getDelegate();
bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean3, "bean3");
assertThat(bean).isInstanceOf(PolarisLoadBalancerFeignClient.class);
bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean3, "bean3");
assertThat(bean).isInstanceOf(EnhancedLoadBalancerFeignClient.class);
// bean instanceOf FeignBlockingLoadBalancerClient.class
FeignBlockingLoadBalancerClient bean4 = mock(FeignBlockingLoadBalancerClient.class);
doReturn(mock(Client.class)).when(bean4).getDelegate();
bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean4, "bean4");
assertThat(bean).isInstanceOf(PolarisFeignBlockingLoadBalancerClient.class);
bean = enhancedFeignBeanPostProcessor.postProcessBeforeInitialization(bean4, "bean4");
assertThat(bean).isInstanceOf(EnhancedFeignBlockingLoadBalancerClient.class);
}
}

@ -15,22 +15,22 @@
* 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.junit.Test;
/**
* Test for {@link PolarisFeignBlockingLoadBalancerClient}.
* Test for {@link EnhancedFeignBlockingLoadBalancerClient}.
*
* @author Haotian Zhang
*/
public class PolarisFeignBlockingLoadBalancerClientTest {
public class EnhancedFeignBlockingLoadBalancerClientTest {
@Test
public void testConstructor() {
try {
new PolarisFeignBlockingLoadBalancerClient(null, null);
new EnhancedFeignBlockingLoadBalancerClient(null, null);
}
catch (Exception e) {
Assertions.fail("Exception encountered.", e);

@ -15,13 +15,16 @@
* 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.util.ArrayList;
import java.util.List;
import com.google.common.collect.Maps;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.rpc.ServiceCallResult;
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.RequestTemplate;
@ -39,23 +42,22 @@ import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
/**
* Test for {@link PolarisFeignClient}.
* Test for {@link EnhancedFeignClient}.
*
* @author Haotian Zhang
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PolarisFeignClientTest.TestApplication.class,
@SpringBootTest(classes = EnhancedFeignClientTest.TestApplication.class,
properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"})
public class PolarisFeignClientTest {
public class EnhancedFeignClientTest {
@Test
public void testConstructor() {
try {
new PolarisFeignClient(null, null);
new EnhancedFeignClient(null, null);
fail("NullPointerException should be thrown.");
}
catch (Throwable e) {
@ -64,17 +66,15 @@ public class PolarisFeignClientTest {
}
try {
new PolarisFeignClient(mock(Client.class), null);
fail("NullPointerException should be thrown.");
new EnhancedFeignClient(mock(Client.class), null);
}
catch (Throwable e) {
assertThat(e).isInstanceOf(NullPointerException.class);
assertThat(e.getMessage()).isEqualTo("CircuitBreakAPI");
fail("Exception encountered.", e);
}
List<EnhancedFeignPlugin> enhancedFeignPlugins = getMockEnhancedFeignPlugins();
try {
assertThat(new PolarisFeignClient(mock(Client.class), mock(ConsumerAPI.class)))
.isInstanceOf(PolarisFeignClient.class);
new EnhancedFeignClient(mock(Client.class), enhancedFeignPlugins);
}
catch (Throwable e) {
fail("Exception encountered.", e);
@ -96,10 +96,6 @@ public class PolarisFeignClientTest {
throw new IOException("Mock exception.");
}).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
Target<Object> target = Target.EmptyTarget.create(Object.class);
@ -107,7 +103,7 @@ public class PolarisFeignClientTest {
RequestTemplate requestTemplate = new RequestTemplate();
requestTemplate.feignTarget(target);
PolarisFeignClient polarisFeignClient = new PolarisFeignClient(delegate, consumerAPI);
EnhancedFeignClient polarisFeignClient = new EnhancedFeignClient(delegate, getMockEnhancedFeignPlugins());
// 200
Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test",
@ -131,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
protected static class TestApplication {

@ -15,22 +15,22 @@
* 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.junit.Test;
/**
* Test for {@link PolarisLoadBalancerFeignClient}.
* Test for {@link EnhancedLoadBalancerFeignClient}.
*
* @author Haotian Zhang
*/
public class PolarisLoadBalancerFeignClientTest {
public class EnhancedLoadBalancerFeignClientTest {
@Test
public void testConstructor() {
try {
new PolarisLoadBalancerFeignClient(null, null, null);
new EnhancedLoadBalancerFeignClient(null, null, null);
}
catch (Exception e) {
Assertions.fail("Exception encountered.", e);

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

@ -15,7 +15,7 @@
* 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.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