pull/898/head
fangjian0423 6 years ago
parent b54765d4b9
commit dcbabd70fb

@ -41,8 +41,27 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<scope>provided</scope>
</dependency>
<!--spring boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
@ -63,7 +82,6 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -1,152 +0,0 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
/**
* {@link ReadableDataSource} Loader
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class DataSourceLoader {
private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class);
private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties";
private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX
+ PROPERTIES_RESOURCE_LOCATION;
private final static ConcurrentMap<String, Class<? extends ReadableDataSource>> dataSourceClassesCache
= new ConcurrentHashMap<String, Class<? extends ReadableDataSource>>(
4);
static void loadAllDataSourceClassesCache() {
Map<String, Class<? extends ReadableDataSource>> dataSourceClassesMap = loadAllDataSourceClassesCache(
ALL_PROPERTIES_RESOURCES_LOCATION);
dataSourceClassesCache.putAll(dataSourceClassesMap);
}
static Map<String, Class<? extends ReadableDataSource>> loadAllDataSourceClassesCache(
String resourcesLocation) {
Map<String, Class<? extends ReadableDataSource>> dataSourcesMap
= new HashMap<String, Class<? extends ReadableDataSource>>(
4);
ClassLoader classLoader = DataSourceLoader.class.getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources(resourcesLocation);
for (Resource resource : resources) {
if (resource.exists()) {
Properties properties = PropertiesLoaderUtils
.loadProperties(resource);
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String type = (String)entry.getKey();
String className = (String)entry.getValue();
if (!ClassUtils.isPresent(className, classLoader)) {
if (logger.isDebugEnabled()) {
logger.debug(
"Sentinel DataSource implementation [ type : "
+ type + ": , class : " + className
+ " , url : " + resource.getURL()
+ "] was not present in current classpath , "
+ "thus loading will be ignored , please add dependency if required !");
}
continue;
}
Assert.isTrue(!dataSourcesMap.containsKey(type),
"The duplicated type[" + type
+ "] of SentinelDataSource were found in "
+ "resource [" + resource.getURL() + "]");
Class<?> dataSourceClass = ClassUtils.resolveClassName(className,
classLoader);
Assert.isAssignable(ReadableDataSource.class, dataSourceClass);
dataSourcesMap.put(type,
(Class<? extends ReadableDataSource>)dataSourceClass);
if (logger.isDebugEnabled()) {
logger.debug("Sentinel DataSource implementation [ type : "
+ type + ": , class : " + className
+ "] was loaded.");
}
}
}
}
} catch (IOException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return dataSourcesMap;
}
public static Class<? extends ReadableDataSource> loadClass(String type)
throws IllegalArgumentException {
Class<? extends ReadableDataSource> dataSourceClass = dataSourceClassesCache.get(type);
if (dataSourceClass == null) {
if (dataSourceClassesCache.isEmpty()) {
loadAllDataSourceClassesCache();
dataSourceClass = dataSourceClassesCache.get(type);
}
}
if (dataSourceClass == null) {
throw new IllegalArgumentException(
"Sentinel DataSource implementation [ type : " + type
+ " ] can't be found!");
}
return dataSourceClass;
}
}

@ -1,272 +0,0 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.csp.sentinel.datasource.Converter;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.sentinel.datasource.annotation.SentinelDataSource;
import org.springframework.cloud.alibaba.sentinel.datasource.util.PropertySourcesUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
/**
* {@link SentinelDataSource @SentinelDataSource} Post Processor
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see ReadableDataSource
* @see SentinelDataSource
*/
public class SentinelDataSourcePostProcessor
extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(SentinelDataSourcePostProcessor.class);
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ConfigurableEnvironment environment;
private final Map<String, List<SentinelDataSourceField>> dataSourceFieldCache = new ConcurrentHashMap<>(
64);
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
Class<?> beanType, String beanName) {
// find all fields using by @SentinelDataSource annotation
ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
SentinelDataSource annotation = getAnnotation(field,
SentinelDataSource.class);
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn(
"@SentinelDataSource annotation is not supported on static fields: "
+ field);
}
return;
}
if (dataSourceFieldCache.containsKey(beanName)) {
dataSourceFieldCache.get(beanName)
.add(new SentinelDataSourceField(annotation, field));
} else {
List<SentinelDataSourceField> list = new ArrayList<>();
list.add(new SentinelDataSourceField(annotation, field));
dataSourceFieldCache.put(beanName, list);
}
}
}
});
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeanCreationException {
if (dataSourceFieldCache.containsKey(beanName)) {
List<SentinelDataSourceField> sentinelDataSourceFields = dataSourceFieldCache
.get(beanName);
sentinelDataSourceFields.forEach(sentinelDataSourceField -> {
try {
// construct DataSource field annotated by @SentinelDataSource
Field field = sentinelDataSourceField.getField();
ReflectionUtils.makeAccessible(field);
String dataSourceBeanName = constructDataSource(
sentinelDataSourceField.getSentinelDataSource());
field.set(bean, applicationContext.getBean(dataSourceBeanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
});
}
return pvs;
}
private String constructDataSource(SentinelDataSource annotation) {
String prefix = annotation.value();
if (StringUtils.isEmpty(prefix)) {
prefix = SentinelDataSourceConstants.PROPERTY_DATASOURCE_PREFIX;
}
Map<String, Object> propertyMap = PropertySourcesUtils
.getSubProperties(environment.getPropertySources(), prefix);
String alias = propertyMap.get("type").toString();
Class dataSourceClass = DataSourceLoader.loadClass(alias);
String beanName = StringUtils.isEmpty(annotation.name())
? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix
: annotation.name();
if (applicationContext.containsBean(beanName)) {
return beanName;
}
Class targetClass = null;
// if alias exists in SentinelDataSourceRegistry, wired properties into
// FactoryBean
if (SentinelDataSourceRegistry.checkFactoryBean(alias)) {
targetClass = SentinelDataSourceRegistry.getFactoryBean(alias);
} else {
// if alias not exists in SentinelDataSourceRegistry, wired properties into
// raw class
targetClass = dataSourceClass;
}
registerDataSource(beanName, targetClass, propertyMap);
return beanName;
}
private void registerDataSource(String beanName, Class targetClass,
Map<String, Object> propertyMap) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(targetClass);
for (String propertyName : propertyMap.keySet()) {
Field field = ReflectionUtils.findField(targetClass, propertyName);
if (field != null) {
if (field.getType().isAssignableFrom(Converter.class)) {
// Converter get from ApplicationContext
builder.addPropertyReference(propertyName,
propertyMap.get(propertyName).toString());
} else {
// wired properties
builder.addPropertyValue(propertyName, propertyMap.get(propertyName));
}
}
}
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)applicationContext
.getAutowireCapableBeanFactory();
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
@EventListener(classes = ApplicationStartedEvent.class)
public void appStartedListener(ApplicationStartedEvent event) throws Exception {
logger.info("[Sentinel Starter] Start to find ReadableDataSource");
Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType(
ReadableDataSource.class);
if (dataSourceMap.size() == 1) {
logger.info("[Sentinel Starter] There exists only one ReadableDataSource named {}, start to load rules",
dataSourceMap.keySet().iterator().next());
ReadableDataSource dataSource = dataSourceMap.values().iterator().next();
Object ruleConfig = dataSource.loadConfig();
SentinelProperty sentinelProperty = dataSource.getProperty();
Integer rulesNum;
if ((rulesNum = checkRuleType(ruleConfig, FlowRule.class)) > 0) {
FlowRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} flow rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, DegradeRule.class)) > 0) {
DegradeRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} degrade rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, SystemRule.class)) > 0) {
SystemRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} system rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, AuthorityRule.class)) > 0) {
AuthorityRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} authority rules", rulesNum);
}
} else if (dataSourceMap.size() > 1) {
logger.warn(
"[Sentinel Starter] There exists more than one ReadableDataSource, can not choose which one to load");
} else {
logger.warn(
"[Sentinel Starter] No ReadableDataSource exists");
}
}
private Integer checkRuleType(Object ruleConfig, Class type) {
if (ruleConfig.getClass() == type) {
return 1;
} else if (ruleConfig instanceof List) {
List ruleList = (List)ruleConfig;
if (ruleList.stream().filter(rule -> rule.getClass() == type).toArray().length == ruleList.size()) {
return ruleList.size();
}
}
return -1;
}
class SentinelDataSourceField {
private SentinelDataSource sentinelDataSource;
private Field field;
public SentinelDataSourceField(SentinelDataSource sentinelDataSource,
Field field) {
this.sentinelDataSource = sentinelDataSource;
this.field = field;
}
public SentinelDataSource getSentinelDataSource() {
return sentinelDataSource;
}
public void setSentinelDataSource(SentinelDataSource sentinelDataSource) {
this.sentinelDataSource = sentinelDataSource;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
}
}

