Refactoring: Refactor Circuitbreaker ut.

pull/1080/head
Shanyou Yu (Sean Yu) 1 year ago committed by Haotian Zhang
parent 1d6825cabe
commit 0673da7818

@ -4,3 +4,4 @@
- feature: support reactive discovery client health indicator.
- feature: Enhance default configuration to support `application*.yaml` and `bootstrap*.yaml`.
- feat:adapt for nacos instance.
- Refactoring: Refactor Circuitbreaker ut.

@ -33,7 +33,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER;
public class ExceptionCircuitBreakerReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin {
@ -65,7 +66,8 @@ public class ExceptionCircuitBreakerReporter extends AbstractPolarisReporterAdap
}
EnhancedRequestContext request = context.getRequest();
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance());
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance())
.orElse(new DefaultServiceInstance());
ResourceStat resourceStat = createInstanceResourceStat(
serviceInstance.getServiceId(),
@ -78,7 +80,8 @@ public class ExceptionCircuitBreakerReporter extends AbstractPolarisReporterAdap
);
LOG.debug("Will report CircuitBreaker ResourceStat of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.",
resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), context.getThrowable().getMessage(), context.getDelay());
resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl()
.getPath(), context.getThrowable().getMessage(), context.getDelay());
circuitBreakAPI.report(resourceStat);
@ -92,6 +95,6 @@ public class ExceptionCircuitBreakerReporter extends AbstractPolarisReporterAdap
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 2;
return CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER;
}
}

@ -35,7 +35,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER;
public class SuccessCircuitBreakerReporter extends AbstractPolarisReporterAdapter implements EnhancedPlugin {
@ -68,7 +69,8 @@ public class SuccessCircuitBreakerReporter extends AbstractPolarisReporterAdapte
}
EnhancedRequestContext request = context.getRequest();
EnhancedResponseContext response = context.getResponse();
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance());
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance())
.orElse(new DefaultServiceInstance());
ResourceStat resourceStat = createInstanceResourceStat(
serviceInstance.getServiceId(),
@ -81,7 +83,8 @@ public class SuccessCircuitBreakerReporter extends AbstractPolarisReporterAdapte
);
LOG.debug("Will report CircuitBreaker ResourceStat of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.",
resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), response.getHttpStatus(), context.getDelay());
resourceStat.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl()
.getPath(), response.getHttpStatus(), context.getDelay());
circuitBreakAPI.report(resourceStat);
}
@ -94,6 +97,6 @@ public class SuccessCircuitBreakerReporter extends AbstractPolarisReporterAdapte
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 2;
return CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER;
}
}

@ -77,13 +77,9 @@ public class PolarisCircuitBreakerMockServerTest {
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service"))
.thenReturn(SERVICE_CIRCUIT_BREAKER);
try {
namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
}
catch (IOException e) {
namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
}
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_CIRCUIT_BREAKER);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();

@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(MockitoExtension.class)
public class PolarisCircuitBreakerTest {
private static final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
private static ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
PolarisContextAutoConfiguration.class,
RpcEnhancementAutoConfiguration.class,
@ -76,7 +76,7 @@ public class PolarisCircuitBreakerTest {
@Test
public void run() {
contextRunner.run(context -> {
this.contextRunner.run(context -> {
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = context.getBean(PolarisCircuitBreakerFactory.class);
CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);
@ -95,4 +95,7 @@ public class PolarisCircuitBreakerTest {
});
}
}

@ -15,15 +15,12 @@
* 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.common.CircuitBreakerConfigModifier;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisCircuitBreakerNameResolver;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -39,12 +36,13 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Haotian Zhang
*/
public class CircuitBreakerPolarisAutoConfigurationTest {
public class PolarisCircuitBreakerAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
PolarisContextAutoConfiguration.class,
RpcEnhancementAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
PolarisCircuitBreakerFeignClientAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
@ -62,7 +60,6 @@ public class CircuitBreakerPolarisAutoConfigurationTest {
assertThat(context).hasSingleBean(PolarisCircuitBreakerAutoConfiguration.class);
assertThat(context).hasSingleBean(CircuitBreakerFactory.class);
assertThat(context).hasSingleBean(CircuitBreakerConfigModifier.class);
assertThat(context).hasSingleBean(CircuitBreakAPI.class);
assertThat(context).hasSingleBean(PolarisCircuitBreakerNameResolver.class);
});
}
@ -73,7 +70,6 @@ public class CircuitBreakerPolarisAutoConfigurationTest {
assertThat(context).hasSingleBean(ReactivePolarisCircuitBreakerAutoConfiguration.class);
assertThat(context).hasSingleBean(ReactiveCircuitBreakerFactory.class);
assertThat(context).hasSingleBean(CircuitBreakerConfigModifier.class);
assertThat(context).hasSingleBean(CircuitBreakAPI.class);
});
}

