add and fix ut

pull/881/head
seanyu 3 years ago
parent ef0c21a414
commit 1f078ebf61

@ -50,10 +50,10 @@ import org.springframework.web.reactive.DispatcherHandler;
public class GatewayPolarisCircuitBreakerAutoConfiguration {
@Bean
@ConditionalOnBean(ReactivePolarisCircuitBreakerFactory.class)
@ConditionalOnBean(ReactiveCircuitBreakerFactory.class)
@ConditionalOnEnabledFilter
public PolarisCircuitBreakerFilterFactory polarisCircuitBreakerFilterFactory(
ReactivePolarisCircuitBreakerFactory reactiveCircuitBreakerFactory,
ReactiveCircuitBreakerFactory reactiveCircuitBreakerFactory,
ObjectProvider<DispatcherHandler> dispatcherHandler,
@Autowired(required = false) ReactiveDiscoveryClient discoveryClient,
@Autowired(required = false) DiscoveryLocatorProperties properties

@ -15,9 +15,12 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.config;
package com.tencent.cloud.polaris.circuitbreaker;
import com.tencent.cloud.polaris.circuitbreaker.common.CircuitBreakerConfigModifier;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
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 com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;

@ -15,8 +15,12 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.config;
package com.tencent.cloud.polaris.circuitbreaker;
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;

@ -0,0 +1,184 @@
/*
* 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;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.EnableFeignClients;
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.test.context.junit4.SpringRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
/**
* @author sean yu
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT,
classes = PolarisCircuitBreakerFeignIntegrationTest.TestConfig.class,
properties = {
"spring.cloud.gateway.enabled=false",
"feign.circuitbreaker.enabled=true",
"spring.cloud.polaris.namespace=default",
"spring.cloud.polaris.service=Test"
})
@DirtiesContext
public class PolarisCircuitBreakerFeignIntegrationTest {
@Autowired
private EchoService echoService;
@Autowired
private FooService fooService;
@Autowired
private BarService barService;
@Autowired
private BazService bazService;
@Before
public void setUp() {
}
@Test
public void contextLoads() throws Exception {
assertThat(echoService).isNotNull();
assertThat(fooService).isNotNull();
}
@Test
public void testFeignClient() {
assertThat(echoService.echo("test")).isEqualTo("echo fallback");
assertThat(fooService.echo("test")).isEqualTo("foo fallback");
assertThatThrownBy(() -> {
barService.bar();
}).isInstanceOf(Exception.class);
assertThatThrownBy(() -> {
bazService.baz();
}).isInstanceOf(Exception.class);
assertThat(fooService.toString()).isNotEqualTo(echoService.toString());
assertThat(fooService.hashCode()).isNotEqualTo(echoService.hashCode());
assertThat(echoService.equals(fooService)).isEqualTo(Boolean.FALSE);
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration({ PolarisCircuitBreakerFeignClientAutoConfiguration.class })
@EnableFeignClients
public static class TestConfig {
@Bean
public EchoServiceFallback echoServiceFallback() {
return new EchoServiceFallback();
}
@Bean
public CustomFallbackFactory customFallbackFactory() {
return new CustomFallbackFactory();
}
}
@FeignClient(value = "test-service", fallback = EchoServiceFallback.class)
public interface EchoService {
@RequestMapping(path = "echo/{str}")
String echo(@RequestParam("str") String param);
}
@FeignClient(value = "foo-service", fallbackFactory = CustomFallbackFactory.class)
public interface FooService {
@GetMapping("echo/{str}")
String echo(@RequestParam("str") String param);
}
@FeignClient("bar-service")
public interface BarService {
@RequestMapping(path = "bar")
String bar();
}
public interface BazService {
@RequestMapping(path = "baz")
String baz();
}
@FeignClient("baz-service")
public interface BazClient extends BazService {
}
public static class EchoServiceFallback implements EchoService {
@Override
public String echo(@RequestParam("str") String param) {
return "echo fallback";
}
}
public static class FooServiceFallback implements FooService {
@Override
public String echo(@RequestParam("str") String param) {
return "foo fallback";
}
}
public static class CustomFallbackFactory
implements FallbackFactory<FooService> {
private FooService fooService = new FooServiceFallback();
@Override
public FooService create(Throwable throwable) {
return fooService;
}
}
}

@ -0,0 +1,79 @@
/*
* 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;
import org.junit.Test;
import org.junit.runner.RunWith;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RunWith(SpringRunner.class)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {
"spring.cloud.gateway.enabled=true",
"spring.cloud.polaris.namespace=default",
"spring.cloud.polaris.service=Test",
"spring.main.web-application-type=reactive"
},
classes = PolarisCircuitBreakerGatewayIntegrationTest.TestApplication.class
)
@ActiveProfiles("test-gateway")
public class PolarisCircuitBreakerGatewayIntegrationTest {
@Autowired
private ApplicationContext context;
@Test
public void contextLoads() throws Exception {
WebTestClient client = WebTestClient.bindToApplicationContext(this.context)
.build();
client.get().uri("/hello/1").exchange().expectStatus().isOk();
}
@Configuration
@EnableAutoConfiguration
public static class TestApplication {
@RestController
static class Controller {
@GetMapping("/hello/{id}")
public Mono<String> hello(@PathVariable Integer id) {
return Mono.just("hello" + id);
}
}
}
}

@ -0,0 +1,128 @@
package com.tencent.cloud.polaris.circuitbreaker;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.polaris.api.config.Configuration;
import com.tencent.polaris.api.pojo.ServiceKey;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.circuitbreak.factory.CircuitBreakAPIFactory;
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.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST;
import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER;
import static com.tencent.polaris.test.common.TestUtils.SERVER_ADDRESS_ENV;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test for {@link PolarisCircuitBreaker} and {@link ReactivePolarisCircuitBreaker} using real mock server.
*
* @author sean yu
*/
public class PolarisCircuitBreakerMockServerTest {
private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;
private NamingServer namingServer;
@Before
public void before() throws IOException {
mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class);
mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace"))
.thenReturn(NAMESPACE_TEST);
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) {
Assert.fail(e.getMessage());
}
ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_CIRCUIT_BREAKER);
CircuitBreakerProto.CircuitBreakerRule.Builder circuitBreakerRuleBuilder = CircuitBreakerProto.CircuitBreakerRule.newBuilder();
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("circuitBreakerRule.json");
String json = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).lines().collect(Collectors.joining(""));
JsonFormat.parser().ignoringUnknownFields().merge(json, circuitBreakerRuleBuilder);
CircuitBreakerProto.CircuitBreakerRule circuitBreakerRule = circuitBreakerRuleBuilder.build();
CircuitBreakerProto.CircuitBreaker circuitBreaker = CircuitBreakerProto.CircuitBreaker.newBuilder().addRules(circuitBreakerRule).build();
namingServer.getNamingService().setCircuitBreaker(serviceKey, circuitBreaker);
}
@After
public void after() {
if (null != namingServer) {
namingServer.terminate();
}
}
@Test
public void testCircuitBreaker() {
Configuration configuration = TestUtils.configWithEnvAddress();
CircuitBreakAPI circuitBreakAPI = CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI);
CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);
// trigger fallback for 5 times
List<String> resList = new ArrayList<>();
for (int i = 0; i < 5; i++){
int finalI = i;
String res = cb.run(() -> {
if (finalI % 2 == 1) {
throw new IllegalArgumentException("invoke failed");
} else {
return "invoke success";
}
}, t -> "fallback");
resList.add(res);
Utils.sleepUninterrupted(1000);
}
assertThat(resList).isEqualTo(Arrays.asList("invoke success","fallback", "fallback", "fallback","fallback"));
// always fallback
ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI);
ReactiveCircuitBreaker rcb = reactivePolarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);
assertThat(Mono.just("foobar").transform(it -> rcb.run(it, t -> Mono.just("fallback")))
.block()).isEqualTo("fallback");
assertThat(Mono.error(new RuntimeException("boom")).transform(it -> rcb.run(it, t -> Mono.just("fallback")))
.block()).isEqualTo("fallback");
assertThat(Flux.just("foobar", "hello world").transform(it -> rcb.run(it, t -> Flux.just("fallback", "fallback")))
.collectList().block())
.isEqualTo(Arrays.asList("fallback", "fallback"));
assertThat(Flux.error(new RuntimeException("boom")).transform(it -> rcb.run(it, t -> Flux.just("fallback")))
.collectList().block())
.isEqualTo(Collections.singletonList("fallback"));
}
}

