1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00
This commit is contained in:
fangjian0423
2018-11-20 14:14:00 +08:00
parent d88e27e45c
commit 3c33b234c8
20 changed files with 1054 additions and 628 deletions

View File

@@ -17,9 +17,12 @@
package org.springframework.cloud.alibaba.sentinel;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
import org.springframework.core.Ordered;
import com.alibaba.csp.sentinel.config.SentinelConfig;
@@ -29,6 +32,7 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig;
* @author xiaojing
* @author hengyunabc
* @author jiashuai.xie
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX)
public class SentinelProperties {
@@ -49,6 +53,12 @@ public class SentinelProperties {
*/
private String charset = "UTF-8";
/**
* configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
*/
private Map<String, DataSourcePropertiesConfiguration> datasource = new TreeMap<>(
String.CASE_INSENSITIVE_ORDER);
/**
* transport configuration about dashboard and client
*/
@@ -145,6 +155,14 @@ public class SentinelProperties {
this.filter = filter;
}
public Map<String, DataSourcePropertiesConfiguration> getDatasource() {
return datasource;
}
public void setDatasource(Map<String, DataSourcePropertiesConfiguration> datasource) {
this.datasource = datasource;
}
public static class Flow {
/**

View File

@@ -21,13 +21,15 @@ import java.util.Optional;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourcePostProcessor;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
@@ -42,9 +44,13 @@ import com.alibaba.csp.sentinel.init.InitExecutor;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.AppNameUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
/**
* @author xiaojing
* @author jiashuai.xie
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@Configuration
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@@ -135,9 +141,33 @@ public class SentinelAutoConfiguration {
}
@Bean
@ConditionalOnMissingBean
public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() {
return new SentinelDataSourcePostProcessor();
public SentinelDataSourceHandler sentinelDataSourceHandler() {
return new SentinelDataSourceHandler();
}
@Bean("sentinel-json-converter")
public JsonConverter jsonConverter(
@Qualifier("sentinel-object-mapper") ObjectMapper objectMapper) {
return new JsonConverter(objectMapper);
}
@Bean("sentinel-object-mapper")
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
@ConditionalOnClass(XmlMapper.class)
protected static class SentinelXmlConfiguration {
@Bean("sentinel-xml-converter")
public XmlConverter xmlConverter(
@Qualifier("sentinel-xml-mapper") XmlMapper xmlMapper) {
return new XmlConverter(xmlMapper);
}
@Bean("sentinel-xml-mapper")
public XmlMapper xmlMapper() {
return new XmlMapper();
}
}
}

View File

@@ -34,7 +34,7 @@ import org.springframework.web.client.RestTemplate;
/**
* PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate
*
* @author fangjian
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see SentinelProtect
* @see SentinelProtectInterceptor
*/

View File

@@ -0,0 +1,315 @@
package org.springframework.cloud.alibaba.sentinel.custom;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
import org.springframework.context.event.EventListener;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
/**
* Sentinel {@link ReadableDataSource} Handler Handle the configurations of
* 'spring.cloud.sentinel.datasource'
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see SentinelProperties#datasource
* @see JsonConverter
* @see XmlConverter
*/
public class SentinelDataSourceHandler {
private static final Logger logger = LoggerFactory
.getLogger(SentinelDataSourceHandler.class);
private List<String> dataTypeList = Arrays.asList("json", "xml");
private List<Class> rulesList = Arrays.asList(FlowRule.class, DegradeRule.class,
SystemRule.class, AuthorityRule.class);
private List<String> dataSourceBeanNameList = Collections
.synchronizedList(new ArrayList<>());
private final String DATATYPE_FIELD = "dataType";
private final String CUSTOM_DATATYPE = "custom";
private final String CONVERTERCLASS_FIELD = "converterClass";
@Autowired
private SentinelProperties sentinelProperties;
@EventListener(classes = ApplicationStartedEvent.class)
public void buildDataSource(ApplicationStartedEvent event) throws Exception {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event
.getApplicationContext().getAutowireCapableBeanFactory();
sentinelProperties.getDatasource()
.forEach((dataSourceName, dataSourceProperties) -> {
if (dataSourceProperties.getInvalidField().size() != 1) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " multi datasource active and won't loaded: "
+ dataSourceProperties.getInvalidField());
return;
}
Optional.ofNullable(dataSourceProperties.getFile())
.ifPresent(file -> {
try {
dataSourceProperties.getFile().setFile(ResourceUtils
.getFile(StringUtils.trimAllWhitespace(
dataSourceProperties.getFile()
.getFile()))
.getAbsolutePath());
}
catch (IOException e) {
logger.error("[Sentinel Starter] DataSource "
+ dataSourceName + " handle file error: "
+ e.getMessage());
throw new RuntimeException(
"[Sentinel Starter] DataSource "
+ dataSourceName
+ " handle file error: "
+ e.getMessage(),
e);
}
registerBean(beanFactory, file,
dataSourceName + "-sentinel-file-datasource");
});
Optional.ofNullable(dataSourceProperties.getNacos())
.ifPresent(nacos -> {
registerBean(beanFactory, nacos,
dataSourceName + "-sentinel-nacos-datasource");
});
Optional.ofNullable(dataSourceProperties.getApollo())
.ifPresent(apollo -> {
registerBean(beanFactory, apollo,
dataSourceName + "-sentinel-apollo-datasource");
});
Optional.ofNullable(dataSourceProperties.getZk()).ifPresent(zk -> {
registerBean(beanFactory, zk,
dataSourceName + "-sentinel-zk-datasource");
});
});
dataSourceBeanNameList.forEach(beanName -> {
ReadableDataSource dataSource = beanFactory.getBean(beanName,
ReadableDataSource.class);
Object ruleConfig;
try {
logger.info("[Sentinel Starter] DataSource " + beanName
+ " start to loadConfig");
ruleConfig = dataSource.loadConfig();
}
catch (Exception e) {
logger.error("[Sentinel Starter] DataSource " + beanName
+ " loadConfig error: " + e.getMessage(), e);
return;
}
SentinelProperty sentinelProperty = dataSource.getProperty();
Class ruleType = getAndCheckRuleType(ruleConfig, beanName);
if (ruleType != null) {
if (ruleType == FlowRule.class) {
FlowRuleManager.register2Property(sentinelProperty);
}
else if (ruleType == DegradeRule.class) {
DegradeRuleManager.register2Property(sentinelProperty);
}
else if (ruleType == SystemRule.class) {
SystemRuleManager.register2Property(sentinelProperty);
}
else {
AuthorityRuleManager.register2Property(sentinelProperty);
}
}
});
}
private void registerBean(DefaultListableBeanFactory beanFactory,
final AbstractDataSourceProperties dataSourceProperties,
String dataSourceName) {
Map<String, Object> propertyMap = Arrays
.stream(dataSourceProperties.getClass().getDeclaredFields())
.collect(HashMap::new, (m, v) -> {
try {
v.setAccessible(true);
m.put(v.getName(), v.get(dataSourceProperties));
}
catch (IllegalAccessException e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " field: " + v.getName() + " invoke error");
throw new RuntimeException(
"[Sentinel Starter] DataSource " + dataSourceName
+ " field: " + v.getName() + " invoke error",
e);
}
}, HashMap::putAll);
propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass());
propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType());
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
propertyMap.forEach((propertyName, propertyValue) -> {
Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
propertyName);
if (field != null) {
if (DATATYPE_FIELD.equals(propertyName)) {
String dataType = StringUtils
.trimAllWhitespace(propertyValue.toString());
if (CUSTOM_DATATYPE.equals(dataType)) {
try {
if (StringUtils
.isEmpty(dataSourceProperties.getConverterClass())) {
throw new RuntimeException(
"[Sentinel Starter] DataSource " + dataSourceName
+ "dataType is custom, please set converter-class "
+ "property");
}
// construct custom Converter with 'converterClass'
// configuration and register
String customConvertBeanName = "sentinel-"
+ dataSourceProperties.getConverterClass();
if (!beanFactory.containsBean(customConvertBeanName)) {
beanFactory.registerBeanDefinition(customConvertBeanName,
BeanDefinitionBuilder
.genericBeanDefinition(
Class.forName(dataSourceProperties
.getConverterClass()))
.getBeanDefinition());
}
builder.addPropertyReference("converter",
customConvertBeanName);
}
catch (ClassNotFoundException e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " handle "
+ dataSourceProperties.getClass().getSimpleName()
+ " error, class name: "
+ dataSourceProperties.getConverterClass());
throw new RuntimeException(
"[Sentinel Starter] DataSource " + dataSourceName
+ " handle "
+ dataSourceProperties.getClass()
.getSimpleName()
+ " error, class name: "
+ dataSourceProperties.getConverterClass(),
e);
}
}
else {
if (!dataTypeList.contains(StringUtils
.trimAllWhitespace(propertyValue.toString()))) {
throw new RuntimeException("[Sentinel Starter] DataSource "
+ dataSourceName + " dataType: " + propertyValue
+ " is not support now. please using these types: "
+ dataTypeList.toString());
}
// converter type now support xml or json.
// The bean name of these converters wrapped by
// 'sentinel-{converterType}-converter'
builder.addPropertyReference("converter",
"sentinel-" + propertyValue.toString() + "-converter");
}
}
else if (CONVERTERCLASS_FIELD.equals(propertyName)) {
return;
}
else {
// wired properties
builder.addPropertyValue(propertyName, propertyValue);
}
}
});
beanFactory.registerBeanDefinition(dataSourceName, builder.getBeanDefinition());
// init in Spring
beanFactory.getBean(dataSourceName);
dataSourceBeanNameList.add(dataSourceName);
}
private Class getAndCheckRuleType(Object ruleConfig, String dataSourceName) {
if (rulesList.contains(ruleConfig.getClass())) {
logger.info("[Sentinel Starter] DataSource {} load {} {}", dataSourceName, 1,
ruleConfig.getClass().getSimpleName());
return ruleConfig.getClass();
}
else if (ruleConfig instanceof List) {
List convertedRuleList = (List) ruleConfig;
if (CollectionUtils.isEmpty(convertedRuleList)) {
logger.warn("[Sentinel Starter] DataSource {} rule list is empty.",
dataSourceName);
return null;
}
if (convertedRuleList.stream()
.allMatch(rule -> rulesList.contains(rule.getClass()))) {
if (rulesList.contains(convertedRuleList.get(0).getClass())
&& convertedRuleList.stream()
.filter(rule -> rule.getClass() == convertedRuleList
.get(0).getClass())
.toArray().length == convertedRuleList.size()) {
logger.info("[Sentinel Starter] DataSource {} load {} {}",
dataSourceName, convertedRuleList.size(),
convertedRuleList.get(0).getClass().getSimpleName());
return convertedRuleList.get(0).getClass();
}
else {
logger.warn(
"[Sentinel Starter] DataSource {} all rules are not same rule type and it will not be used. "
+ "Rule List: {}",
dataSourceName, convertedRuleList.toString());
}
}
else {
List<Class> classList = (List<Class>) convertedRuleList.stream()
.map(Object::getClass).collect(Collectors.toList());
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " rule class is invalid. Class List: " + classList);
throw new RuntimeException(
"[Sentinel Starter] DataSource " + dataSourceName
+ " rule class is invalid. Class List: " + classList);
}
}
else {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " rule class is invalid. Class: " + ruleConfig.getClass());
throw new RuntimeException("[Sentinel Starter] DataSource " + dataSourceName
+ " rule class is invalid. Class: " + ruleConfig.getClass());
}
return null;
}
public List<String> getDataSourceBeanNameList() {
return dataSourceBeanNameList;
}
}