@ -15,12 +15,8 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker;
package com.tencent.cloud.polaris.circuitbreaker.config;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.ReactivePolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import org.junit.jupiter.api.Test;

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker;
package com.tencent.cloud.polaris.circuitbreaker.feign;
import java.io.BufferedReader;
@ -26,7 +26,6 @@ import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.polaris.api.pojo.ServiceKey;
@ -36,10 +35,10 @@ import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
@ -50,7 +49,7 @@ import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -75,27 +74,22 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
"spring.cloud.polaris.service=" + SERVICE_CIRCUIT_BREAKER,
"spring.cloud.polaris.address=grpc://127.0.0.1:10081"
})
@DirtiesContext
public class PolarisCircuitBreakerFeignIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
private EchoService echoService;
@Autowired
private FooService fooService;
@Autowired
private BarService barService;
@Autowired
private BazService bazService;
@AfterAll
public static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void contextLoads() throws Exception {
assertThat(echoService).isNotNull();
@ -122,6 +116,7 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
}
@FeignClient(value = TEST_SERVICE_NAME, contextId = "1", fallback = EchoServiceFallback.class)
@Primary
public interface EchoService {
@RequestMapping(path = "echo/{str}")
@ -174,18 +169,18 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
}
@Bean
public CircuitBreakAPI circuitBreakAPI() throws InvalidProtocolBufferException {
try {
namingServer = NamingServer.startNamingServer(10081);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
}
catch (IOException e) {
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
}
@Bean
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerMockServerTest.class.getClassLoader()
InputStream inputStream = PolarisCircuitBreakerFeignIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
@ -194,6 +189,11 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@ -205,7 +205,7 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
@Override
public String echo(@RequestParam("str") String param) throws InvocationTargetException {
if (param == null) {
throw new InvocationTargetException(new Exception(), "test InvocationTargetException");
throw new InvocationTargetException(new Exception());
}
return "echo fallback";
}
@ -233,4 +233,18 @@ public class PolarisCircuitBreakerFeignIntegrationTest {
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -0,0 +1,125 @@
/*
* 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.feign;
import java.lang.reflect.Method;
import com.tencent.cloud.common.metadata.MetadataContext;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ReflectionUtils;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementReporterProperties;
import feign.Target;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
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;
/**
* @author sean yu
*/
@ExtendWith(MockitoExtension.class)
public class PolarisCircuitBreakerNameResolverTest {
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
@BeforeAll
static void beforeAll() {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString()))
.thenReturn("unit-test");
ApplicationContext applicationContext = mock(ApplicationContext.class);
RpcEnhancementReporterProperties reporterProperties = mock(RpcEnhancementReporterProperties.class);
doReturn(reporterProperties)
.when(applicationContext).getBean(RpcEnhancementReporterProperties.class);
mockedApplicationContextAwareUtils.when(ApplicationContextAwareUtils::getApplicationContext)
.thenReturn(applicationContext);
}
@AfterAll
static void afterAll() {
mockedApplicationContextAwareUtils.close();
}
@BeforeEach
void setUp() {
MetadataContext.LOCAL_NAMESPACE = NAMESPACE_TEST;
MetadataContext.LOCAL_SERVICE = SERVICE_PROVIDER;
}
@Test
public void test() {
Target target = mock(Target.class);
doReturn("test-svc").when(target).name();
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerNameResolverTest.class, "mockRequestMapping");
PolarisCircuitBreakerNameResolver resolver = new PolarisCircuitBreakerNameResolver();
String polarisCircuitBreakerName = resolver.resolveCircuitBreakerName("test", target, method);
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc");
}
@Test
public void test2() {
Target target = mock(Target.class);
doReturn("test-svc").when(target).name();
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerNameResolverTest.class, "mockRequestMapping2");
PolarisCircuitBreakerNameResolver resolver = new PolarisCircuitBreakerNameResolver();
String polarisCircuitBreakerName = resolver.resolveCircuitBreakerName("test", target, method);
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/");
}
@Test
public void test3() {
Target target = mock(Target.class);
doReturn("test-svc").when(target).name();
Method method = ReflectionUtils.findMethod(PolarisCircuitBreakerNameResolverTest.class, "mockRequestMapping3");
PolarisCircuitBreakerNameResolver resolver = new PolarisCircuitBreakerNameResolver();
String polarisCircuitBreakerName = resolver.resolveCircuitBreakerName("test", target, method);
assertThat(polarisCircuitBreakerName).isEqualTo("Test#test-svc#/");
}
@RequestMapping
public void mockRequestMapping() {
}
@RequestMapping(path = "/")
public void mockRequestMapping2() {
}
@RequestMapping("/")
public void mockRequestMapping3() {
}
}

