|
|
|
@ -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<RefreshScopeRefreshedEvent> {
|
|
|
|
|
public class CircuitBreakerRuleChangeListener implements ApplicationContextAware,
|
|
|
|
|
ApplicationListener<RefreshScopeRefreshedEvent>, 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<DegradeRule> rules = Arrays.stream(dataSourceNames)
|
|
|
|
|
.map(this::getDegradeRules)
|
|
|
|
|
.flatMap(Collection::stream)
|
|
|
|
|
.distinct()
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
DegradeRuleManager.loadRules(rules);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<DegradeRule> getDegradeRules(String dataSourceName) {
|
|
|
|
|
AbstractDataSource ds = applicationContext.getBean(dataSourceName, AbstractDataSource.class);
|
|
|
|
|
try {
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
List<DegradeRule> result = (List<DegradeRule>) 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
|
|
|
|
|