View File

@@ -20,16 +20,20 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.custom.SentinelDataSourceHandler;
import org.springframework.context.ApplicationContext;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
/**
* Endpoint for Sentinel, contains ans properties and rules
@@ -41,9 +45,15 @@ public class SentinelEndpoint {
@Autowired
private SentinelProperties sentinelProperties;
@Autowired
private SentinelDataSourceHandler dataSourceHandler;
@Autowired
private ApplicationContext applicationContext;
@ReadOperation
public Map<String, Object> invoke() {
Map<String, Object> result = new HashMap<>();
final Map<String, Object> result = new HashMap<>();
List<FlowRule> flowRules = FlowRuleManager.getRules();
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
@@ -52,6 +62,21 @@ public class SentinelEndpoint {
result.put("FlowRules", flowRules);
result.put("DegradeRules", degradeRules);
result.put("SystemRules", systemRules);
result.put("datasources", new HashMap<String, Object>());
dataSourceHandler.getDataSourceBeanNameList().forEach(dataSourceBeanName -> {
ReadableDataSource dataSource = applicationContext.getBean(dataSourceBeanName,
ReadableDataSource.class);
try {
((HashMap) result.get("datasources")).put(dataSourceBeanName,
dataSource.loadConfig());
}
catch (Exception e) {
((HashMap) result.get("datasources")).put(dataSourceBeanName,
"load error: " + e.getMessage());
}
});
return result;
}