mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
fixes #91
This commit is contained in:
parent
d88e27e45c
commit
3c33b234c8
@ -41,8 +41,27 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</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-->
|
<!--spring boot-->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot</artifactId>
|
<artifactId>spring-boot</artifactId>
|
||||||
@ -63,7 +82,6 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</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>
|
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
<artifactId>jackson-dataformat-xml</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--spring boot-->
|
<!--spring boot-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -17,9 +17,12 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel;
|
package org.springframework.cloud.alibaba.sentinel;
|
||||||
|
|
||||||
import java.util.List;
|
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.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||||
|
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||||
@ -29,6 +32,7 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
|||||||
* @author xiaojing
|
* @author xiaojing
|
||||||
* @author hengyunabc
|
* @author hengyunabc
|
||||||
* @author jiashuai.xie
|
* @author jiashuai.xie
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX)
|
@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX)
|
||||||
public class SentinelProperties {
|
public class SentinelProperties {
|
||||||
@ -49,6 +53,12 @@ public class SentinelProperties {
|
|||||||
*/
|
*/
|
||||||
private String charset = "UTF-8";
|
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
|
* transport configuration about dashboard and client
|
||||||
*/
|
*/
|
||||||
@ -145,6 +155,14 @@ public class SentinelProperties {
|
|||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, DataSourcePropertiesConfiguration> getDatasource() {
|
||||||
|
return datasource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDatasource(Map<String, DataSourcePropertiesConfiguration> datasource) {
|
||||||
|
this.datasource = datasource;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Flow {
|
public static class Flow {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,13 +21,15 @@ import java.util.Optional;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.util.StringUtils;
|
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.transport.config.TransportConfig;
|
||||||
import com.alibaba.csp.sentinel.util.AppNameUtil;
|
import com.alibaba.csp.sentinel.util.AppNameUtil;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author xiaojing
|
* @author xiaojing
|
||||||
* @author jiashuai.xie
|
* @author jiashuai.xie
|
||||||
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||||
@ -135,9 +141,33 @@ public class SentinelAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
public SentinelDataSourceHandler sentinelDataSourceHandler() {
|
||||||
public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() {
|
return new SentinelDataSourceHandler();
|
||||||
return new SentinelDataSourcePostProcessor();
|
}
|
||||||
|
|
||||||
|
@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
|
* PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate
|
||||||
*
|
*
|
||||||
* @author fangjian
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
* @see SentinelProtect
|
* @see SentinelProtect
|
||||||
* @see SentinelProtectInterceptor
|
* @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.List;
|
||||||
import java.util.Map;
|
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.DegradeRule;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
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.FlowRule;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||||
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
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
|
* Endpoint for Sentinel, contains ans properties and rules
|
||||||
@ -41,9 +45,15 @@ public class SentinelEndpoint {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SentinelProperties sentinelProperties;
|
private SentinelProperties sentinelProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SentinelDataSourceHandler dataSourceHandler;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@ReadOperation
|
@ReadOperation
|
||||||
public Map<String, Object> invoke() {
|
public Map<String, Object> invoke() {
|
||||||
Map<String, Object> result = new HashMap<>();
|
final Map<String, Object> result = new HashMap<>();
|
||||||
|
|
||||||
List<FlowRule> flowRules = FlowRuleManager.getRules();
|
List<FlowRule> flowRules = FlowRuleManager.getRules();
|
||||||
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
|
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
|
||||||
@ -52,6 +62,21 @@ public class SentinelEndpoint {
|
|||||||
result.put("FlowRules", flowRules);
|
result.put("FlowRules", flowRules);
|
||||||
result.put("DegradeRules", degradeRules);
|
result.put("DegradeRules", degradeRules);
|
||||||
result.put("SystemRules", systemRules);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user