Skip to content

Commit

Permalink
fix: fix PolarisCircuitBreakerConfiguration not clear when gateway in…
Browse files Browse the repository at this point in the history
…voke by wildcard apis (#1423)

Co-authored-by: andrew shan <45474304+andrewshan@users.noreply.github.com>
  • Loading branch information
fuyuwei01 and andrewshan committed Aug 28, 2024
1 parent 7f34b4a commit dbb1a59
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@
- [fix:fix contract reporting bug when using TSF.](https://github.com/Tencent/spring-cloud-tencent/pull/1375)
- [update: update contract autoconfiguration class](https://github.com/Tencent/spring-cloud-tencent/pull/1387)
- [fix: fix TSF context bootstrap configuration](https://github.com/Tencent/spring-cloud-tencent/pull/1393)

- [fix: fix PolarisCircuitBreakerConfiguration not clear when gateway invoke by wildcard apis](https://github.com/Tencent/spring-cloud-tencent/pull/1423)
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@

package com.tencent.cloud.polaris.circuitbreaker;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.utils.ThreadPoolUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.util.NamedThreadFactory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;

Expand All @@ -33,7 +41,7 @@
* @author seanyu 2023-02-27
*/
public class PolarisCircuitBreakerFactory
extends CircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> {
extends CircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> implements DisposableBean {

private Function<String, PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration> defaultConfiguration =
id -> {
Expand All @@ -50,9 +58,19 @@ public class PolarisCircuitBreakerFactory

private final ConsumerAPI consumerAPI;

public PolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
private final ScheduledExecutorService cleanupService = Executors.newSingleThreadScheduledExecutor(
new NamedThreadFactory("sct-circuitbreaker-cleanup", true));

public PolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
this.circuitBreakAPI = circuitBreakAPI;
this.consumerAPI = consumerAPI;
cleanupService.scheduleWithFixedDelay(
() -> {
getConfigurations().clear();
},
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(),
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), TimeUnit.MILLISECONDS);
}

@Override
Expand All @@ -73,4 +91,9 @@ public void configureDefault(Function<String, PolarisCircuitBreakerConfigBuilder
this.defaultConfiguration = defaultConfiguration;
}

@Override
public void destroy() {
ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{cleanupService});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@

package com.tencent.cloud.polaris.circuitbreaker;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.circuitbreaker.util.PolarisCircuitBreakerUtils;
import com.tencent.polaris.api.core.ConsumerAPI;
import com.tencent.polaris.api.utils.ThreadPoolUtils;
import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI;
import com.tencent.polaris.client.util.NamedThreadFactory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;

Expand All @@ -33,7 +41,7 @@
* @author seanyu 2023-02-27
*/
public class ReactivePolarisCircuitBreakerFactory extends
ReactiveCircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> {
ReactiveCircuitBreakerFactory<PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration, PolarisCircuitBreakerConfigBuilder> implements DisposableBean {

private Function<String, PolarisCircuitBreakerConfigBuilder.PolarisCircuitBreakerConfiguration> defaultConfiguration =
id -> {
Expand All @@ -49,9 +57,19 @@ public class ReactivePolarisCircuitBreakerFactory extends

private final ConsumerAPI consumerAPI;

public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI) {
private final ScheduledExecutorService cleanupService = Executors.newSingleThreadScheduledExecutor(
new NamedThreadFactory("sct-reactive-circuitbreaker-cleanup", true));

public ReactivePolarisCircuitBreakerFactory(CircuitBreakAPI circuitBreakAPI, ConsumerAPI consumerAPI,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
this.circuitBreakAPI = circuitBreakAPI;
this.consumerAPI = consumerAPI;
cleanupService.scheduleWithFixedDelay(
() -> {
getConfigurations().clear();
},
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(),
polarisCircuitBreakerProperties.getConfigurationCleanupInterval(), TimeUnit.MILLISECONDS);
}


Expand All @@ -74,4 +92,8 @@ public void configureDefault(
this.defaultConfiguration = defaultConfiguration;
}

@Override
public void destroy() {
ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{cleanupService});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.ApplicationContext;
Expand All @@ -47,6 +48,7 @@
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnPolarisCircuitBreakerEnabled
@EnableConfigurationProperties(PolarisCircuitBreakerProperties.class)
@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class)
public class PolarisCircuitBreakerAutoConfiguration {

Expand Down Expand Up @@ -76,9 +78,10 @@ public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhanc

@Bean
@ConditionalOnMissingBean(CircuitBreakerFactory.class)
public CircuitBreakerFactory polarisCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager) {
public CircuitBreakerFactory polarisCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
PolarisCircuitBreakerFactory factory = new PolarisCircuitBreakerFactory(
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI());
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI(), polarisCircuitBreakerProperties);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Tencent is pleased to support the open source community by making Spring Cloud Tencent available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package com.tencent.cloud.polaris.circuitbreaker.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Properties of Polaris CircuitBreaker .
*
*/
@ConfigurationProperties("spring.cloud.polaris.circuitbreaker")
public class PolarisCircuitBreakerProperties {

/**
* Whether enable polaris circuit-breaker function.
*/
@Value("${spring.cloud.polaris.circuitbreaker.enabled:#{true}}")
private boolean enabled = true;

/**
* Interval to clean up PolarisCircuitBreakerConfiguration, unit millisecond.
*/
@Value("${spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval:#{300000}}")
private long configurationCleanupInterval = 300000;

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public long getConfigurationCleanupInterval() {
return configurationCleanupInterval;
}

public void setConfigurationCleanupInterval(long configurationCleanupInterval) {
this.configurationCleanupInterval = configurationCleanupInterval;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.circuitbreaker.ReactiveCircuitBreakerFactory;
import org.springframework.context.annotation.Bean;
Expand All @@ -45,6 +46,7 @@
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = {"reactor.core.publisher.Mono", "reactor.core.publisher.Flux"})
@ConditionalOnPolarisCircuitBreakerEnabled
@EnableConfigurationProperties(PolarisCircuitBreakerProperties.class)
@AutoConfigureAfter(RpcEnhancementAutoConfiguration.class)
public class ReactivePolarisCircuitBreakerAutoConfiguration {

Expand All @@ -67,9 +69,10 @@ public ExceptionCircuitBreakerReporter exceptionCircuitBreakerReporter(RpcEnhanc

@Bean
@ConditionalOnMissingBean(ReactiveCircuitBreakerFactory.class)
public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager) {
public ReactiveCircuitBreakerFactory polarisReactiveCircuitBreakerFactory(PolarisSDKContextManager polarisSDKContextManager,
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties) {
ReactivePolarisCircuitBreakerFactory factory = new ReactivePolarisCircuitBreakerFactory(
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI());
polarisSDKContextManager.getCircuitBreakAPI(), polarisSDKContextManager.getConsumerAPI(), polarisCircuitBreakerProperties);
customizers.forEach(customizer -> customizer.customize(factory));
return factory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
{
"name": "spring.cloud.polaris.circuitbreaker.enabled",
"type": "java.lang.Boolean",
"defaultValue": "true"
"defaultValue": "true",
"description": "If polaris circuitbreaker enabled."
},
{
"name": "spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval",
"type": "java.lang.Long",
"defaultValue": "300000",
"description": "Interval to clean up PolarisCircuitBreakerConfiguration, unit millisecond."
}
],
"hints": []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import com.google.protobuf.util.JsonFormat;
import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerProperties;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.api.config.Configuration;
import com.tencent.polaris.api.core.ConsumerAPI;
Expand Down Expand Up @@ -106,9 +107,10 @@ public static void afterAll() {
public void testCircuitBreaker() {
Configuration configuration = TestUtils.configWithEnvAddress();
CircuitBreakAPI circuitBreakAPI = CircuitBreakAPIFactory.createCircuitBreakAPIByConfig(configuration);
ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration);

PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration);
PolarisCircuitBreakerProperties polarisCircuitBreakerProperties = new PolarisCircuitBreakerProperties();
PolarisCircuitBreakerFactory polarisCircuitBreakerFactory = new PolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI, polarisCircuitBreakerProperties);
CircuitBreaker cb = polarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);

// trigger fallback for 5 times
Expand All @@ -129,7 +131,7 @@ public void testCircuitBreaker() {
assertThat(resList).isEqualTo(Arrays.asList("invoke success", "fallback", "fallback", "fallback", "fallback"));

// always fallback
ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI);
ReactivePolarisCircuitBreakerFactory reactivePolarisCircuitBreakerFactory = new ReactivePolarisCircuitBreakerFactory(circuitBreakAPI, consumerAPI, polarisCircuitBreakerProperties);
ReactiveCircuitBreaker rcb = reactivePolarisCircuitBreakerFactory.create(SERVICE_CIRCUIT_BREAKER);

assertThat(Mono.just("foobar").transform(it -> rcb.run(it, t -> Mono.just("fallback")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@
package com.tencent.cloud.polaris.circuitbreaker;


import java.lang.reflect.Method;
import java.util.Map;

import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ReflectionUtils;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
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 com.tencent.polaris.client.util.Utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -56,7 +62,8 @@ public class PolarisCircuitBreakerTest {
LoadBalancerAutoConfiguration.class,
PolarisCircuitBreakerFeignClientAutoConfiguration.class,
PolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval=5000");

private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;

Expand Down Expand Up @@ -92,6 +99,18 @@ public void run() {
throw new RuntimeException("boom");
}, t -> "fallback")).isEqualTo("fallback");

Method getConfigurationsMethod = ReflectionUtils.findMethod(PolarisCircuitBreakerFactory.class,
"getConfigurations");
Assertions.assertNotNull(getConfigurationsMethod);
ReflectionUtils.makeAccessible(getConfigurationsMethod);
Map<?, ?> values = (Map<?, ?>) ReflectionUtils.invokeMethod(getConfigurationsMethod, polarisCircuitBreakerFactory);
Assertions.assertNotNull(values);

Assertions.assertEquals(1, values.size());

Utils.sleepUninterrupted(10 * 1000);

Assertions.assertEquals(0, values.size());
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@

package com.tencent.cloud.polaris.circuitbreaker;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import com.tencent.cloud.common.util.ApplicationContextAwareUtils;
import com.tencent.cloud.common.util.ReflectionUtils;
import com.tencent.cloud.polaris.circuitbreaker.common.PolarisCircuitBreakerConfigBuilder;
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.client.util.Utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -58,7 +63,8 @@ public class ReactivePolarisCircuitBreakerTest {
RpcEnhancementAutoConfiguration.class,
LoadBalancerAutoConfiguration.class,
ReactivePolarisCircuitBreakerAutoConfiguration.class))
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true");
.withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true")
.withPropertyValues("spring.cloud.polaris.circuitbreaker.configuration-cleanup-interval=5000");

private static MockedStatic<ApplicationContextAwareUtils> mockedApplicationContextAwareUtils;

Expand Down Expand Up @@ -97,6 +103,18 @@ public void run() {

assertThat(Flux.error(new RuntimeException("boom")).transform(it -> cb.run(it, t -> Flux.just("fallback")))
.collectList().block()).isEqualTo(Collections.singletonList("fallback"));

Method getConfigurationsMethod = ReflectionUtils.findMethod(PolarisCircuitBreakerFactory.class,
"getConfigurations");
Assertions.assertNotNull(getConfigurationsMethod);
ReflectionUtils.makeAccessible(getConfigurationsMethod);
Map<?, ?> values = (Map<?, ?>) ReflectionUtils.invokeMethod(getConfigurationsMethod, polarisCircuitBreakerFactory);
Assertions.assertNotNull(values);
Assertions.assertEquals(1, values.size());

Utils.sleepUninterrupted(10 * 1000);

Assertions.assertEquals(0, values.size());
});
}

Expand Down
Loading

0 comments on commit dbb1a59

Please sign in to comment.