1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

sentinel datasource refactor: including code refactor and update examples, docs

This commit is contained in:
fangjian0423
2018-11-22 19:18:40 +08:00
parent 5403fe3165
commit 10bb95c11a
30 changed files with 1758 additions and 1035 deletions

View File

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

View File

@@ -17,256 +17,285 @@
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;
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 {
/**
* 是否提前初始化心跳连接
*/
private boolean eager = false;
/**
* earlier initialize heart-beat when the spring container starts <note> when the
* transport dependency is on classpath ,the configuration is effective </note>
*/
private boolean eager = false;
/**
* Enable sentinel auto configure, the default value is true
*/
private boolean enabled = true;
/**
* enable sentinel auto configure, the default value is true
*/
private boolean enabled = true;
/**
* 字符编码集
*/
private String charset = "UTF-8";
/**
* charset when sentinel write or search metric file {@link SentinelConfig#CHARSET}
*/
private String charset = "UTF-8";
/**
* 通信相关配置
*/
@NestedConfigurationProperty
private Transport transport = new Transport();
/**
* configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
*/
private Map<String, DataSourcePropertiesConfiguration> datasource = new TreeMap<>(
String.CASE_INSENSITIVE_ORDER);
/**
* 监控数据相关配置
*/
@NestedConfigurationProperty
private Metric metric = new Metric();
/**
* transport configuration about dashboard and client
*/
@NestedConfigurationProperty
private Transport transport = new Transport();
/**
* web 相关配置
*/
@NestedConfigurationProperty
private Servlet servlet = new Servlet();
/**
* metric configuration about resource
*/
@NestedConfigurationProperty
private Metric metric = new Metric();
/**
* 限流相关
*/
@NestedConfigurationProperty
private Filter filter = new Filter();
/**
* web servlet configuration <note> when the application is web ,the configuration is
* effective </note>
*/
@NestedConfigurationProperty
private Servlet servlet = new Servlet();
@NestedConfigurationProperty
private Flow flow = new Flow();
/**
* sentinel filter <note> when the application is web ,the configuration is effective
* </note>
*/
@NestedConfigurationProperty
private Filter filter = new Filter();
public boolean isEager() {
return eager;
}
/**
* flow configuration
*/
@NestedConfigurationProperty
private Flow flow = new Flow();
public void setEager(boolean eager) {
this.eager = eager;
}
public boolean isEager() {
return eager;
}
public Flow getFlow() {
return flow;
}
public void setEager(boolean eager) {
this.eager = eager;
}
public void setFlow(Flow flow) {
this.flow = flow;
}
public Flow getFlow() {
return flow;
}
public String getCharset() {
return charset;
}
public void setFlow(Flow flow) {
this.flow = flow;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getCharset() {
return charset;
}
public Transport getTransport() {
return transport;
}
public void setCharset(String charset) {
this.charset = charset;
}
public void setTransport(Transport transport) {
this.transport = transport;
}
public Transport getTransport() {
return transport;
}
public Metric getMetric() {
return metric;
}
public void setTransport(Transport transport) {
this.transport = transport;
}
public void setMetric(Metric metric) {
this.metric = metric;
}
public Metric getMetric() {
return metric;
}
public Servlet getServlet() {
return servlet;
}
public void setMetric(Metric metric) {
this.metric = metric;
}
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
public Servlet getServlet() {
return servlet;
}
public boolean isEnabled() {
return enabled;
}
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
public Filter getFilter() {
return filter;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setFilter(Filter filter) {
this.filter = filter;
}
public Filter getFilter() {
return filter;
}
public static class Flow {
public void setFilter(Filter filter) {
this.filter = filter;
}
/**
* 限流冷启动因子
*/
private String coldFactor = "3";
public Map<String, DataSourcePropertiesConfiguration> getDatasource() {
return datasource;
}
public String getColdFactor() {
return coldFactor;
}
public void setDatasource(Map<String, DataSourcePropertiesConfiguration> datasource) {
this.datasource = datasource;
}
public void setColdFactor(String coldFactor) {
this.coldFactor = coldFactor;
}
public static class Flow {
}
/**
* the cold factor {@link SentinelConfig#COLD_FACTOR}
*/
private String coldFactor = "3";
public static class Servlet {
public String getColdFactor() {
return coldFactor;
}
/**
* url 限流后的处理页面
*/
private String blockPage;
public void setColdFactor(String coldFactor) {
this.coldFactor = coldFactor;
}
public String getBlockPage() {
return blockPage;
}
}
public void setBlockPage(String blockPage) {
this.blockPage = blockPage;
}
}
public static class Servlet {
public static class Metric {
/**
* The process page when the flow control is triggered
*/
private String blockPage;
/**
* 监控数据写磁盘时单个文件的大小
*/
private String fileSingleSize;
public String getBlockPage() {
return blockPage;
}
/**
* 监控数据在磁盘上的总数量
*/
private String fileTotalCount;
public void setBlockPage(String blockPage) {
this.blockPage = blockPage;
}
}
public String getFileSingleSize() {
return fileSingleSize;
}
public static class Metric {
public void setFileSingleSize(String fileSingleSize) {
this.fileSingleSize = fileSingleSize;
}
/**
* the metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}
*/
private String fileSingleSize;
public String getFileTotalCount() {
return fileTotalCount;
}
/**
* the total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}
*/
private String fileTotalCount;
public void setFileTotalCount(String fileTotalCount) {
this.fileTotalCount = fileTotalCount;
}
}
public String getFileSingleSize() {
return fileSingleSize;
}
public static class Transport {
public void setFileSingleSize(String fileSingleSize) {
this.fileSingleSize = fileSingleSize;
}
/**
* sentinel api port,default value is 8721
*/
private String port = "8721";
public String getFileTotalCount() {
return fileTotalCount;
}
/**
* Sentinel dashboard address, won't try to connect dashboard when address is
* empty
*/
private String dashboard = "";
public void setFileTotalCount(String fileTotalCount) {
this.fileTotalCount = fileTotalCount;
}
}
/**
* 客户端和DashBord心跳发送时间
*/
private String heartbeatIntervalMs;
public static class Transport {
public String getHeartbeatIntervalMs() {
return heartbeatIntervalMs;
}
/**
* sentinel api port,default value is 8721 {@link TransportConfig#SERVER_PORT}
*/
private String port = "8721";
public void setHeartbeatIntervalMs(String heartbeatIntervalMs) {
this.heartbeatIntervalMs = heartbeatIntervalMs;
}
/**
* sentinel dashboard address, won't try to connect dashboard when address is
* empty {@link TransportConfig#CONSOLE_SERVER}
*/
private String dashboard = "";
public String getPort() {
return port;
}
/**
* send heartbeat interval millisecond
* {@link TransportConfig#HEARTBEAT_INTERVAL_MS}
*/
private String heartbeatIntervalMs;
public void setPort(String port) {
this.port = port;
}
public String getHeartbeatIntervalMs() {
return heartbeatIntervalMs;
}
public String getDashboard() {
return dashboard;
}
public void setHeartbeatIntervalMs(String heartbeatIntervalMs) {
this.heartbeatIntervalMs = heartbeatIntervalMs;
}
public void setDashboard(String dashboard) {
this.dashboard = dashboard;
}
public String getPort() {
return port;
}
}
public void setPort(String port) {
this.port = port;
}
public static class Filter {
public String getDashboard() {
return dashboard;
}
/**
* Sentinel filter chain order.
*/
private int order = Ordered.HIGHEST_PRECEDENCE;
public void setDashboard(String dashboard) {
this.dashboard = dashboard;
}
/**
* URL pattern for sentinel filter,default is /*
*/
private List<String> urlPatterns;
}
public int getOrder() {
return this.order;
}
public static class Filter {
public void setOrder(int order) {
this.order = order;
}
/**
* sentinel filter chain order.
*/
private int order = Ordered.HIGHEST_PRECEDENCE;
public List<String> getUrlPatterns() {
return urlPatterns;
}
/**
* URL pattern for sentinel filter,default is /*
*/
private List<String> urlPatterns;
public void setUrlPatterns(List<String> urlPatterns) {
this.urlPatterns = urlPatterns;
}
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public List<String> getUrlPatterns() {
return urlPatterns;
}
public void setUrlPatterns(List<String> urlPatterns) {
this.urlPatterns = urlPatterns;
}
}
}

View File

@@ -18,6 +18,20 @@ package org.springframework.cloud.alibaba.sentinel.custom;
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.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;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
@@ -28,115 +42,131 @@ import com.alibaba.csp.sentinel.init.InitExecutor;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
import com.alibaba.csp.sentinel.util.AppNameUtil;
import org.springframework.beans.factory.annotation.Autowired;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
/**
* @author xiaojing
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@Configuration
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelAutoConfiguration {
@Value("${project.name:${spring.application.name:}}")
private String projectName;
@Value("${project.name:${spring.application.name:}}")
private String projectName;
@Autowired
private SentinelProperties properties;
@Autowired
private SentinelProperties properties;
@Autowired(required = false)
private UrlCleaner urlCleaner;
@Autowired(required = false)
private UrlCleaner urlCleaner;
@Autowired(required = false)
private UrlBlockHandler urlBlockHandler;
@Autowired(required = false)
private UrlBlockHandler urlBlockHandler;
@PostConstruct
private void init() {
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))
&& StringUtils.hasText(properties.getTransport().getPort())) {
System.setProperty(TransportConfig.SERVER_PORT,
properties.getTransport().getPort());
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))
&& StringUtils.hasText(properties.getTransport().getDashboard())) {
System.setProperty(TransportConfig.CONSOLE_SERVER,
properties.getTransport().getDashboard());
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS))
&& StringUtils
.hasText(properties.getTransport().getHeartbeatIntervalMs())) {
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
properties.getTransport().getHeartbeatIntervalMs());
}
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getCharset())) {
System.setProperty(SentinelConfig.CHARSET, properties.getCharset());
}
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE))
&& StringUtils.hasText(properties.getMetric().getFileSingleSize())) {
System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE,
properties.getMetric().getFileSingleSize());
}
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT))
&& StringUtils.hasText(properties.getMetric().getFileTotalCount())) {
System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT,
properties.getMetric().getFileTotalCount());
}
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR))
&& StringUtils.hasText(properties.getFlow().getColdFactor())) {
System.setProperty(SentinelConfig.COLD_FACTOR,
properties.getFlow().getColdFactor());
}
@PostConstruct
private void init() {
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))
&& StringUtils.hasText(properties.getTransport().getPort())) {
System.setProperty(TransportConfig.SERVER_PORT,
properties.getTransport().getPort());
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))
&& StringUtils.hasText(properties.getTransport().getDashboard())) {
System.setProperty(TransportConfig.CONSOLE_SERVER,
properties.getTransport().getDashboard());
}
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS))
&& StringUtils
.hasText(properties.getTransport().getHeartbeatIntervalMs())) {
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
properties.getTransport().getHeartbeatIntervalMs());
}
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getCharset())) {
System.setProperty(SentinelConfig.CHARSET, properties.getCharset());
}
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE))
&& StringUtils.hasText(properties.getMetric().getFileSingleSize())) {
System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE,
properties.getMetric().getFileSingleSize());
}
if (StringUtils
.isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT))
&& StringUtils.hasText(properties.getMetric().getFileTotalCount())) {
System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT,
properties.getMetric().getFileTotalCount());
}
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR))
&& StringUtils.hasText(properties.getFlow().getColdFactor())) {
System.setProperty(SentinelConfig.COLD_FACTOR,
properties.getFlow().getColdFactor());
}
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
WebServletConfig.setBlockPage(properties.getServlet().getBlockPage());
}
if (urlBlockHandler != null) {
WebCallbackManager.setUrlBlockHandler(urlBlockHandler);
}
if (urlCleaner != null) {
WebCallbackManager.setUrlCleaner(urlCleaner);
}
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
WebServletConfig.setBlockPage(properties.getServlet().getBlockPage());
}
if (urlBlockHandler != null) {
WebCallbackManager.setUrlBlockHandler(urlBlockHandler);
}
if (urlCleaner != null) {
WebCallbackManager.setUrlCleaner(urlCleaner);
}
// earlier initialize
if (properties.isEager()) {
InitExecutor.doInit();
}
}
// earlier initialize
if (properties.isEager()) {
InitExecutor.doInit();
}
}
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
return new SentinelBeanPostProcessor();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
return new SentinelBeanPostProcessor();
}
@Bean
@ConditionalOnMissingBean
public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() {
return new SentinelDataSourcePostProcessor();
}
@Bean
public SentinelDataSourceHandler sentinelDataSourceHandler() {
return new SentinelDataSourceHandler();
}
@Bean("sentinel-json-converter")
public JsonConverter jsonConverter(
@Qualifier("sentinel-object-mapper") ObjectMapper objectMapper) {
return new JsonConverter(objectMapper);
}
@Bean("sentinel-object-mapper")
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
@ConditionalOnClass(XmlMapper.class)
protected static class SentinelXmlConfiguration {
@Bean("sentinel-xml-converter")
public XmlConverter xmlConverter(
@Qualifier("sentinel-xml-mapper") XmlMapper xmlMapper) {
return new XmlConverter(xmlMapper);
}
@Bean("sentinel-xml-mapper")
public XmlMapper xmlMapper() {
return new XmlMapper();
}
}
}

View File

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

View File

@@ -0,0 +1,353 @@
/*
* 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.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 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.ApplicationReadyEvent;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
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.AbstractRule;
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<? extends AbstractRule>> rulesList = Arrays.asList(FlowRule.class,
DegradeRule.class, SystemRule.class, AuthorityRule.class);
private List<String> dataSourceBeanNameList = Collections
.synchronizedList(new ArrayList<String>());
private final String DATATYPE_FIELD = "dataType";
private final String CUSTOM_DATATYPE = "custom";
private final String CONVERTERCLASS_FIELD = "converterClass";
@Autowired
private SentinelProperties sentinelProperties;
@EventListener(classes = ApplicationReadyEvent.class)
public void buildDataSource(ApplicationReadyEvent event) throws Exception {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event
.getApplicationContext().getAutowireCapableBeanFactory();
for (Map.Entry<String, DataSourcePropertiesConfiguration> entry : sentinelProperties
.getDatasource().entrySet()) {
String dataSourceName = entry.getKey();
DataSourcePropertiesConfiguration dataSourceProperties = entry.getValue();
if (dataSourceProperties.getInvalidField().size() != 1) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " multi datasource active and won't loaded: "
+ dataSourceProperties.getInvalidField());
return;
}
if (dataSourceProperties.getFile() != null) {
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, dataSourceProperties.getFile(),
dataSourceName + "-sentinel-file-datasource");
}
if (dataSourceProperties.getNacos() != null) {
registerBean(beanFactory, dataSourceProperties.getNacos(),
dataSourceName + "-sentinel-nacos-datasource");
}
if (dataSourceProperties.getApollo() != null) {
registerBean(beanFactory, dataSourceProperties.getApollo(),
dataSourceName + "-sentinel-apollo-datasource");
}
if (dataSourceProperties.getZk() != null) {
registerBean(beanFactory, dataSourceProperties.getZk(),
dataSourceName + "-sentinel-zk-datasource");
}
}
for (String beanName : dataSourceBeanNameList) {
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 = new HashMap<>();
for (Field field : dataSourceProperties.getClass().getDeclaredFields()) {
try {
field.setAccessible(true);
Object fieldVal = field.get(dataSourceProperties);
if (fieldVal != null) {
propertyMap.put(field.getName(), fieldVal);
}
}
catch (IllegalAccessException e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
+ " field: " + field.getName() + " invoke error");
throw new RuntimeException("[Sentinel Starter] DataSource "
+ dataSourceName + " field: " + field.getName() + " invoke error",
e);
}
}
propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass());
propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType());
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
String propertyName = entry.getKey();
Object propertyValue = entry.getValue();
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)) {
continue;
}
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 (checkRuleTypeValid(convertedRuleList)) {
if (checkAllRuleTypeSame(convertedRuleList)) {
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 = new ArrayList<>();
for (Object rule : convertedRuleList) {
classList.add(rule.getClass());
}
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;
}
private boolean checkRuleTypeValid(List convertedRuleList) {
int matchCount = 0;
for (Object rule : convertedRuleList) {
if (rulesList.contains(rule.getClass())) {
matchCount++;
}
}
return matchCount == convertedRuleList.size();
}
private boolean checkAllRuleTypeSame(List convertedRuleList) {
int matchCount = 0;
for(Object rule : convertedRuleList) {
if(rulesList.contains(rule.getClass()) && rule.getClass() == convertedRuleList.get(0).getClass()) {
matchCount ++;
}
}
return matchCount == convertedRuleList.size();
}
public List<String> getDataSourceBeanNameList() {
return dataSourceBeanNameList;
}
}

View File

@@ -20,15 +20,19 @@ 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.AbstractEndpoint;
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.AbstractEndpoint;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
/**
* Endpoint for Sentinel, contains ans properties and rules
@@ -39,6 +43,12 @@ public class SentinelEndpoint extends AbstractEndpoint<Map<String, Object>> {
@Autowired
private SentinelProperties sentinelProperties;
@Autowired
private SentinelDataSourceHandler dataSourceHandler;
@Autowired
private ApplicationContext applicationContext;
public SentinelEndpoint() {
super("sentinel");
}
@@ -54,6 +64,20 @@ public class SentinelEndpoint extends AbstractEndpoint<Map<String, Object>> {
result.put("FlowRules", flowRules);
result.put("DegradeRules", degradeRules);
result.put("SystemRules", systemRules);
result.put("datasources", new HashMap<String, Object>());
for (String dataSourceBeanName : dataSourceHandler.getDataSourceBeanNameList()) {
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;
}