@ -1,68 +0,0 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.alibaba.sentinel.datasource;
import java.util.HashMap;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean;
/**
* Registry to save DataSource FactoryBean
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see ReadableDataSource
* @see FileRefreshableDataSourceFactoryBean
* @see ZookeeperDataSourceFactoryBean
* @see NacosDataSourceFactoryBean
* @see ApolloDataSourceFactoryBean
*/
public class SentinelDataSourceRegistry {
private static HashMap<String, Class<? extends FactoryBean>> cache = new HashMap<>(
32);
static {
SentinelDataSourceRegistry.registerFactoryBean("file",
FileRefreshableDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("zk",
ZookeeperDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("nacos",
NacosDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("apollo",
ApolloDataSourceFactoryBean.class);
}
public static synchronized void registerFactoryBean(String alias,
Class<? extends FactoryBean> clazz) {
cache.put(alias, clazz);
}
public static Class<? extends FactoryBean> getFactoryBean(String alias) {
return cache.get(alias);
}
public static boolean checkFactoryBean(String alias) {
return cache.containsKey(alias);
}
}

@ -1,50 +0,0 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.alibaba.sentinel.datasource.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import org.springframework.core.annotation.AliasFor;
/**
* An annotation to inject {@link ReadableDataSource} instance
* into a Spring Bean. The Properties of DataSource bean get from config files with
* specific prefix.
*
* <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see ReadableDataSource
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SentinelDataSource {
@AliasFor("prefix")
String value() default "";
@AliasFor("value")
String prefix() default "";
String name() default ""; // spring bean name
}

@ -0,0 +1,41 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Abstract class Using by {@link DataSourcePropertiesConfiguration}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class AbstractDataSourceProperties {
private String dataType = "json";
private String converterClass;
@JsonIgnore
private final String factoryBeanName;
public AbstractDataSourceProperties(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
}
public String getDataType() {
return dataType;
}
public void setDataType(String dataType) {
this.dataType = dataType;
}
public String getConverterClass() {
return converterClass;
}
public void setConverterClass(String converterClass) {
this.converterClass = converterClass;
}
public String getFactoryBeanName() {
return factoryBeanName;
}
}

@ -0,0 +1,44 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
/**
* Apollo Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link ApolloDataSourceFactoryBean}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class ApolloDataSourceProperties extends AbstractDataSourceProperties {
private String namespaceName;
private String flowRulesKey;
private String defaultFlowRuleValue;
public ApolloDataSourceProperties() {
super(ApolloDataSourceFactoryBean.class.getName());
}
public String getNamespaceName() {
return namespaceName;
}
public void setNamespaceName(String namespaceName) {
this.namespaceName = namespaceName;
}
public String getFlowRulesKey() {
return flowRulesKey;
}
public void setFlowRulesKey(String flowRulesKey) {
this.flowRulesKey = flowRulesKey;
}
public String getDefaultFlowRuleValue() {
return defaultFlowRuleValue;
}
public void setDefaultFlowRuleValue(String defaultFlowRuleValue) {
this.defaultFlowRuleValue = defaultFlowRuleValue;
}
}

@ -0,0 +1,79 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.util.ObjectUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* Using By ConfigurationProperties.
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see NacosDataSourceProperties
* @see ApolloDataSourceProperties
* @see ZookeeperDataSourceProperties
* @see FileDataSourceProperties
*/
public class DataSourcePropertiesConfiguration {
private FileDataSourceProperties file;
private NacosDataSourceProperties nacos;
private ZookeeperDataSourceProperties zk;
private ApolloDataSourceProperties apollo;
public FileDataSourceProperties getFile() {
return file;
}
public void setFile(FileDataSourceProperties file) {
this.file = file;
}
public NacosDataSourceProperties getNacos() {
return nacos;
}
public void setNacos(NacosDataSourceProperties nacos) {
this.nacos = nacos;
}
public ZookeeperDataSourceProperties getZk() {
return zk;
}
public void setZk(ZookeeperDataSourceProperties zk) {
this.zk = zk;
}
public ApolloDataSourceProperties getApollo() {
return apollo;
}
public void setApollo(ApolloDataSourceProperties apollo) {
this.apollo = apollo;
}
@JsonIgnore
public List<String> getInvalidField() {
return Arrays.stream(this.getClass().getDeclaredFields()).map(field -> {
try {
if (!ObjectUtils.isEmpty(field.get(this))) {
return field.getName();
}
return null;
}
catch (IllegalAccessException e) {
// won't happen
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList());
}
}

@ -0,0 +1,53 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean;
/**
* File Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link FileRefreshableDataSourceFactoryBean}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class FileDataSourceProperties extends AbstractDataSourceProperties {
private String file;
private String charset = "utf-8";
private long recommendRefreshMs = 3000L;
private int bufSize = 1024 * 1024;
public FileDataSourceProperties() {
super(FileRefreshableDataSourceFactoryBean.class.getName());
}
public String getFile() {
return file;
}
public void setFile(String file) {
this.file = file;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public long getRecommendRefreshMs() {
return recommendRefreshMs;
}
public void setRecommendRefreshMs(long recommendRefreshMs) {
this.recommendRefreshMs = recommendRefreshMs;
}
public int getBufSize() {
return bufSize;
}
public void setBufSize(int bufSize) {
this.bufSize = bufSize;
}
}

@ -0,0 +1,44 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
/**
* Nacos Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link NacosDataSourceFactoryBean}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class NacosDataSourceProperties extends AbstractDataSourceProperties {
private String serverAddr;
private String groupId;
private String dataId;
public NacosDataSourceProperties() {
super(NacosDataSourceFactoryBean.class.getName());
}
public String getServerAddr() {
return serverAddr;
}
public void setServerAddr(String serverAddr) {
this.serverAddr = serverAddr;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
}

@ -0,0 +1,56 @@
package org.springframework.cloud.alibaba.sentinel.datasource.config;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean;
/**
* Zookeeper Properties class Using by {@link DataSourcePropertiesConfiguration} and
* {@link ZookeeperDataSourceFactoryBean}
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class ZookeeperDataSourceProperties extends AbstractDataSourceProperties {
public ZookeeperDataSourceProperties() {
super(ZookeeperDataSourceFactoryBean.class.getName());
}
private String serverAddr;
private String path;
private String groupId;
private String dataId;
public String getServerAddr() {
return serverAddr;
}
public void setServerAddr(String serverAddr) {
this.serverAddr = serverAddr;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
}

@ -0,0 +1,157 @@
package org.springframework.cloud.alibaba.sentinel.datasource.converter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Convert sentinel rules for json array Using strict mode to parse json
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see FlowRule
* @see DegradeRule
* @see SystemRule
* @see AuthorityRule
* @see ObjectMapper
*/
public class JsonConverter implements Converter<String, List<AbstractRule>> {
private static final Logger logger = LoggerFactory.getLogger(JsonConverter.class);
private final ObjectMapper objectMapper;
public JsonConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public List<AbstractRule> convert(String source) {
List<AbstractRule> ruleList = new ArrayList<>();
if (StringUtils.isEmpty(source)) {
logger.info(
"Sentinel JsonConverter can not convert rules because source is empty");
return ruleList;
}
try {
List jsonArray = objectMapper.readValue(source,
new TypeReference<List<HashMap>>() {
});
jsonArray.stream().forEach(obj -> {
String itemJson = null;
try {
itemJson = objectMapper.writeValueAsString(obj);
}
catch (JsonProcessingException e) {
// won't be happen
}
List<AbstractRule> rules = Arrays.asList(convertFlowRule(itemJson),
convertDegradeRule(itemJson), convertSystemRule(itemJson),
convertAuthorityRule(itemJson));
List<AbstractRule> convertRuleList = rules.stream()
.filter(rule -> !ObjectUtils.isEmpty(rule))
.collect(Collectors.toList());
if (convertRuleList.size() == 0) {
logger.warn(
"Sentinel JsonConverter can not convert {} to any rules, ignore",
itemJson);
}
else if (convertRuleList.size() > 1) {
logger.warn(
"Sentinel JsonConverter convert {} and match multi rules, ignore",
itemJson);
}
else {
ruleList.add(convertRuleList.get(0));
}
});
if (jsonArray.size() != ruleList.size()) {
logger.warn(
"Sentinel JsonConverter Source list size is not equals to Target List, maybe a "
+ "part of json is invalid. Source List: " + jsonArray
+ ", Target List: " + ruleList);
}
}
catch (Exception e) {
logger.error("Sentinel JsonConverter convert error: " + e.getMessage());
throw new RuntimeException(
"Sentinel JsonConverter convert error: " + e.getMessage(), e);
}
logger.info("Sentinel JsonConverter convert {} rules: {}", ruleList.size(),
ruleList);
return ruleList;
}
private FlowRule convertFlowRule(String json) {
try {
FlowRule rule = objectMapper.readValue(json, FlowRule.class);
if (FlowRuleManager.isValidRule(rule)) {
return rule;
}
}
catch (Exception e) {
// ignore
}
return null;
}
private DegradeRule convertDegradeRule(String json) {
try {
DegradeRule rule = objectMapper.readValue(json, DegradeRule.class);
if (DegradeRuleManager.isValidRule(rule)) {
return rule;
}
}
catch (Exception e) {
// ignore
}
return null;
}
private SystemRule convertSystemRule(String json) {
SystemRule rule = null;
try {
rule = objectMapper.readValue(json, SystemRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
private AuthorityRule convertAuthorityRule(String json) {
AuthorityRule rule = null;
try {
rule = objectMapper.readValue(json, AuthorityRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
}

@ -0,0 +1,157 @@
package org.springframework.cloud.alibaba.sentinel.datasource.converter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
/**
* Convert sentinel rules for xml array Using strict mode to parse xml
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see FlowRule
* @see DegradeRule
* @see SystemRule
* @see AuthorityRule
* @see XmlMapper
*/
public class XmlConverter implements Converter<String, List<AbstractRule>> {
private static final Logger logger = LoggerFactory.getLogger(XmlConverter.class);
private final XmlMapper xmlMapper;
public XmlConverter(XmlMapper xmlMapper) {
this.xmlMapper = xmlMapper;
}
@Override
public List<AbstractRule> convert(String source) {
List<AbstractRule> ruleList = new ArrayList<>();
if (StringUtils.isEmpty(source)) {
logger.info(
"Sentinel XmlConverter can not convert rules because source is empty");
return ruleList;
}
try {
List xmlArray = xmlMapper.readValue(source,
new TypeReference<List<HashMap>>() {
});
xmlArray.stream().forEach(obj -> {
String itemXml = null;
try {
itemXml = xmlMapper.writeValueAsString(obj);
}
catch (JsonProcessingException e) {
// won't be happen
}
List<AbstractRule> rules = Arrays.asList(convertFlowRule(itemXml),
convertDegradeRule(itemXml), convertSystemRule(itemXml),
convertAuthorityRule(itemXml));
List<AbstractRule> convertRuleList = rules.stream()
.filter(rule -> !ObjectUtils.isEmpty(rule))
.collect(Collectors.toList());
if (convertRuleList.size() == 0) {
logger.warn(
"Sentinel XmlConverter can not convert {} to any rules, ignore",
itemXml);
}
else if (convertRuleList.size() > 1) {
logger.warn(
"Sentinel XmlConverter convert {} and match multi rules, ignore",
itemXml);
}
else {
ruleList.add(convertRuleList.get(0));
}
});
if (xmlArray.size() != ruleList.size()) {
logger.warn(
"Sentinel XmlConverter Source list size is not equals to Target List, maybe a "
+ "part of xml is invalid. Source List: " + xmlArray
+ ", Target List: " + ruleList);
}
}
catch (Exception e) {
logger.error("Sentinel XmlConverter convert error: " + e.getMessage());
throw new RuntimeException(
"Sentinel XmlConverter convert error: " + e.getMessage(), e);
}
logger.info("Sentinel XmlConverter convert {} rules: {}", ruleList.size(),
ruleList);
return ruleList;
}
private FlowRule convertFlowRule(String xml) {
try {
FlowRule rule = xmlMapper.readValue(xml, FlowRule.class);
if (FlowRuleManager.isValidRule(rule)) {
return rule;
}
}
catch (Exception e) {
// ignore
}
return null;
}
private DegradeRule convertDegradeRule(String xml) {
try {
DegradeRule rule = xmlMapper.readValue(xml, DegradeRule.class);
if (DegradeRuleManager.isValidRule(rule)) {
return rule;
}
}
catch (Exception e) {
// ignore
}
return null;
}
private SystemRule convertSystemRule(String xml) {
SystemRule rule = null;
try {
rule = xmlMapper.readValue(xml, SystemRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
private AuthorityRule convertAuthorityRule(String xml) {
AuthorityRule rule = null;
try {
rule = xmlMapper.readValue(xml, AuthorityRule.class);
}
catch (Exception e) {
// ignore
}
return rule;
}
}

@ -1,75 +0,0 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.springframework.cloud.alibaba.sentinel.datasource.util;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.PropertySources;
/**
* {@link PropertySources} Utilities
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class PropertySourcesUtils {
/**
* Get Sub {@link Properties}
*
* @param propertySources {@link PropertySource} Iterable
* @param prefix the prefix of property name
* @return Map
* @see Properties
*/
public static Map<String, Object> getSubProperties(Iterable<PropertySource<?>> propertySources, String prefix) {
Map<String, Object> subProperties = new LinkedHashMap<String, Object>();
String normalizedPrefix = normalizePrefix(prefix);
for (PropertySource<?> source : propertySources) {
if (source instanceof EnumerablePropertySource) {
for (String name : ((EnumerablePropertySource<?>)source).getPropertyNames()) {
if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) {
String subName = name.substring(normalizedPrefix.length());
if (!subProperties.containsKey(subName)) { // take first one
Object value = source.getProperty(name);
subProperties.put(subName, value);
}
}
}
}
}
return subProperties;
}
/**
* Normalize the prefix
*
* @param prefix the prefix
* @return the prefix
*/
public static String normalizePrefix(String prefix) {
return prefix.endsWith(".") ? prefix : prefix + ".";
}
}

@ -40,6 +40,12 @@
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<scope>provided</scope>
</dependency>
<!--spring boot-->
<dependency>

@ -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 {
/**

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

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

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

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

Loading…
Cancel
Save