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) - [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: 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) - [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) - [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) - [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) - [Optimize: Specification apollo code reference notes](https://github.com/Tencent/spring-cloud-tencent/pull/442)

@ -29,20 +29,23 @@ Spring Cloud Tencent提供的能力包括但不限于
- 服务注册和发现 - 服务注册和发现
- 动态配置管理 - 动态配置管理
- 服务治理 - 服务治理
- 服务限流 - 服务限流
- 服务熔断 - 服务熔断
- 服务路由 - 服务路由
- ... - ...
- 标签透传 - 标签透传
## 体验环境 ## 体验环境
- 管控台地址: http://14.116.241.63:8080/ - 管控台地址: http://14.116.241.63:8080/
- 账号polaris - 账号polaris
- 密码polaris - 密码polaris
- 控制面地址: `grpc://183.47.111.80:8091` - 控制面地址: `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"> <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 所有组件都已上传到 Maven 中央仓库,只需要引入依赖即可。
> 注意:
>
> Spring Cloud Tencent 的版本列表可以查看 [Spring Cloud Tencent 版本管理](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86) 。
例如: 例如:
```` xml ```` xml
@ -88,8 +95,8 @@ Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要
- [Spring Cloud Tencent 标签传递](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) - [Spring Cloud Tencent 标签传递](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97)
- ### 开发文档 - ### 开发文档
- [项目概览](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88) - [项目概览](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88)
- [参与共建](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA) - [参与共建](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA)
## 交流群 ## 交流群
@ -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%" /> <img src="https://user-images.githubusercontent.com/24446200/169198148-d4cc3494-3485-4515-9897-c8cb5504f706.png" width="30%" height="30%" />
## 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)
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 ## 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 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 Github home page](https://github.com/polarismesh/polaris)
- [Polaris official website](https://polarismesh.cn/) - [Polaris official website](https://polarismesh.cn/)
@ -28,21 +30,22 @@ The capabilities provided by Spring Cloud Tencent include but are not limited to
- Service registration and discovery - Service registration and discovery
- Dynamic configuration management - Dynamic configuration management
- Service Governance - Service Governance
- Service rate limit - Service rate limit
- Service circuit breaker - Service circuit breaker
- Service routing - Service routing
- ... - ...
- Label transparent transmission - Label transparent transmission
## Demo Environment ## Demo Environment
- Console Address : http://14.116.241.63:8080/ - Console Address : http://14.116.241.63:8080/
- Username: polaris - Username: polaris
- Password: polaris - Password: polaris
- Server Address: `grpc://183.47.111.80:8091` - 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. The example addresses under `spring-cloud-tencent-example` all point to the experience service
If you only experience Spring Cloud Tencent, you can run any example directly with one click. 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 ## Screenshots
@ -50,7 +53,12 @@ If you only experience Spring Cloud Tencent, you can run any example directly wi
## Use Guide ## 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: For example:
@ -79,7 +87,7 @@ For example:
```` ````
- ### Quick Start - ### Quick Start
- [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) - [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)
- [Spring Cloud Tencent Discovery](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Discovery-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) - [Spring Cloud Tencent Discovery](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Discovery-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3)
- [Spring Cloud Tencent Config](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Config-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) - [Spring Cloud Tencent Config](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Config-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3)
@ -89,8 +97,8 @@ For example:
- [Spring Cloud Tencent Metadata Transfer](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) - [Spring Cloud Tencent Metadata Transfer](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97)
- ### Development Documentation - ### Development Documentation
- [Project Structure Overview](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88) - [Project Structure Overview](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88)
- [Participate in co-construction](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA) - [Participate in co-construction](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA)
## Chat Group ## Chat Group
@ -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%" /> <img src="https://user-images.githubusercontent.com/24446200/169198148-d4cc3494-3485-4515-9897-c8cb5504f706.png" width="30%" height="30%" />
## 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)
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 ## Stargazers over time

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

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

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

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

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

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

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

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

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

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

@ -1,63 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0" 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"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>spring-cloud-tencent</artifactId> <artifactId>spring-cloud-tencent</artifactId>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<version>${revision}</version> <version>${revision}</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-tencent-coverage</artifactId> <artifactId>spring-cloud-tencent-coverage</artifactId>
<name>Spring Cloud Tencent Coverage</name> <name>Spring Cloud Tencent Coverage</name>
<packaging>pom</packaging> <packaging>pom</packaging>
<properties> <properties>
<maven.deploy.skip>true</maven.deploy.skip> <maven.deploy.skip>true</maven.deploy.skip>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-commons</artifactId> <artifactId>spring-cloud-tencent-commons</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-context</artifactId> <artifactId>spring-cloud-tencent-polaris-context</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-rpc-enhancement</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId> <artifactId>spring-cloud-tencent-polaris-loadbalancer</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-ratelimit</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-circuitbreaker</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-circuitbreaker</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId> <artifactId>spring-cloud-starter-tencent-metadata-transfer</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-starter-tencent-polaris-router</artifactId> <artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
@ -73,26 +78,26 @@
<groupId>com.tencent.cloud</groupId> <groupId>com.tencent.cloud</groupId>
<artifactId>spring-cloud-tencent-gateway-plugin</artifactId> <artifactId>spring-cloud-tencent-gateway-plugin</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<executions> <executions>
<execution> <execution>
<id>report-aggregate</id> <id>report-aggregate</id>
<phase>test</phase> <phase>test</phase>
<goals> <goals>
<goal>report-aggregate</goal> <goal>report-aggregate</goal>
</goals> </goals>
<configuration> <configuration>
<outputDirectory>${basedir}/../target/site/jacoco</outputDirectory> <outputDirectory>${basedir}/../target/site/jacoco</outputDirectory>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

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

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

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

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

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

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

@ -22,6 +22,41 @@
<!-- Spring Cloud Tencent dependencies end --> <!-- Spring Cloud Tencent dependencies end -->
<!-- Polaris dependencies start --> <!-- Polaris dependencies start -->
<dependency>
<groupId>com.tencent.polaris</groupId>
<artifactId>polaris-discovery-factory</artifactId>
<exclusions>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-rule</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-nearby</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-metadata</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-canary</artifactId>
</exclusion>
<exclusion>
<groupId>com.tencent.polaris</groupId>
<artifactId>router-set</artifactId>
</exclusion>
<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> <dependency>
<groupId>com.tencent.polaris</groupId> <groupId>com.tencent.polaris</groupId>
<artifactId>polaris-client</artifactId> <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.ConditionalOnPolarisEnabled;
import com.tencent.cloud.polaris.context.ModifyAddress; import com.tencent.cloud.polaris.context.ModifyAddress;
import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.cloud.polaris.context.ServiceRuleManager;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.core.ProviderAPI;
import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.exception.PolarisException;
import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.factory.api.DiscoveryAPIFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
@ -46,6 +49,18 @@ public class PolarisContextAutoConfiguration {
return SDKContext.initContextByConfig(properties.configuration()); 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 @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public ModifyAddress polarisConfigModifier() { public ModifyAddress polarisConfigModifier() {

@ -0,0 +1,56 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.context.logging;
import com.tencent.polaris.logging.PolarisLogging;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.ResolvableType;
/**
* Reload of Polaris logging configuration.
*
* @author Haotian Zhang
*/
public class PolarisLoggingApplicationListener implements GenericApplicationListener {
private static final int ORDER = LoggingApplicationListener.DEFAULT_ORDER + 2;
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
Class<?> type = resolvableType.getRawClass();
if (type == null) {
return false;
}
return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(type)
|| ApplicationFailedEvent.class.isAssignableFrom(type);
}
@Override
public int getOrder() {
return ORDER;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
PolarisLogging.getInstance().loadConfiguration();
}
}

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

@ -0,0 +1,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. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import com.tencent.polaris.api.core.ConsumerAPI; import java.util.List;
import com.tencent.cloud.rpc.enhancement.feign.plugin.EnhancedFeignPlugin;
import feign.Client; import feign.Client;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -35,14 +37,14 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { public class EnhancedFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private final ConsumerAPI consumerAPI; private final List<EnhancedFeignPlugin> enhancedFeignPlugins;
private BeanFactory factory; private BeanFactory factory;
public PolarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) { public EnhancedFeignBeanPostProcessor(List<EnhancedFeignPlugin> enhancedFeignPlugins) {
this.consumerAPI = consumerAPI; this.enhancedFeignPlugins = enhancedFeignPlugins;
} }
@Override @Override
@ -54,14 +56,14 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
if (isNeedWrap(bean)) { if (isNeedWrap(bean)) {
if (bean instanceof LoadBalancerFeignClient) { if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean); LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new PolarisLoadBalancerFeignClient( return new EnhancedLoadBalancerFeignClient(
createPolarisFeignClient(client.getDelegate()), createPolarisFeignClient(client.getDelegate()),
factory(), factory(),
clientFactory()); clientFactory());
} }
if (bean instanceof FeignBlockingLoadBalancerClient) { if (bean instanceof FeignBlockingLoadBalancerClient) {
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean; FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) bean;
return new PolarisFeignBlockingLoadBalancerClient( return new EnhancedFeignBlockingLoadBalancerClient(
createPolarisFeignClient(client.getDelegate()), createPolarisFeignClient(client.getDelegate()),
factory.getBean(BlockingLoadBalancerClient.class)); factory.getBean(BlockingLoadBalancerClient.class));
} }
@ -71,13 +73,13 @@ public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFac
} }
private boolean isNeedWrap(Object bean) { private boolean isNeedWrap(Object bean) {
return bean instanceof Client && !(bean instanceof PolarisFeignClient) return bean instanceof Client && !(bean instanceof EnhancedFeignClient)
&& !(bean instanceof PolarisFeignBlockingLoadBalancerClient) && !(bean instanceof EnhancedFeignBlockingLoadBalancerClient)
&& !(bean instanceof PolarisLoadBalancerFeignClient); && !(bean instanceof EnhancedLoadBalancerFeignClient);
} }
private PolarisFeignClient createPolarisFeignClient(Client delegate) { private EnhancedFeignClient createPolarisFeignClient(Client delegate) {
return new PolarisFeignClient(delegate, consumerAPI); return new EnhancedFeignClient(delegate, enhancedFeignPlugins);
} }
@Override @Override

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import feign.Client; import feign.Client;
@ -27,9 +27,9 @@ import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalance
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient { public class EnhancedFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient {
public PolarisFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) { public EnhancedFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) {
super(delegate, 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. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign;
import feign.Client; import feign.Client;
@ -28,9 +28,9 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
* *
* @author Haotian Zhang * @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) { SpringClientFactory clientFactory) {
super(delegate, lbClientFactory, 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. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.feign.plugin;
import org.springframework.web.client.ResponseErrorHandler;
/** /**
* Polaris Response Error Handler Definition Of {@link ResponseErrorHandler}. * Type of EnhancedFeignPlugin.
* *
* @author wh 2022/6/21 * @author Haotian Zhang
*/ */
public interface PolarisResponseErrorHandler extends ResponseErrorHandler { public enum EnhancedFeignPluginType {
/**
* Pre feign plugin.
*/
PRE,
/**
* Post feign plugin.
*/
POST,
/**
* Exception feign plugin.
*/
EXCEPTION,
/**
* Finally feign plugin.
*/
FINALLY
} }

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

@ -13,71 +13,30 @@
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the * CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*
*/ */
package com.tencent.cloud.polaris.circuitbreaker.feign; package com.tencent.cloud.rpc.enhancement.feign.plugin.reporter;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.pojo.RetStatus;
import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.api.rpc.ServiceCallResult; import com.tencent.polaris.api.rpc.ServiceCallResult;
import feign.Client;
import feign.Request; import feign.Request;
import feign.Request.Options;
import feign.Response;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static feign.Util.checkNotNull;
/** /**
* Wrap for {@link Client}. * Util for polaris reporter.
* *
* @author Haotian Zhang * @author Haotian Zhang
*/ */
public class PolarisFeignClient implements Client { public final class ReporterUtils {
private static final Logger LOG = LoggerFactory.getLogger(PolarisFeignClient.class);
private final Client delegate; private ReporterUtils() {
private final ConsumerAPI consumerAPI;
public PolarisFeignClient(Client target, ConsumerAPI consumerAPI) {
this.delegate = checkNotNull(target, "target");
this.consumerAPI = checkNotNull(consumerAPI, "CircuitBreakAPI");
} }
@Override public static ServiceCallResult createServiceCallResult(final Request request, RetStatus retStatus) {
public Response execute(Request request, Options options) throws IOException {
final ServiceCallResult resultRequest = createServiceCallResult(request);
try {
Response response = delegate.execute(request, options);
// HTTP code greater than 500 is an exception
if (response.status() > 500) {
resultRequest.setRetStatus(RetStatus.RetFail);
}
LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].",
resultRequest.getRetStatus().name(), request, response);
return response;
}
catch (IOException origin) {
resultRequest.setRetStatus(RetStatus.RetFail);
LOG.debug("Will report result of {}. Request=[{}].", resultRequest.getRetStatus().name(), request, origin);
throw origin;
}
finally {
consumerAPI.updateServiceCallResult(resultRequest);
}
}
private ServiceCallResult createServiceCallResult(final Request request) {
ServiceCallResult resultRequest = new ServiceCallResult(); ServiceCallResult resultRequest = new ServiceCallResult();
resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE);
@ -85,7 +44,7 @@ public class PolarisFeignClient implements Client {
resultRequest.setService(serviceName); resultRequest.setService(serviceName);
URI uri = URI.create(request.url()); URI uri = URI.create(request.url());
resultRequest.setMethod(uri.getPath()); resultRequest.setMethod(uri.getPath());
resultRequest.setRetStatus(RetStatus.RetSuccess); resultRequest.setRetStatus(retStatus);
String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; String sourceNamespace = MetadataContext.LOCAL_NAMESPACE;
String sourceService = MetadataContext.LOCAL_SERVICE; String sourceService = MetadataContext.LOCAL_SERVICE;
if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) {
@ -96,5 +55,4 @@ public class PolarisFeignClient implements Client {
return resultRequest; 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. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.util.Map; import java.util.Map;
@ -28,19 +28,19 @@ import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
/** /**
* Auto configuration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced}, * Autoconfiguration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced},
* then replace {@link org.springframework.web.client.ResponseErrorHandler} * then replace {@link org.springframework.web.client.ResponseErrorHandler}
* with {@link PolarisRestTemplateResponseErrorHandler} . * with {@link EnhancedRestTemplateReporter} .
* *
* @author wh 2022/6/21 * @author wh 2022/6/21
*/ */
public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton { public class EnhancedRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton {
private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler; private final EnhancedRestTemplateReporter enhancedRestTemplateReporter;
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) { public EnhancedRestTemplateModifier(EnhancedRestTemplateReporter enhancedRestTemplateReporter) {
this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler; this.enhancedRestTemplateReporter = enhancedRestTemplateReporter;
} }
@Override @Override
@ -54,7 +54,7 @@ public class PolarisRestTemplateModifier implements ApplicationContextAware, Sma
private void initRestTemplate(String beanName, Object bean) { private void initRestTemplate(String beanName, Object bean) {
if (bean instanceof RestTemplate) { if (bean instanceof RestTemplate) {
RestTemplate restTemplate = (RestTemplate) bean; RestTemplate restTemplate = (RestTemplate) bean;
restTemplate.setErrorHandler(polarisRestTemplateResponseErrorHandler); restTemplate.setErrorHandler(enhancedRestTemplateReporter);
} }
} }

@ -15,13 +15,12 @@
* specific language governing permissions and limitations under the License. * specific language governing permissions and limitations under the License.
*/ */
package com.tencent.cloud.polaris.circuitbreaker.resttemplate; package com.tencent.cloud.rpc.enhancement.resttemplate;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.Objects;
import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ReflectionUtils; import com.tencent.cloud.common.util.ReflectionUtils;
@ -43,21 +42,16 @@ import org.springframework.web.client.ResponseErrorHandler;
* *
* @author wh 2022/6/21 * @author wh 2022/6/21
*/ */
public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler { public class EnhancedRestTemplateReporter implements ResponseErrorHandler {
private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class); private static final Logger LOG = LoggerFactory.getLogger(EnhancedRestTemplateReporter.class);
private static final String FIELD_NAME = "connection"; private static final String FIELD_NAME = "connection";
private final ConsumerAPI consumerAPI; private final ConsumerAPI consumerAPI;
private final PolarisResponseErrorHandler polarisResponseErrorHandler; public EnhancedRestTemplateReporter(ConsumerAPI consumerAPI) {
public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI,
PolarisResponseErrorHandler polarisResponseErrorHandler) {
this.consumerAPI = consumerAPI; this.consumerAPI = consumerAPI;
this.polarisResponseErrorHandler = polarisResponseErrorHandler;
} }
@Override @Override
@ -66,12 +60,8 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
} }
@Override @Override
public void handleError(@NonNull ClientHttpResponse response) throws IOException { public void handleError(@NonNull ClientHttpResponse response) {
if (Objects.nonNull(polarisResponseErrorHandler)) {
if (polarisResponseErrorHandler.hasError(response)) {
polarisResponseErrorHandler.handleError(response);
}
}
} }
@Override @Override
@ -92,9 +82,12 @@ public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHan
} }
catch (Exception e) { catch (Exception e) {
LOG.error("Will report response of {} url {}", response, url, e); LOG.error("Will report response of {} url {}", response, url, e);
resultRequest.setRetStatus(RetStatus.RetFail);
throw e; throw e;
} }
finally { finally {
LOG.debug("Will report result of {}. URL=[{}]. Response=[{}].", resultRequest.getRetStatus().name(),
url, response);
consumerAPI.updateServiceCallResult(resultRequest); consumerAPI.updateServiceCallResult(resultRequest);
} }
} }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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