diff --git a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/pom.xml b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/pom.xml
index ca0cbd4bc..7368ff741 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/pom.xml
+++ b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/pom.xml
@@ -64,6 +64,13 @@
+
+
+ com.alibaba.csp
+ sentinel-datasource-extension
+ provided
+
+
org.springframework.boot
spring-boot-configuration-processor
diff --git a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/CircuitBreakerRuleChangeListener.java b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/CircuitBreakerRuleChangeListener.java
index 1fed9ab35..8f9b99c3f 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/CircuitBreakerRuleChangeListener.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/CircuitBreakerRuleChangeListener.java
@@ -2,20 +2,24 @@ package com.alibaba.cloud.circuitbreaker.sentinel.feign;
import java.lang.reflect.Method;
import java.util.*;
+import java.util.stream.Collectors;
import com.alibaba.cloud.circuitbreaker.sentinel.SentinelConfigBuilder;
import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.client.circuitbreaker.AbstractCircuitBreakerFactory;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
+import org.springframework.util.CollectionUtils;
/**
* Sentinel circuit breaker config change listener.
@@ -23,11 +27,15 @@ import org.springframework.context.ApplicationListener;
* @author freeman
* @date 2022/1/10 12:23
*/
-public class CircuitBreakerRuleChangeListener
- implements ApplicationContextAware, ApplicationListener {
+public class CircuitBreakerRuleChangeListener implements ApplicationContextAware,
+ ApplicationListener, SmartInitializingSingleton {
private static final Logger LOGGER = LoggerFactory.getLogger(CircuitBreakerRuleChangeListener.class);
private SentinelFeignClientProperties properties;
+ /**
+ * properties backup, prevent rules from being updated every time the container is refreshed
+ */
+ private SentinelFeignClientProperties propertiesBackup;
private AbstractCircuitBreakerFactory circuitBreakerFactory;
private ApplicationContext applicationContext;
@@ -35,12 +43,20 @@ public class CircuitBreakerRuleChangeListener
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
ensureReady();
+ // No need to update the rules
+ if (Objects.equals(properties, propertiesBackup)) {
+ return;
+ }
+
clearRules();
// rebind
+ configureRulesInDataSource();
configureDefault();
configureCustom();
+ updateBackup();
+
LOGGER.info("sentinel circuit beaker rules refreshed.");
}
@@ -49,6 +65,12 @@ public class CircuitBreakerRuleChangeListener
this.applicationContext = applicationContext;
}
+ @Override
+ public void afterSingletonsInstantiated() {
+ this.propertiesBackup = applicationContext
+ .getBean(SentinelFeignClientProperties.class).copy();
+ }
+
private void ensureReady() {
// Do not inject these beans directly,
// as it will cause the bean to be initialized prematurely,
@@ -85,7 +107,45 @@ public class CircuitBreakerRuleChangeListener
}
private void clearSentinelDegradeManager() {
- DegradeRuleManager.loadRules(Collections.emptyList());
+ DegradeRuleManager.loadRules(new ArrayList<>());
+ }
+
+ private void updateBackup() {
+ this.propertiesBackup = this.properties.copy();
+ }
+
+ private void configureRulesInDataSource() {
+ // TODO allow feign client rules to be configured in the data source?
+ // How to distinguish feign client rules from ordinary rules ?
+
+ // Temporarily does not support configuring feign client rules in the data source.
+ // But need to keep the normal degrade rules.
+ String[] dataSourceNames = applicationContext.getBeanNamesForType(AbstractDataSource.class);
+
+ List rules = Arrays.stream(dataSourceNames)
+ .map(this::getDegradeRules)
+ .flatMap(Collection::stream)
+ .distinct()
+ .collect(Collectors.toList());
+
+ DegradeRuleManager.loadRules(rules);
+ }
+
+ private List getDegradeRules(String dataSourceName) {
+ AbstractDataSource ds = applicationContext.getBean(dataSourceName, AbstractDataSource.class);
+ try {
+ @SuppressWarnings("unchecked")
+ List result = (List) ds.loadConfig();
+ // be careful with generic wipes
+ if (!CollectionUtils.isEmpty(result)
+ && DegradeRule.class.isAssignableFrom(result.get(0).getClass())) {
+ return result;
+ }
+ }
+ catch (Exception ignored) {
+ // illegal config, ignore
+ }
+ return new ArrayList<>();
}
// static method
diff --git a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/SentinelFeignClientProperties.java b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/SentinelFeignClientProperties.java
index 5977942ef..cb93eb7db 100644
--- a/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/SentinelFeignClientProperties.java
+++ b/spring-cloud-alibaba-starters/spring-cloud-circuitbreaker-sentinel/src/main/java/com/alibaba/cloud/circuitbreaker/sentinel/feign/SentinelFeignClientProperties.java
@@ -3,9 +3,12 @@ package com.alibaba.cloud.circuitbreaker.sentinel.feign;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@@ -52,4 +55,33 @@ public class SentinelFeignClientProperties {
this.rules = rules;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SentinelFeignClientProperties that = (SentinelFeignClientProperties) o;
+ return refreshRules == that.refreshRules
+ && Objects.equals(defaultRule, that.defaultRule)
+ && Objects.equals(rules, that.rules);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(defaultRule, refreshRules, rules);
+ }
+
+ public SentinelFeignClientProperties copy() {
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ String json = objectMapper.writeValueAsString(this);
+ return objectMapper.readValue(json, this.getClass());
+ } catch (JsonProcessingException ignored) {
+ }
+ return new SentinelFeignClientProperties();
+ }
+
}