From 050c35b522461737bed8b77b9128f030dea0eeb4 Mon Sep 17 00:00:00 2001 From: Haotian Zhang <928016560@qq.com> Date: Fri, 13 Oct 2023 18:13:43 +0800 Subject: [PATCH] feat: add circuit breaker actuator. (#1171) Co-authored-by: wenxuan70 --- CHANGELOG.md | 1 + .../pom.xml | 12 ++ .../PolarisCircuitBreakerEndpoint.java | 90 ++++++++++ ...rcuitBreakerEndpointAutoConfiguration.java | 49 +++++ .../main/resources/META-INF/spring.factories | 3 +- ...tBreakerEndpointAutoConfigurationTest.java | 50 ++++++ .../PolarisCircuitBreakerEndpointTest.java | 84 +++++++++ .../pom.xml | 5 + .../src/main/resources/bootstrap.yml | 6 + .../pom.xml | 5 + .../src/main/resources/bootstrap.yml | 6 + .../pom.xml | 5 + .../src/main/resources/bootstrap.yml | 6 + .../pom.xml | 5 + .../src/main/resources/bootstrap.yml | 6 + .../polaris/context/ServiceRuleManager.java | 29 ++- .../context/ServiceRuleManagerTest.java | 169 ++++++++++++++++++ 17 files changed, 529 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpoint.java create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfiguration.java create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfigurationTest.java create mode 100644 spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointTest.java create mode 100644 spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/ServiceRuleManagerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa436abd..969cf23de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,3 +12,4 @@ - [fix: dynamic routing using cookies.](https://github.com/Tencent/spring-cloud-tencent/pull/1151) - [fix:fix retry loadbalancer not working bug.](https://github.com/Tencent/spring-cloud-tencent/pull/1156) - [fix:fix header validation when using Chinese char.](https://github.com/Tencent/spring-cloud-tencent/pull/1168) +- [feat: add circuit breaker actuator.](https://github.com/Tencent/spring-cloud-tencent/pull/1171) diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml index 58a68a422..230e50a82 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml @@ -96,6 +96,18 @@ + + org.springframework.boot + spring-boot-actuator + true + + + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpoint.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpoint.java new file mode 100644 index 000000000..f9ada5121 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpoint.java @@ -0,0 +1,90 @@ +/* + * 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.circuitbreaker.endpoint; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.annotation.Selector; + +/** + * Endpoint of polaris circuit breaker, include circuit breaker rules. + * + * @author wenxuan70 + */ +@Endpoint(id = "polaris-circuit-breaker") +public class PolarisCircuitBreakerEndpoint { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisCircuitBreakerEndpoint.class); + + private final ServiceRuleManager serviceRuleManager; + + public PolarisCircuitBreakerEndpoint(ServiceRuleManager serviceRuleManager) { + this.serviceRuleManager = serviceRuleManager; + } + + @ReadOperation + public Map circuitBreaker(@Selector String dstService) { + List rules = serviceRuleManager.getServiceCircuitBreakerRule( + MetadataContext.LOCAL_NAMESPACE, + MetadataContext.LOCAL_SERVICE, + dstService + ); + + Map polarisCircuitBreakerInfo = new HashMap<>(); + + polarisCircuitBreakerInfo.put("namespace", MetadataContext.LOCAL_NAMESPACE); + polarisCircuitBreakerInfo.put("service", MetadataContext.LOCAL_SERVICE); + + List circuitBreakerRules = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(rules)) { + for (CircuitBreakerProto.CircuitBreakerRule rule : rules) { + circuitBreakerRules.add(parseCircuitBreakerRule(rule)); + + } + } + polarisCircuitBreakerInfo.put("circuitBreakerRules", circuitBreakerRules); + return polarisCircuitBreakerInfo; + } + + private Object parseCircuitBreakerRule(CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule) { + String ruleJson; + try { + ruleJson = JsonFormat.printer().print(circuitBreakerRule); + } + catch (InvalidProtocolBufferException e) { + LOG.error("rule to Json failed. check rule {}.", circuitBreakerRule, e); + throw new RuntimeException("Json failed.", e); + } + return JacksonUtils.deserialize2Map(ruleJson); + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfiguration.java new file mode 100644 index 000000000..6c9073925 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfiguration.java @@ -0,0 +1,49 @@ +/* + * 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.circuitbreaker.endpoint; + +import com.tencent.cloud.polaris.circuitbreaker.config.ConditionalOnPolarisCircuitBreakerEnabled; +import com.tencent.cloud.polaris.context.ServiceRuleManager; + +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The AutoConfiguration for Polaris CircuitBreaker's Endpoint. + * + * @author wenxuan70 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(Endpoint.class) +@ConditionalOnPolarisCircuitBreakerEnabled +public class PolarisCircuitBreakerEndpointAutoConfiguration { + + @Bean + @ConditionalOnBean(ServiceRuleManager.class) + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public PolarisCircuitBreakerEndpoint polarisCircuitBreakerEndpoint(ServiceRuleManager serviceRuleManager) { + return new PolarisCircuitBreakerEndpoint(serviceRuleManager); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories index a873ed563..07972a65c 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories @@ -2,6 +2,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration,\ com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration,\ com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration,\ - com.tencent.cloud.polaris.circuitbreaker.config.GatewayPolarisCircuitBreakerAutoConfiguration + com.tencent.cloud.polaris.circuitbreaker.config.GatewayPolarisCircuitBreakerAutoConfiguration,\ + com.tencent.cloud.polaris.circuitbreaker.endpoint.PolarisCircuitBreakerEndpointAutoConfiguration org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfigurationTest.java new file mode 100644 index 000000000..48e23ecdd --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointAutoConfigurationTest.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.cloud.polaris.circuitbreaker.endpoint; + +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisCircuitBreakerEndpointAutoConfiguration}. + * + * @author wenxuan70 + */ +public class PolarisCircuitBreakerEndpointAutoConfigurationTest { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + RpcEnhancementAutoConfiguration.class, + PolarisCircuitBreakerAutoConfiguration.class, + PolarisCircuitBreakerEndpointAutoConfiguration.class + )) + .withPropertyValues("management.endpoints.web.exposure.include=polaris-circuit-breaker"); + + @Test + public void testEndpointInitialization() { + contextRunner.run(context -> assertThat(context).hasSingleBean(PolarisCircuitBreakerEndpoint.class)); + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointTest.java new file mode 100644 index 000000000..b253ac96b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/endpoint/PolarisCircuitBreakerEndpointTest.java @@ -0,0 +1,84 @@ +/* + * 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.circuitbreaker.endpoint; + +import java.util.Map; + +import com.google.protobuf.StringValue; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; +import com.tencent.polaris.specification.api.v1.model.ModelProto; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +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.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link PolarisCircuitBreakerEndpoint}. + * + * @author wenxuan70 + */ +@ExtendWith(MockitoExtension.class) +public class PolarisCircuitBreakerEndpointTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withBean(ApplicationContextAwareUtils.class) + .withPropertyValues("spring.cloud.polaris.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.service=" + SERVICE_PROVIDER); + + private ServiceRuleManager serviceRuleManager; + + @BeforeEach + void setUp() { + serviceRuleManager = mock(ServiceRuleManager.class); + when(serviceRuleManager.getServiceCircuitBreakerRule(anyString(), anyString(), anyString())).thenAnswer(invocation -> { + CircuitBreakerProto.CircuitBreakerRule.Builder ruleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder(); + ruleBuilder.setName("test_for_circuit_breaker"); + ruleBuilder.setEnable(true); + ruleBuilder.setLevel(CircuitBreakerProto.Level.METHOD); + CircuitBreakerProto.RuleMatcher.Builder rmBuilder = CircuitBreakerProto.RuleMatcher.newBuilder(); + rmBuilder.setDestination(CircuitBreakerProto.RuleMatcher.DestinationService.newBuilder().setNamespace("default").setService("svc2").setMethod( + ModelProto.MatchString.newBuilder().setValue(StringValue.newBuilder().setValue("*").build()).build()).build()); + rmBuilder.setSource(CircuitBreakerProto.RuleMatcher.SourceService.newBuilder().setNamespace("*").setService("*").build()); + ruleBuilder.setRuleMatcher(rmBuilder.build()); + return CircuitBreakerProto.CircuitBreaker.newBuilder().addRules(ruleBuilder.build()).build().getRulesList(); + }); + } + + @Test + public void testPolarisCircuitBreaker() { + contextRunner.run(context -> { + PolarisCircuitBreakerEndpoint endpoint = new PolarisCircuitBreakerEndpoint(serviceRuleManager); + Map circuitBreakerInfo = endpoint.circuitBreaker("test"); + assertThat(circuitBreakerInfo).isNotNull(); + assertThat(circuitBreakerInfo.get("namespace")).isNotNull(); + assertThat(circuitBreakerInfo.get("service")).isNotNull(); + assertThat(circuitBreakerInfo.get("circuitBreakerRules")).asList().isNotEmpty(); + }); + } +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml index 6f7382e7b..a45ce9336 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/pom.xml @@ -43,6 +43,11 @@ org.springframework.cloud spring-cloud-starter-bootstrap + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml index a7966baf6..0afe7375d 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-feign-example/src/main/resources/bootstrap.yml @@ -22,3 +22,9 @@ logging: root: info com.tencent.cloud: debug +management: + endpoints: + web: + exposure: + include: + - polaris-circuit-breaker diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml index 9064f5b88..833f2e51e 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/pom.xml @@ -53,6 +53,11 @@ org.springframework.cloud spring-cloud-starter-bootstrap + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml index 3ea326fab..1c946649c 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-gateway-example/src/main/resources/bootstrap.yml @@ -57,3 +57,9 @@ logging: com.tencent.polaris.plugins.registry: off com.tencent.cloud: debug +management: + endpoints: + web: + exposure: + include: + - polaris-circuit-breaker diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml index f86372777..942c7587f 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/pom.xml @@ -38,6 +38,11 @@ org.springframework.cloud spring-cloud-starter-bootstrap + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml index 4687ea9da..9cd52b817 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-resttemplate-example/src/main/resources/bootstrap.yml @@ -18,3 +18,9 @@ logging: root: info com.tencent.cloud: debug +management: + endpoints: + web: + exposure: + include: + - polaris-circuit-breaker diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml index 3305c2c58..5e85be2d8 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/pom.xml @@ -38,6 +38,11 @@ org.springframework.cloud spring-cloud-starter-bootstrap + + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml index a6b162df9..9c6f22fde 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-webclient-example/src/main/resources/bootstrap.yml @@ -18,3 +18,9 @@ logging: root: info com.tencent.cloud: debug +management: + endpoints: + web: + exposure: + include: + - polaris-circuit-breaker diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java index 128899491..abf932191 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java @@ -27,13 +27,14 @@ import com.tencent.polaris.api.pojo.ServiceRule; import com.tencent.polaris.api.rpc.GetServiceRuleRequest; import com.tencent.polaris.api.rpc.ServiceRuleResponse; import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * the manager of service governance rules. for example: rate limit rule, router rules. + * the manager of service governance rules. for example: rate limit rule, router rules, circuit breaker rules. * * @author lepdou 2022-05-13 */ @@ -88,6 +89,32 @@ public class ServiceRuleManager { return rules; } + public List getServiceCircuitBreakerRule(String namespace, String sourceService, String dstService) { + LOG.debug("Get service circuit breaker rules with namespace:{} and sourceService:{} and dstService:{}.", namespace, sourceService, dstService); + + List rules = new ArrayList<>(); + + // get source service circuit breaker rules. + ServiceRule sourceServiceRule = getServiceRule(namespace, sourceService, ServiceEventKey.EventType.CIRCUIT_BREAKING); + if (sourceServiceRule != null) { + Object rule = sourceServiceRule.getRule(); + if (rule instanceof CircuitBreakerProto.CircuitBreaker) { + rules.addAll(((CircuitBreakerProto.CircuitBreaker) rule).getRulesList()); + } + } + + // get peer service circuit breaker rules. + ServiceRule dstServiceRule = getServiceRule(namespace, dstService, ServiceEventKey.EventType.CIRCUIT_BREAKING); + if (dstServiceRule != null) { + Object rule = dstServiceRule.getRule(); + if (rule instanceof CircuitBreakerProto.CircuitBreaker) { + rules.addAll(((CircuitBreakerProto.CircuitBreaker) rule).getRulesList()); + } + } + + return rules; + } + private ServiceRule getServiceRule(String namespace, String service, ServiceEventKey.EventType eventType) { GetServiceRuleRequest getServiceRuleRequest = new GetServiceRuleRequest(); getServiceRuleRequest.setRuleType(eventType); diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/ServiceRuleManagerTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/ServiceRuleManagerTest.java new file mode 100644 index 000000000..43c61d3b3 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/ServiceRuleManagerTest.java @@ -0,0 +1,169 @@ +/* + * 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; + +import java.util.List; + +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.rpc.ServiceRuleResponse; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RateLimitProto; +import com.tencent.polaris.specification.api.v1.traffic.manage.RoutingProto; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.when; + +/** + * Test for {@link ServiceRuleManager}. + * + * @author wenxuan70 + */ +@ExtendWith(MockitoExtension.class) +public class ServiceRuleManagerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private SDKContext sdkContext; + + @Mock + private ConsumerAPI consumerAPI; + + @BeforeEach + public void setUp() { + when(sdkContext.getConfig().getGlobal().getAPI().getTimeout()).thenReturn(500L); + } + + @Test + public void testGetServiceCircuitBreakerRule() { + final String testNamespace = "testNamespace"; + final String testSourceService = "testSourceService"; + final String testDstService = "testDstService"; + + CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder() + .addRules(CircuitBreakerProto.CircuitBreakerRule.newBuilder().build()) + .build(); + ServiceRuleByProto serviceRule = new ServiceRuleByProto(circuitBreaker, + "111", + false, + ServiceEventKey.EventType.CIRCUIT_BREAKING); + ServiceRuleResponse serviceRuleResponse = new ServiceRuleResponse(serviceRule); + + // source + when(consumerAPI.getServiceRule( + argThat(request -> request != null + && testNamespace.equals(request.getNamespace()) + && testSourceService.equals(request.getService()) + && ServiceEventKey.EventType.CIRCUIT_BREAKING.equals(request.getRuleType())) + )).thenReturn(serviceRuleResponse); + + ServiceRuleResponse emptyRuleResponse = new ServiceRuleResponse(null); + + // destination + when(consumerAPI.getServiceRule( + argThat(request -> request != null + && testNamespace.equals(request.getNamespace()) + && testDstService.equals(request.getService()) + && ServiceEventKey.EventType.CIRCUIT_BREAKING.equals(request.getRuleType())) + )).thenReturn(emptyRuleResponse); + + ServiceRuleManager serviceRuleManager = new ServiceRuleManager(sdkContext, consumerAPI); + List serviceCircuitBreakerRule = serviceRuleManager.getServiceCircuitBreakerRule(testNamespace, + testSourceService, + testDstService); + + assertThat(serviceCircuitBreakerRule).hasSize(1); + } + + @Test + public void testGetServiceRouterRule() { + final String testNamespace = "testNamespace"; + final String testSourceService = "testSourceService"; + final String testDstService = "testDstService"; + + RoutingProto.Routing routing = RoutingProto.Routing.newBuilder() + .addOutbounds(RoutingProto.Route.newBuilder().build()) + .build(); + ServiceRule serviceRule = new ServiceRuleByProto(routing, + "111", + false, + ServiceEventKey.EventType.ROUTING); + ServiceRuleResponse serviceRuleResponse = new ServiceRuleResponse(serviceRule); + + // source + when(consumerAPI.getServiceRule( + argThat(request -> request != null + && testNamespace.equals(request.getNamespace()) + && testSourceService.equals(request.getService()) + && ServiceEventKey.EventType.ROUTING.equals(request.getRuleType())) + )).thenReturn(serviceRuleResponse); + + + ServiceRuleResponse emptyRuleResponse = new ServiceRuleResponse(null); + + // destination + when(consumerAPI.getServiceRule( + argThat(request -> request != null + && testNamespace.equals(request.getNamespace()) + && testDstService.equals(request.getService()) + && ServiceEventKey.EventType.ROUTING.equals(request.getRuleType())) + )).thenReturn(emptyRuleResponse); + + ServiceRuleManager serviceRuleManager = new ServiceRuleManager(sdkContext, consumerAPI); + List serviceRouterRule = serviceRuleManager.getServiceRouterRule(testNamespace, + testSourceService, + testDstService); + + assertThat(serviceRouterRule).hasSize(1); + } + + @Test + public void testGetServiceRateLimitRule() { + final String testNamespace = "testNamespace"; + final String testService = "testService"; + + RateLimitProto.RateLimit rateLimit = RateLimitProto.RateLimit.getDefaultInstance(); + ServiceRule serviceRule = new ServiceRuleByProto(rateLimit, + "111", + false, + ServiceEventKey.EventType.ROUTING); + ServiceRuleResponse serviceRuleResponse = new ServiceRuleResponse(serviceRule); + + when(consumerAPI.getServiceRule( + argThat(request -> request != null + && testNamespace.equals(request.getNamespace()) + && testService.equals(request.getService()) + && ServiceEventKey.EventType.RATE_LIMITING.equals(request.getRuleType())) + )).thenReturn(serviceRuleResponse); + + ServiceRuleManager serviceRuleManager = new ServiceRuleManager(sdkContext, consumerAPI); + RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(testNamespace, testService); + + assertThat(rateLimitRule).isNotNull(); + } +}