@ -0,0 +1,89 @@
/*
* 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.feign;
import feign.Feign;
import feign.RequestLine;
import feign.Target;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.PolarisFeignCircuitBreakerTargeter;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
/**
* PolarisFeignCircuitBreakerTargeterTest.
*
* @author sean yu
*/
@ExtendWith(MockitoExtension.class)
public class PolarisFeignCircuitBreakerTargeterTest {
@Mock
CircuitBreakerFactory circuitBreakerFactory;
@Mock
PolarisCircuitBreakerNameResolver circuitBreakerNameResolver;
@Test
public void testTarget() {
PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver);
targeter.target(new FeignClientFactoryBean(), new Feign.Builder(), new FeignContext(), new Target.HardCodedTarget<>(TestApi.class, "/test"));
}
@Test
public void testTarget2() {
PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver);
FeignClientFactoryBean feignClientFactoryBean = mock(FeignClientFactoryBean.class);
doReturn(TestApi.class).when(feignClientFactoryBean).getFallback();
doReturn("test").when(feignClientFactoryBean).getName();
FeignContext feignClientFactory = mock(FeignContext.class);
doReturn(null).when(feignClientFactory).getInstance("test", TestApi.class);
assertThatThrownBy(() -> {
targeter.target(feignClientFactoryBean, new PolarisFeignCircuitBreaker.Builder(), feignClientFactory, new Target.HardCodedTarget<>(TestApi.class, "/test"));
}).isInstanceOf(IllegalStateException.class);
}
@Test
public void testTarget3() {
PolarisFeignCircuitBreakerTargeter targeter = new PolarisFeignCircuitBreakerTargeter(circuitBreakerFactory, circuitBreakerNameResolver);
FeignClientFactoryBean feignClientFactoryBean = mock(FeignClientFactoryBean.class);
doReturn(void.class).when(feignClientFactoryBean).getFallback();
doReturn(TestApi.class).when(feignClientFactoryBean).getFallbackFactory();
doReturn("test").when(feignClientFactoryBean).getName();
FeignContext feignClientFactory = mock(FeignContext.class);
doReturn(Object.class).when(feignClientFactory).getInstance("test", TestApi.class);
assertThatThrownBy(() -> {
targeter.target(feignClientFactoryBean, new PolarisFeignCircuitBreaker.Builder(), feignClientFactory, new Target.HardCodedTarget<>(TestApi.class, "/test"));
}).isInstanceOf(IllegalStateException.class);
}
interface TestApi {
@RequestLine("GET /test")
void test();
}
}

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker;
package com.tencent.cloud.polaris.circuitbreaker.gateway;
import java.io.BufferedReader;
@ -28,9 +28,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.gateway.PolarisCircuitBreakerFilterFactory;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
@ -38,11 +36,11 @@ import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
@ -75,8 +73,7 @@ import static org.assertj.core.api.Assertions.assertThat;
"spring.cloud.gateway.enabled=true",
"spring.cloud.polaris.namespace=" + NAMESPACE_TEST,
"spring.cloud.polaris.service=" + SERVICE_CIRCUIT_BREAKER,
"spring.main.web-application-type=reactive",
"spring.cloud.polaris.address=grpc://127.0.0.1:10081"
"spring.main.web-application-type=reactive"
},
classes = PolarisCircuitBreakerGatewayIntegrationTest.TestApplication.class
)
@ -85,19 +82,13 @@ import static org.assertj.core.api.Assertions.assertThat;
public class PolarisCircuitBreakerGatewayIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
private WebTestClient webClient;
@Autowired
private ApplicationContext applicationContext;
@AfterAll
public static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void fallback() throws Exception {
SpringCloudCircuitBreakerFilterFactory.Config config = new SpringCloudCircuitBreakerFilterFactory.Config();
@ -160,18 +151,18 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
public static class TestApplication {
@Bean
public CircuitBreakAPI circuitBreakAPI() throws InvalidProtocolBufferException {
try {
namingServer = NamingServer.startNamingServer(10081);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
}
catch (IOException e) {
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
}
@Bean
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerMockServerTest.class.getClassLoader()
InputStream inputStream = PolarisCircuitBreakerGatewayIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
@ -180,6 +171,11 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) throws IOException {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@ -227,4 +223,18 @@ public class PolarisCircuitBreakerGatewayIntegrationTest {
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker;
package com.tencent.cloud.polaris.circuitbreaker.resttemplate;
import java.io.BufferedReader;
import java.io.IOException;
@ -27,12 +27,8 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.stream.Collectors;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerFallback;
import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreakerHttpResponse;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
@ -40,10 +36,10 @@ import com.tencent.polaris.client.util.Utils;
import com.tencent.polaris.specification.api.v1.fault.tolerance.CircuitBreakerProto;
import com.tencent.polaris.test.common.TestUtils;
import com.tencent.polaris.test.mock.discovery.NamingServer;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@ -57,7 +53,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
@ -82,47 +77,44 @@ import static org.springframework.test.web.client.response.MockRestResponseCreat
*/
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = PolarisCircuitBreakerIntegrationTest.TestConfig.class,
classes = PolarisCircuitBreakerRestTemplateIntegrationTest.TestConfig.class,
properties = {
"spring.cloud.gateway.enabled=false",
"feign.circuitbreaker.enabled=true",
"spring.cloud.polaris.namespace=" + NAMESPACE_TEST,
"spring.cloud.polaris.service=" + SERVICE_CIRCUIT_BREAKER,
"spring.cloud.polaris.address=grpc://127.0.0.1:10081"
"spring.cloud.polaris.service=" + SERVICE_CIRCUIT_BREAKER
})
@DirtiesContext
public class PolarisCircuitBreakerIntegrationTest {
public class PolarisCircuitBreakerRestTemplateIntegrationTest {
private static final String TEST_SERVICE_NAME = "test-service-callee";
private static NamingServer namingServer;
@Autowired
@Qualifier("defaultRestTemplate")
private RestTemplate defaultRestTemplate;
@Autowired
@Qualifier("restTemplateFallbackFromPolaris")
private RestTemplate restTemplateFallbackFromPolaris;
@Autowired
@Qualifier("restTemplateFallbackFromCode")
private RestTemplate restTemplateFallbackFromCode;
@Autowired
@Qualifier("restTemplateFallbackFromCode2")
private RestTemplate restTemplateFallbackFromCode2;
@Autowired
@Qualifier("restTemplateFallbackFromCode3")
private RestTemplate restTemplateFallbackFromCode3;
@Autowired
@Qualifier("restTemplateFallbackFromCode4")
private RestTemplate restTemplateFallbackFromCode4;
@Autowired
private ApplicationContext applicationContext;
@AfterAll
public static void afterAll() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void testRestTemplate() throws URISyntaxException {
@ -168,14 +160,14 @@ public class PolarisCircuitBreakerIntegrationTest {
public static class TestConfig {
@Bean
@PolarisCircuitBreaker(fallback = "fallback")
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallback = "fallback")
public RestTemplate defaultRestTemplate() {
return new RestTemplate();
}
@Bean
@LoadBalanced
@PolarisCircuitBreaker
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker
public RestTemplate restTemplateFallbackFromPolaris() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate();
@ -185,7 +177,7 @@ public class PolarisCircuitBreakerIntegrationTest {
@Bean
@LoadBalanced
@PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback.class)
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback.class)
public RestTemplate restTemplateFallbackFromCode() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate();
@ -195,7 +187,7 @@ public class PolarisCircuitBreakerIntegrationTest {
@Bean
@LoadBalanced
@PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback2.class)
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback2.class)
public RestTemplate restTemplateFallbackFromCode2() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate();
@ -205,7 +197,7 @@ public class PolarisCircuitBreakerIntegrationTest {
@Bean
@LoadBalanced
@PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback3.class)
@com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisCircuitBreaker(fallbackClass = CustomPolarisCircuitBreakerFallback3.class)
public RestTemplate restTemplateFallbackFromCode3() {
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory("http://" + TEST_SERVICE_NAME);
RestTemplate restTemplate = new RestTemplate();
@ -239,18 +231,12 @@ public class PolarisCircuitBreakerIntegrationTest {
}
@Bean
public CircuitBreakAPI circuitBreakAPI() throws InvalidProtocolBufferException {
try {
namingServer = NamingServer.startNamingServer(10081);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
}
catch (IOException e) {
}
public NamingServer namingServer() throws IOException {
NamingServer namingServer = NamingServer.startNamingServer(-1);
System.setProperty(SERVER_ADDRESS_ENV, String.format("127.0.0.1:%d", namingServer.getPort()));
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, TEST_SERVICE_NAME);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = PolarisCircuitBreakerMockServerTest.class.getClassLoader()
InputStream inputStream = PolarisCircuitBreakerRestTemplateIntegrationTest.class.getClassLoader()
.getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining(""));
@ -259,6 +245,16 @@ public class PolarisCircuitBreakerIntegrationTest {
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder()
.addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
return namingServer;
}
@Bean
public PreDestroy preDestroy(NamingServer namingServer) {
return new PreDestroy(namingServer);
}
@Bean
public CircuitBreakAPI circuitBreakAPI(NamingServer namingServer) {
com.tencent.polaris.api.config.Configuration configuration = TestUtils.configWithEnvAddress();
return CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
}
@ -312,4 +308,18 @@ public class PolarisCircuitBreakerIntegrationTest {
}
}
public static class PreDestroy implements DisposableBean {
private final NamingServer namingServer;
public PreDestroy(NamingServer namingServer) {
this.namingServer = namingServer;
}
@Override
public void destroy() throws Exception {
namingServer.terminate();
}
}
}

@ -35,6 +35,7 @@ import org.mockito.Mockito;
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;
public class PolarisCircuitBreakerUtilsTests {
@ -68,4 +69,11 @@ public class PolarisCircuitBreakerUtilsTests {
PolarisCircuitBreakerUtils.reportStatus(consumerAPI, conf, new CallAbortedException("mock", new CircuitBreakerStatus.FallbackInfo(0, new HashMap<>(), "")));
}
@Test
public void testResolveCircuitBreakerId() {
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc")).isEqualTo(new String[]{NAMESPACE_TEST, "test_svc", ""});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_svc#test_path")).isEqualTo(new String[]{NAMESPACE_TEST, "test_svc", "test_path"});
assertThat(PolarisCircuitBreakerUtils.resolveCircuitBreakerId("test_ns#test_svc#test_path")).isEqualTo(new String[]{"test_ns", "test_svc", "test_path"});
}
}

@ -73,7 +73,7 @@
<revision>1.12.0-Hoxton.SR12</revision>
<!-- Dependencies -->
<polaris.version>1.12.11</polaris.version>
<polaris.version>1.13.0</polaris.version>
<guava.version>31.0.1-jre</guava.version>
<logback.version>1.2.11</logback.version>
<mocktio.version>4.5.1</mocktio.version>

@ -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.plugin;
import org.springframework.core.Ordered;
/**
* PluginOrderConstant.
*
* @author sean yu
*/
public class PluginOrderConstant {
public static class ClientPluginOrder {
/**
* order for
* {@link com.tencent.cloud.rpc.enhancement.plugin.reporter.SuccessPolarisReporter}
* and
* {@link com.tencent.cloud.rpc.enhancement.plugin.reporter.ExceptionPolarisReporter}.
*/
public static final int CONSUMER_REPORTER_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 1;
/**
* order for
* {@link com.tencent.cloud.polaris.circuitbreaker.reporter.SuccessCircuitBreakerReporter}
* and
* {@link com.tencent.cloud.polaris.circuitbreaker.reporter.ExceptionCircuitBreakerReporter}.
*/
public static final int CIRCUIT_BREAKER_REPORTER_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 2;
/**
* order for
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.client.AssemblyClientPreHook}
* and
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.client.AssemblyClientPostHook}
* and
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.client.AssemblyClientExceptionHook}.
*/
public static final int ASSEMBLY_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 3;
}
public static class ServerPluginOrder {
/**
* order for
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.server.AssemblyServerPreHook}
* and
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.server.AssemblyServerPostHook}
* and
* {@link com.tencent.cloud.rpc.enhancement.plugin.assembly.server.AssemblyServerExceptionHook}.
*/
public static final int ASSEMBLY_PLUGIN_ORDER = Ordered.HIGHEST_PRECEDENCE + 1;
}
}

@ -34,7 +34,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CONSUMER_REPORTER_PLUGIN_ORDER;
/**
* Polaris reporter when feign call fails.
@ -71,7 +72,8 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp
}
EnhancedRequestContext request = context.getRequest();
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance());
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance())
.orElse(new DefaultServiceInstance());
ServiceCallResult resultRequest = createServiceCallResult(
serviceInstance.getServiceId(),
@ -86,7 +88,8 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp
);
LOG.debug("Will report ServiceCallResult of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.",
resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), context.getThrowable().getMessage(), context.getDelay());
resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl()
.getPath(), context.getThrowable().getMessage(), context.getDelay());
consumerAPI.updateServiceCallResult(resultRequest);
@ -100,7 +103,7 @@ public class ExceptionPolarisReporter extends AbstractPolarisReporterAdapter imp
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
return CONSUMER_REPORTER_PLUGIN_ORDER;
}
}

@ -34,7 +34,8 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.Ordered;
import static com.tencent.cloud.rpc.enhancement.plugin.PluginOrderConstant.ClientPluginOrder.CONSUMER_REPORTER_PLUGIN_ORDER;
/**
* Polaris reporter when feign call is successful.
@ -72,7 +73,8 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
EnhancedRequestContext request = context.getRequest();
EnhancedResponseContext response = context.getResponse();
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance()).orElse(new DefaultServiceInstance());
ServiceInstance serviceInstance = Optional.ofNullable(context.getServiceInstance())
.orElse(new DefaultServiceInstance());
ServiceCallResult resultRequest = createServiceCallResult(
serviceInstance.getServiceId(),
@ -87,7 +89,8 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
);
LOG.debug("Will report ServiceCallResult of {}. Request=[{} {}]. Response=[{}]. Delay=[{}]ms.",
resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl().getPath(), response.getHttpStatus(), context.getDelay());
resultRequest.getRetStatus().name(), request.getHttpMethod().name(), request.getUrl()
.getPath(), response.getHttpStatus(), context.getDelay());
consumerAPI.updateServiceCallResult(resultRequest);
@ -101,6 +104,6 @@ public class SuccessPolarisReporter extends AbstractPolarisReporterAdapter imple
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
return CONSUMER_REPORTER_PLUGIN_ORDER;
}
}

Loading…
Cancel
Save