fix: Keep rules in dataSource

保留数据源中配置
pull/2342/head
Freeman Lau 3 years ago
parent 06b2c25717
commit 77b3d7922b

@ -64,6 +64,13 @@
</dependency>
<!-- support Feign client configuration end -->
<!-- datasource rules -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>

@ -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

@ -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();
}
}

Loading…
Cancel
Save