@ -15,12 +15,12 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.config;
package com.tencent.cloud.polaris.circuitbreaker;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreaker;
import com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerFactory;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerAutoConfiguration;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerFeignClientAutoConfiguration;
import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration;
import com.tencent.cloud.rpc.enhancement.config.RpcEnhancementAutoConfiguration;
import org.junit.After;
@ -76,6 +76,7 @@ public class PolarisCircuitBreakerTest {
@Test
public void run() {
this.contextRunner.run(context -> {
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = context.getBean(PolarisCircuitBreakerFactory.class);
CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);

@ -15,7 +15,7 @@
* specific language governing permissions and limitations under the License.
*/
package com.tencent.cloud.polaris.circuitbreaker.config;
package com.tencent.cloud.polaris.circuitbreaker;
import java.util.Arrays;
import java.util.Collections;
@ -23,6 +23,7 @@ import java.util.Collections;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.circuitbreaker.ReactivePolarisCircuitBreaker;
import com.tencent.cloud.polaris.circuitbreaker.ReactivePolarisCircuitBreakerFactory;
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.After;

@ -0,0 +1,36 @@
spring:
application:
name: GatewayScgService
cloud:
tencent:
plugin:
scg:
staining:
enabled: true
rule-staining:
enabled: true
router:
feature-env:
enabled: true
polaris:
address: grpc://127.0.0.1:8091
namespace: default
enabled: true
gateway:
routes:
- id: cb-test
uri: http://localhost:${server.port}/hello/1
predicates:
- Path=/cb-test/**
filters:
- name: CircuitBreaker
args:
statusCodes: 5**,4**
fallbackUri: forward:/polaris-fallback
logging:
level:
root: info
com.tencent.polaris.discovery.client.flow.RegisterFlow: off
com.tencent.polaris.plugins.registry.memory.CacheObject: off
com.tencent.cloud.polaris.circuitbreaker: debug

@ -0,0 +1,59 @@
{
"@type": "type.googleapis.com/v1.CircuitBreakerRule",
"id": "5f1601f01823474d9be39c0bbb26ab87",
"name": "test",
"namespace": "TestCircuitBreakerRule",
"enable": true,
"revision": "10b120c08706429f8fdc3fb44a53224b",
"ctime": "1754-08-31 06:49:24",
"mtime": "2023-02-21 17:35:31",
"etime": "",
"description": "",
"level": "SERVICE",
"ruleMatcher": {
"source": {
"service": "*",
"namespace": "*"
},
"destination": {
"service": "*",
"namespace": "*",
"method": null
}
},
"errorConditions": [
{
"inputType": "RET_CODE",
"condition": {
"type": "NOT_EQUALS",
"value": "200",
"valueType": "TEXT"
}
}
],
"triggerCondition": [
{
"triggerType": "CONSECUTIVE_ERROR",
"errorCount": 1,
"errorPercent": 1,
"interval": 5,
"minimumRequest": 5
}
],
"maxEjectionPercent": 0,
"recoverCondition": {
"sleepWindow": 60,
"consecutiveSuccess": 3
},
"faultDetectConfig": {
"enable": true
},
"fallbackConfig": {
"enable": false,
"response": {
"code": 0,
"headers": [],
"body": ""
}
}
}
Loading…
Cancel
Save