diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml
index b5707134..3abdf979 100644
--- a/spring-cloud-alibaba-sentinel/pom.xml
+++ b/spring-cloud-alibaba-sentinel/pom.xml
@@ -42,6 +42,13 @@
true
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-ribbon
+ provided
+ true
+
+
com.alibaba.csp
sentinel-parameter-flow-control
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
index 714cc08b..0c2a9d09 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelConstants.java
@@ -23,6 +23,9 @@ public interface SentinelConstants {
String PROPERTY_PREFIX = "spring.cloud.sentinel";
+ String BLOCK_TYPE = "block";
+ String FALLBACK_TYPE = "fallback";
+
// commercialization
String FLOW_DATASOURCE_NAME = "edas-flow";
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
index 63ce6865..53aea051 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelProperties.java
@@ -21,7 +21,6 @@ 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 org.springframework.validation.annotation.Validated;
@@ -31,6 +30,8 @@ import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
+ * {@link ConfigurationProperties} for Sentinel.
+ *
* @author xiaojing
* @author hengyunabc
* @author jiashuai.xie
@@ -41,58 +42,51 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig;
public class SentinelProperties {
/**
- * earlier initialize heart-beat when the spring container starts when the
- * transport dependency is on classpath ,the configuration is effective
+ * Earlier initialize heart-beat when the spring container starts when the transport
+ * dependency is on classpath, the configuration is effective.
*/
private boolean eager = false;
/**
- * enable sentinel auto configure, the default value is true
+ * Enable sentinel auto configure, the default value is true.
*/
private boolean enabled = true;
/**
- * configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
+ * Configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'.
*/
private Map datasource = new TreeMap<>(
String.CASE_INSENSITIVE_ORDER);
/**
- * transport configuration about dashboard and client
+ * Transport configuration about dashboard and client.
*/
- @NestedConfigurationProperty
private Transport transport = new Transport();
/**
- * metric configuration about resource
+ * Metric configuration about resource.
*/
- @NestedConfigurationProperty
private Metric metric = new Metric();
/**
- * web servlet configuration when the application is web ,the configuration is
- * effective
+ * Web servlet configuration when the application is web, the configuration is
+ * effective.
*/
- @NestedConfigurationProperty
private Servlet servlet = new Servlet();
/**
- * sentinel filter when the application is web ,the configuration is effective
- *
+ * Sentinel filter when the application is web, the configuration is effective.
*/
- @NestedConfigurationProperty
private Filter filter = new Filter();
/**
- * flow configuration
+ * Sentinel Flow configuration.
*/
- @NestedConfigurationProperty
private Flow flow = new Flow();
/**
- * sentinel log configuration {@link LogBase}
+ * Sentinel log configuration {@link LogBase}.
*/
- @NestedConfigurationProperty
private Log log = new Log();
public boolean isEager() {
@@ -170,7 +164,7 @@ public class SentinelProperties {
public static class Flow {
/**
- * the cold factor {@link SentinelConfig#COLD_FACTOR}
+ * The cold factor {@link SentinelConfig#COLD_FACTOR}.
*/
private String coldFactor = "3";
@@ -187,7 +181,7 @@ public class SentinelProperties {
public static class Servlet {
/**
- * The process page when the flow control is triggered
+ * The process page when the flow control is triggered.
*/
private String blockPage;
@@ -203,17 +197,17 @@ public class SentinelProperties {
public static class Metric {
/**
- * the metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}
+ * The metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}.
*/
private String fileSingleSize;
/**
- * the total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}
+ * The total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}.
*/
private String fileTotalCount;
/**
- * charset when sentinel write or search metric file
+ * Charset when sentinel write or search metric file.
* {@link SentinelConfig#CHARSET}
*/
private String charset = "UTF-8";
@@ -246,22 +240,28 @@ public class SentinelProperties {
public static class Transport {
/**
- * sentinel api port,default value is 8719 {@link TransportConfig#SERVER_PORT}
+ * Sentinel api port, default value is 8719 {@link TransportConfig#SERVER_PORT}.
*/
private String port = "8719";
/**
- * sentinel dashboard address, won't try to connect dashboard when address is
- * empty {@link TransportConfig#CONSOLE_SERVER}
+ * Sentinel dashboard address, won't try to connect dashboard when address is
+ * empty {@link TransportConfig#CONSOLE_SERVER}.
*/
private String dashboard = "";
/**
- * send heartbeat interval millisecond
- * {@link TransportConfig#HEARTBEAT_INTERVAL_MS}
+ * Send heartbeat interval millisecond
+ * {@link TransportConfig#HEARTBEAT_INTERVAL_MS}.
*/
private String heartbeatIntervalMs;
+ /**
+ * Get heartbeat client local ip. If the client ip not configured, it will be the
+ * address of local host.
+ */
+ private String clientIp;
+
public String getHeartbeatIntervalMs() {
return heartbeatIntervalMs;
}
@@ -286,17 +286,24 @@ public class SentinelProperties {
this.dashboard = dashboard;
}
+ public String getClientIp() {
+ return clientIp;
+ }
+
+ public void setClientIp(String clientIp) {
+ this.clientIp = clientIp;
+ }
}
public static class Filter {
/**
- * sentinel filter chain order.
+ * Sentinel filter chain order.
*/
private int order = Ordered.HIGHEST_PRECEDENCE;
/**
- * URL pattern for sentinel filter,default is /*
+ * URL pattern for sentinel filter, default is /*
*/
private List urlPatterns;
@@ -320,12 +327,12 @@ public class SentinelProperties {
public static class Log {
/**
- * sentinel log base dir
+ * Sentinel log base dir.
*/
private String dir;
/**
- * distinguish the log file by pid number
+ * Distinguish the log file by pid number.
*/
private boolean switchPid = false;
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
index 8428632f..d70fe785 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/SentinelWebAutoConfiguration.java
@@ -54,11 +54,6 @@ public class SentinelWebAutoConfiguration {
SentinelProperties.Filter filterConfig = properties.getFilter();
- if (null == filterConfig) {
- filterConfig = new SentinelProperties.Filter();
- properties.setFilter(filterConfig);
- }
-
if (filterConfig.getUrlPatterns() == null
|| filterConfig.getUrlPatterns().isEmpty()) {
List defaultPatterns = new ArrayList<>();
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
index 4349cf70..27739845 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java
@@ -22,6 +22,7 @@ import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -29,10 +30,12 @@ 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.ApplicationContext;
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.RequestOriginParser;
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;
@@ -74,8 +77,20 @@ public class SentinelAutoConfiguration {
@Autowired
private Optional urlBlockHandlerOptional;
+ @Autowired
+ private Optional requestOriginParserOptional;
+
@PostConstruct
private void init() {
+ if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
+ && StringUtils.hasText(properties.getLog().getDir())) {
+ System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
+ }
+ if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
+ && properties.getLog().isSwitchPid()) {
+ System.setProperty(LogBase.LOG_NAME_USE_PID,
+ String.valueOf(properties.getLog().isSwitchPid()));
+ }
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
&& StringUtils.hasText(projectName)) {
System.setProperty(AppNameUtil.APP_NAME, projectName);
@@ -96,6 +111,11 @@ public class SentinelAutoConfiguration {
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
properties.getTransport().getHeartbeatIntervalMs());
}
+ if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP))
+ && StringUtils.hasText(properties.getTransport().getClientIp())) {
+ System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP,
+ properties.getTransport().getClientIp());
+ }
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
&& StringUtils.hasText(properties.getMetric().getCharset())) {
System.setProperty(SentinelConfig.CHARSET,
@@ -121,18 +141,10 @@ public class SentinelAutoConfiguration {
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
WebServletConfig.setBlockPage(properties.getServlet().getBlockPage());
}
- if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
- && StringUtils.hasText(properties.getLog().getDir())) {
- System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
- }
- if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
- && properties.getLog().isSwitchPid()) {
- System.setProperty(LogBase.LOG_NAME_USE_PID,
- String.valueOf(properties.getLog().isSwitchPid()));
- }
urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
+ requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
// earlier initialize
if (properties.isEager()) {
@@ -150,13 +162,15 @@ public class SentinelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
- public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
- return new SentinelBeanPostProcessor();
+ public SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
}
@Bean
- public SentinelDataSourceHandler sentinelDataSourceHandler() {
- return new SentinelDataSourceHandler();
+ public SentinelDataSourceHandler sentinelDataSourceHandler(
+ DefaultListableBeanFactory beanFactory) {
+ return new SentinelDataSourceHandler(beanFactory);
}
protected static class SentinelConverterConfiguration {
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
index dd84109e..18580aa5 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java
@@ -16,22 +16,32 @@
package org.springframework.cloud.alibaba.sentinel.custom;
+import java.lang.reflect.Method;
+import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
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.cloud.alibaba.sentinel.SentinelConstants;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.core.type.StandardMethodMetadata;
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
/**
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
*
@@ -41,8 +51,14 @@ import org.springframework.web.client.RestTemplate;
*/
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
- @Autowired
- private ApplicationContext applicationContext;
+ private static final Logger logger = LoggerFactory
+ .getLogger(SentinelBeanPostProcessor.class);
+
+ private final ApplicationContext applicationContext;
+
+ public SentinelBeanPostProcessor(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
private ConcurrentHashMap cache = new ConcurrentHashMap<>();
@@ -60,10 +76,74 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
.getAnnotation(SentinelRestTemplate.class);
}
+ // check class and method validation
+ checkSentinelRestTemplate(sentinelRestTemplate, beanName);
cache.put(beanName, sentinelRestTemplate);
}
}
+ private void checkSentinelRestTemplate(SentinelRestTemplate sentinelRestTemplate,
+ String beanName) {
+ checkBlock4RestTemplate(sentinelRestTemplate.blockHandlerClass(),
+ sentinelRestTemplate.blockHandler(), beanName,
+ SentinelConstants.BLOCK_TYPE);
+ checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(),
+ sentinelRestTemplate.fallback(), beanName,
+ SentinelConstants.FALLBACK_TYPE);
+ }
+
+ private void checkBlock4RestTemplate(Class> blockClass, String blockMethod,
+ String beanName, String type) {
+ if (blockClass == void.class && StringUtils.isEmpty(blockMethod)) {
+ return;
+ }
+ if (blockClass != void.class && StringUtils.isEmpty(blockMethod)) {
+ logger.error(
+ "{} class attribute exists but {} method attribute is not exists in bean[{}]",
+ type, type, beanName);
+ throw new IllegalArgumentException(type + " class attribute exists but "
+ + type + " method attribute is not exists in bean[" + beanName + "]");
+ }
+ else if (blockClass == void.class && !StringUtils.isEmpty(blockMethod)) {
+ logger.error(
+ "{} method attribute exists but {} class attribute is not exists in bean[{}]",
+ type, type, beanName);
+ throw new IllegalArgumentException(type + " method attribute exists but "
+ + type + " class attribute is not exists in bean[" + beanName + "]");
+ }
+ Class[] args = new Class[] { HttpRequest.class, byte[].class,
+ ClientHttpRequestExecution.class, BlockException.class };
+ String argsStr = Arrays.toString(
+ Arrays.stream(args).map(clazz -> clazz.getSimpleName()).toArray());
+ Method foundMethod = ClassUtils.getStaticMethod(blockClass, blockMethod, args);
+ if (foundMethod == null) {
+ logger.error(
+ "{} static method can not be found in bean[{}]. The right method signature is {}#{}{}, please check your class name, method name and arguments",
+ type, beanName, blockClass.getName(), blockMethod, argsStr);
+ throw new IllegalArgumentException(type
+ + " static method can not be found in bean[" + beanName
+ + "]. The right method signature is " + blockClass.getName() + "#"
+ + blockMethod + argsStr
+ + ", please check your class name, method name and arguments");
+ }
+
+ if (!ClientHttpResponse.class.isAssignableFrom(foundMethod.getReturnType())) {
+ logger.error(
+ "{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}",
+ type, beanName, blockClass.getName(), blockMethod, argsStr);
+ throw new IllegalArgumentException(type + " method return value in bean["
+ + beanName + "] is not ClientHttpResponse: " + blockClass.getName()
+ + "#" + blockMethod + argsStr);
+ }
+ if (type.equals(SentinelConstants.BLOCK_TYPE)) {
+ BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod,
+ foundMethod);
+ }
+ else {
+ BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod);
+ }
+ }
+
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
Class> beanType) {
return beanType == RestTemplate.class
@@ -103,7 +183,7 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
.getBean(interceptorBeanName.toString(),
SentinelProtectInterceptor.class);
- restTemplate.getInterceptors().add(sentinelProtectInterceptor);
+ restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
}
return bean;
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
index f9776d27..a6cf6dfa 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java
@@ -1,9 +1,7 @@
package org.springframework.cloud.alibaba.sentinel.custom;
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;
@@ -12,10 +10,10 @@ import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.SmartInitializingSingleton;
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.SentinelConstants;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
@@ -24,7 +22,6 @@ import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePr
import org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties;
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.StringUtils;
@@ -44,29 +41,28 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
* @see JsonConverter
* @see XmlConverter
*/
-public class SentinelDataSourceHandler {
+public class SentinelDataSourceHandler implements SmartInitializingSingleton {
private static final Logger logger = LoggerFactory
.getLogger(SentinelDataSourceHandler.class);
private List dataTypeList = Arrays.asList("json", "xml");
- private List dataSourceBeanNameList = Collections
- .synchronizedList(new ArrayList<>());
+ private final String DATA_TYPE_FIELD = "dataType";
+ private final String CUSTOM_DATA_TYPE = "custom";
+ private final String CONVERTER_CLASS_FIELD = "converterClass";
- private final String DATATYPE_FIELD = "dataType";
- private final String CUSTOM_DATATYPE = "custom";
- private final String CONVERTERCLASS_FIELD = "converterClass";
+ private final DefaultListableBeanFactory beanFactory;
+
+ public SentinelDataSourceHandler(DefaultListableBeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
@Autowired
private SentinelProperties sentinelProperties;
- @EventListener(classes = ApplicationStartedEvent.class)
- public void buildDataSource(ApplicationStartedEvent event) throws Exception {
-
- DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event
- .getApplicationContext().getAutowireCapableBeanFactory();
-
+ @Override
+ public void afterSingletonsInstantiated() {
// commercialization
if (!StringUtils.isEmpty(System.getProperties()
.getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
@@ -101,9 +97,8 @@ public class SentinelDataSourceHandler {
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
.getValidDataSourceProperties();
abstractDataSourceProperties.preCheck(dataSourceName);
- registerBean(beanFactory, abstractDataSourceProperties,
- dataSourceName + "-sentinel-" + validFields.get(0)
- + "-datasource");
+ registerBean(abstractDataSourceProperties, dataSourceName
+ + "-sentinel-" + validFields.get(0) + "-datasource");
}
catch (Exception e) {
logger.error("[Sentinel Starter] DataSource " + dataSourceName
@@ -112,8 +107,7 @@ public class SentinelDataSourceHandler {
});
}
- private void registerBean(DefaultListableBeanFactory beanFactory,
- final AbstractDataSourceProperties dataSourceProperties,
+ private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
String dataSourceName) {
Map propertyMap = Arrays
@@ -132,8 +126,8 @@ public class SentinelDataSourceHandler {
e);
}
}, HashMap::putAll);
- propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass());
- propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType());
+ propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceProperties.getConverterClass());
+ propertyMap.put(DATA_TYPE_FIELD, dataSourceProperties.getDataType());
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
@@ -141,81 +135,78 @@ public class SentinelDataSourceHandler {
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()))) {
+ if (null == field) {
+ return;
+ }
+ if (DATA_TYPE_FIELD.equals(propertyName)) {
+ String dataType = StringUtils.trimAllWhitespace(propertyValue.toString());
+ if (CUSTOM_DATA_TYPE.equals(dataType)) {
+ try {
+ if (StringUtils
+ .isEmpty(dataSourceProperties.getConverterClass())) {
throw new RuntimeException("[Sentinel Starter] DataSource "
- + dataSourceName + " dataType: " + propertyValue
- + " is not support now. please using these types: "
- + dataTypeList.toString());
+ + dataSourceName
+ + "dataType is custom, please set converter-class "
+ + "property");
}
- // converter type now support xml or json.
- // The bean name of these converters wrapped by
- // 'sentinel-{converterType}-{ruleType}-converter'
- builder.addPropertyReference("converter",
- "sentinel-" + propertyValue.toString() + "-"
- + dataSourceProperties.getRuleType().getName()
- + "-converter");
+ // construct custom Converter with 'converterClass'
+ // configuration and register
+ String customConvertBeanName = "sentinel-"
+ + dataSourceProperties.getConverterClass();
+ if (!this.beanFactory.containsBean(customConvertBeanName)) {
+ this.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 (CONVERTERCLASS_FIELD.equals(propertyName)) {
- return;
}
else {
- // wired properties
- Optional.ofNullable(propertyValue)
- .ifPresent(v -> builder.addPropertyValue(propertyName, v));
+ 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}-{ruleType}-converter'
+ builder.addPropertyReference("converter",
+ "sentinel-" + propertyValue.toString() + "-"
+ + dataSourceProperties.getRuleType().getName()
+ + "-converter");
}
}
+ else if (CONVERTER_CLASS_FIELD.equals(propertyName)) {
+ return;
+ }
+ else {
+ // wired properties
+ Optional.ofNullable(propertyValue)
+ .ifPresent(v -> builder.addPropertyValue(propertyName, v));
+ }
});
- beanFactory.registerBeanDefinition(dataSourceName, builder.getBeanDefinition());
+ this.beanFactory.registerBeanDefinition(dataSourceName,
+ builder.getBeanDefinition());
// init in Spring
- AbstractDataSource newDataSource = (AbstractDataSource) beanFactory
+ AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
.getBean(dataSourceName);
logAndCheckRuleType(newDataSource, dataSourceName,
@@ -234,7 +225,6 @@ public class SentinelDataSourceHandler {
DegradeRuleManager.register2Property(newDataSource.getProperty());
}
}
- dataSourceBeanNameList.add(dataSourceName);
}
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
@@ -282,9 +272,4 @@ public class SentinelDataSourceHandler {
+ ruleClass.getSimpleName() + ">. Class: " + ruleConfig.getClass());
}
}
-
- public List getDataSourceBeanNameList() {
- return dataSourceBeanNameList;
- }
-
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
index a1ab9e22..49bd003f 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java
@@ -17,9 +17,9 @@
package org.springframework.cloud.alibaba.sentinel.custom;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
-import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,7 +29,6 @@ import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.util.ClassUtils;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
@@ -37,19 +36,18 @@ import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
-import com.alibaba.csp.sentinel.util.StringUtil;
/**
* Interceptor using by SentinelRestTemplate
*
- * @author fangjian
+ * @author Jim
*/
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory
.getLogger(SentinelProtectInterceptor.class);
- private SentinelRestTemplate sentinelRestTemplate;
+ private final SentinelRestTemplate sentinelRestTemplate;
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) {
this.sentinelRestTemplate = sentinelRestTemplate;
@@ -82,18 +80,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
throw new IllegalStateException(e);
}
else {
- try {
- return handleBlockException(request, body, execution,
- (BlockException) e);
- }
- catch (Exception ex) {
- if (ex instanceof IllegalStateException) {
- throw (IllegalStateException) ex;
- }
- throw new IllegalStateException(
- "sentinel handle BlockException error: " + ex.getMessage(),
- ex);
- }
+ return handleBlockException(request, body, execution, (BlockException) e);
}
}
finally {
@@ -109,84 +96,49 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
}
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
- ClientHttpRequestExecution execution, BlockException ex) throws Exception {
+ ClientHttpRequestExecution execution, BlockException ex) {
Object[] args = new Object[] { request, body, execution, ex };
// handle degrade
if (isDegradeFailure(ex)) {
- Method method = extractFallbackMethod(sentinelRestTemplate.fallback(),
+ Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(),
sentinelRestTemplate.fallbackClass());
- if (method != null) {
- return (ClientHttpResponse) method.invoke(null, args);
+ if (fallbackMethod != null) {
+ return methodInvoke(fallbackMethod, args);
}
else {
return new SentinelClientHttpResponse();
}
}
- // handle block
+ // handle flow
Method blockHandler = extractBlockHandlerMethod(
sentinelRestTemplate.blockHandler(),
sentinelRestTemplate.blockHandlerClass());
if (blockHandler != null) {
- return (ClientHttpResponse) blockHandler.invoke(null, args);
+ return methodInvoke(blockHandler, args);
}
else {
return new SentinelClientHttpResponse();
}
}
+ private ClientHttpResponse methodInvoke(Method method, Object... args) {
+ try {
+ return (ClientHttpResponse) method.invoke(null, args);
+ }
+ catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private Method extractFallbackMethod(String fallback, Class> fallbackClass) {
- if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
- return null;
- }
- Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback);
- Class[] args = new Class[] { HttpRequest.class, byte[].class,
- ClientHttpRequestExecution.class, BlockException.class };
- if (cachedMethod == null) {
- cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, args);
- if (cachedMethod != null) {
- if (!ClientHttpResponse.class
- .isAssignableFrom(cachedMethod.getReturnType())) {
- throw new IllegalStateException(String.format(
- "the return type of method [%s] in class [%s] is not ClientHttpResponse in degrade",
- cachedMethod.getName(), fallbackClass.getCanonicalName()));
- }
- BlockClassRegistry.updateFallbackFor(fallbackClass, fallback,
- cachedMethod);
- }
- else {
- throw new IllegalStateException(String.format(
- "Cannot find method [%s] in class [%s] with parameters %s in degrade",
- fallback, fallbackClass.getCanonicalName(), Arrays.asList(args)));
- }
- }
- return cachedMethod;
+ return BlockClassRegistry.lookupFallback(fallbackClass, fallback);
}
private Method extractBlockHandlerMethod(String block, Class> blockClass) {
- if (StringUtil.isBlank(block) || blockClass == void.class) {
- return null;
- }
- Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block);
- Class[] args = new Class[] { HttpRequest.class, byte[].class,
- ClientHttpRequestExecution.class, BlockException.class };
- if (cachedMethod == null) {
- cachedMethod = ClassUtils.getStaticMethod(blockClass, block, args);
- if (cachedMethod != null) {
- if (!ClientHttpResponse.class
- .isAssignableFrom(cachedMethod.getReturnType())) {
- throw new IllegalStateException(String.format(
- "the return type of method [%s] in class [%s] is not ClientHttpResponse in flow control",
- cachedMethod.getName(), blockClass.getCanonicalName()));
- }
- BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod);
- }
- else {
- throw new IllegalStateException(String.format(
- "Cannot find method [%s] in class [%s] with parameters %s in flow control",
- block, blockClass.getCanonicalName(), Arrays.asList(args)));
- }
- }
- return cachedMethod;
+ return BlockClassRegistry.lookupBlockHandler(blockClass, block);
}
private boolean isDegradeFailure(BlockException ex) {
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
index 03b6c0f8..d2500768 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpoint.java
@@ -17,25 +17,20 @@
package org.springframework.cloud.alibaba.sentinel.endpoint;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
-import org.springframework.cloud.alibaba.sentinel.custom.SentinelDataSourceHandler;
-import org.springframework.context.ApplicationContext;
-import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.LogBase;
+import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
-import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
-import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+import com.alibaba.csp.sentinel.transport.config.TransportConfig;
/**
* Endpoint for Sentinel, contains ans properties and rules
@@ -44,43 +39,39 @@ import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
@Endpoint(id = "sentinel")
public class SentinelEndpoint {
- @Autowired
- private SentinelProperties sentinelProperties;
+ private final SentinelProperties sentinelProperties;
- @Autowired
- private SentinelDataSourceHandler dataSourceHandler;
-
- @Autowired
- private ApplicationContext applicationContext;
+ public SentinelEndpoint(SentinelProperties sentinelProperties) {
+ this.sentinelProperties = sentinelProperties;
+ }
@ReadOperation
public Map invoke() {
final Map result = new HashMap<>();
+ if (sentinelProperties.isEnabled()) {
- List flowRules = FlowRuleManager.getRules();
- List degradeRules = DegradeRuleManager.getRules();
- List systemRules = SystemRuleManager.getRules();
- List paramFlowRules = ParamFlowRuleManager.getRules();
- result.put("properties", sentinelProperties);
- result.put("FlowRules", flowRules);
- result.put("DegradeRules", degradeRules);
- result.put("SystemRules", systemRules);
- result.put("ParamFlowRule", paramFlowRules);
- result.put("datasources", new HashMap());
- 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());
- }
-
- });
+ result.put("logDir", LogBase.getLogBaseDir());
+ result.put("logUsePid", LogBase.isLogNameUsePid());
+ result.put("blockPage", WebServletConfig.getBlockPage());
+ result.put("metricsFileSize", SentinelConfig.singleMetricFileSize());
+ result.put("metricsFileCharset", SentinelConfig.charset());
+ result.put("totalMetricsFileCount", SentinelConfig.totalMetricFileCount());
+ result.put("consoleServer", TransportConfig.getConsoleServer());
+ result.put("clientIp", TransportConfig.getHeartbeatClientIp());
+ result.put("heartbeatIntervalMs", TransportConfig.getHeartbeatIntervalMs());
+ result.put("clientPort", TransportConfig.getPort());
+ result.put("coldFactor", sentinelProperties.getFlow().getColdFactor());
+ result.put("filter", sentinelProperties.getFilter());
+ result.put("datasource", sentinelProperties.getDatasource());
+ final Map rules = new HashMap<>();
+ result.put("rules", rules);
+ rules.put("flowRules", FlowRuleManager.getRules());
+ rules.put("degradeRules", SystemRuleManager.getRules());
+ rules.put("systemRules", SystemRuleManager.getRules());
+ rules.put("authorityRule", AuthorityRuleManager.getRules());
+ rules.put("paramFlowRule", ParamFlowRuleManager.getRules());
+ }
return result;
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
index 098822a2..7c9b5d1c 100644
--- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
+++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/endpoint/SentinelEndpointAutoConfiguration.java
@@ -34,8 +34,8 @@ public class SentinelEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
- public SentinelEndpoint sentinelEndPoint() {
- return new SentinelEndpoint();
+ public SentinelEndpoint sentinelEndPoint(SentinelProperties sentinelProperties) {
+ return new SentinelEndpoint(sentinelProperties);
}
}
diff --git a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index d05b0863..b445fc6a 100644
--- a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -18,6 +18,11 @@
"defaultValue": "8719",
"description": "sentinel api port."
},
+ {
+ "name": "spring.cloud.sentinel.transport.clientIp",
+ "type": "java.lang.String",
+ "description": "sentinel client ip connect to dashboard."
+ },
{
"name": "spring.cloud.sentinel.transport.dashboard",
"type": "java.lang.String",
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
index 1ba2c170..2cd3b53a 100644
--- a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java
@@ -16,76 +16,268 @@
package org.springframework.cloud.alibaba.sentinel;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.junit.Before;
import org.junit.Test;
-import org.springframework.boot.autoconfigure.AutoConfigurations;
-import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
-import org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor;
+import org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpoint;
+import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.LogBase;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+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.transport.config.TransportConfig;
/**
- * @author fangjian
+ * @author Jim
* @author jiashuai.xie
*/
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {
+ SentinelAutoConfigurationTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.filter.order=123",
+ "spring.cloud.sentinel.filter.urlPatterns=/*,/test",
+ "spring.cloud.sentinel.metric.fileSingleSize=9999",
+ "spring.cloud.sentinel.metric.fileTotalCount=100",
+ "spring.cloud.sentinel.servlet.blockPage=/error",
+ "spring.cloud.sentinel.flow.coldFactor=3",
+ "spring.cloud.sentinel.eager=true",
+ "spring.cloud.sentinel.log.switchPid=true",
+ "spring.cloud.sentinel.transport.dashboard=http://localhost:8080",
+ "spring.cloud.sentinel.transport.port=9999",
+ "spring.cloud.sentinel.transport.clientIp=1.1.1.1",
+ "spring.cloud.sentinel.transport.heartbeatIntervalMs=20000" }, webEnvironment = RANDOM_PORT)
public class SentinelAutoConfigurationTests {
- private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
- .withConfiguration(AutoConfigurations.of(SentinelAutoConfiguration.class,
- SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class))
- .withPropertyValues("spring.cloud.sentinel.transport.port=8888")
- .withPropertyValues("spring.cloud.sentinel.filter.order=123")
- .withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Autowired
+ private FilterRegistrationBean filterRegistrationBean;
+
+ @Autowired
+ private SentinelBeanPostProcessor sentinelBeanPostProcessor;
+
+ @Autowired
+ private RestTemplate restTemplate;
+
+ @Autowired
+ private RestTemplate restTemplateWithBlockClass;
+
+ @Autowired
+ private RestTemplate restTemplateWithoutBlockClass;
+
+ @Autowired
+ private RestTemplate restTemplateWithFallbackClass;
+
+ @LocalServerPort
+ private int port;
+
+ private String url = "http://localhost:" + port;
+
+ @Before
+ public void setUp() {
+ FlowRule rule = new FlowRule();
+ rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule.setCount(0);
+ rule.setResource(url);
+ rule.setLimitApp("default");
+ rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRuleManager.loadRules(Arrays.asList(rule));
+
+ DegradeRule degradeRule = new DegradeRule();
+ degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
+ degradeRule.setResource(url + "/test");
+ degradeRule.setCount(0);
+ degradeRule.setTimeWindow(60);
+ DegradeRuleManager.loadRules(Arrays.asList(degradeRule));
+ }
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("FilterRegistrationBean was not created", filterRegistrationBean);
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+ assertNotNull("SentinelBeanPostProcessor was not created",
+ sentinelBeanPostProcessor);
+
+ checkSentinelLog();
+ checkSentinelEager();
+ checkSentinelTransport();
+ checkSentinelColdFactor();
+ checkSentinelMetric();
+ checkSentinelFilter();
+ checkEndpoint();
+ }
+
+ private void checkEndpoint() {
+ SentinelEndpoint sentinelEndpoint = new SentinelEndpoint(sentinelProperties);
+ Map map = sentinelEndpoint.invoke();
+ assertEquals("Endpoint Sentinel log pid was wrong", true, map.get("logUsePid"));
+ assertEquals("Endpoint Sentinel transport console server was wrong",
+ "http://localhost:8080", map.get("consoleServer"));
+ assertEquals("Endpoint Sentinel transport port was wrong", "9999",
+ map.get("clientPort"));
+ assertEquals("Endpoint Sentinel transport heartbeatIntervalMs was wrong", 20000l,
+ map.get("heartbeatIntervalMs"));
+ assertEquals("Endpoint Sentinel transport clientIp was wrong", "1.1.1.1",
+ map.get("clientIp"));
+ assertEquals("Endpoint Sentinel metric file size was wrong", 9999l,
+ map.get("metricsFileSize"));
+ assertEquals("Endpoint Sentinel metric file count was wrong", 100,
+ map.get("totalMetricsFileCount"));
+ assertEquals("Endpoint Sentinel metric file charset was wrong", "UTF-8",
+ map.get("metricsFileCharset"));
+ assertEquals("Endpoint Sentinel block page was wrong", "/error",
+ map.get("blockPage"));
+ }
+
+ private void checkSentinelFilter() {
+ assertEquals("SentinelProperties filter order was wrong", 123,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 2,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern item was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ assertEquals("SentinelProperties filter url pattern item was wrong", "/test",
+ sentinelProperties.getFilter().getUrlPatterns().get(1));
+ }
+
+ private void checkSentinelMetric() {
+ assertEquals("SentinelProperties metric charset was wrong", "UTF-8",
+ sentinelProperties.getMetric().getCharset());
+ assertEquals("SentinelProperties metric file single size was wrong", "9999",
+ sentinelProperties.getMetric().getFileSingleSize());
+ assertEquals("SentinelProperties metric file total count was wrong", "100",
+ sentinelProperties.getMetric().getFileTotalCount());
+ }
+
+ private void checkSentinelColdFactor() {
+ assertEquals("SentinelProperties coldFactor was wrong", "3",
+ sentinelProperties.getFlow().getColdFactor());
+ }
+
+ private void checkSentinelTransport() {
+ assertEquals("SentinelProperties transport port was wrong", "9999",
+ sentinelProperties.getTransport().getPort());
+ assertEquals("SentinelProperties transport dashboard was wrong",
+ "http://localhost:8080",
+ sentinelProperties.getTransport().getDashboard());
+ assertEquals("SentinelProperties transport clientIp was wrong", "1.1.1.1",
+ sentinelProperties.getTransport().getClientIp());
+ assertEquals("SentinelProperties transport heartbeatIntervalMs was wrong",
+ "20000", sentinelProperties.getTransport().getHeartbeatIntervalMs());
+ }
+
+ private void checkSentinelEager() {
+ assertEquals("SentinelProperties eager was wrong", true,
+ sentinelProperties.isEager());
+ }
+
+ private void checkSentinelLog() {
+ assertEquals("SentinelProperties log file pid was wrong", true,
+ sentinelProperties.getLog().isSwitchPid());
+ }
@Test
public void testFilter() {
- this.contextRunner.run(context -> {
- assertThat(context.getBean("servletRequestListener")
- .getClass() == FilterRegistrationBean.class).isTrue();
+ assertEquals("Sentinel Filter order was wrong", filterRegistrationBean.getOrder(),
+ 123);
+ assertEquals("Sentinel Filter url-pattern was wrong",
+ filterRegistrationBean.getUrlPatterns().size(), 2);
+ }
+
+ @Test
+ public void testSentinelSystemProperties() {
+ assertEquals("Sentinel log pid was wrong", true, LogBase.isLogNameUsePid());
+ assertEquals("Sentinel transport console server was wrong",
+ "http://localhost:8080", TransportConfig.getConsoleServer());
+ assertEquals("Sentinel transport port was wrong", "9999",
+ TransportConfig.getPort());
+ assertEquals("Sentinel transport heartbeatIntervalMs was wrong", 20000l,
+ TransportConfig.getHeartbeatIntervalMs().longValue());
+ assertEquals("Sentinel transport clientIp was wrong", "1.1.1.1",
+ TransportConfig.getHeartbeatClientIp());
+ assertEquals("Sentinel metric file size was wrong", 9999,
+ SentinelConfig.singleMetricFileSize());
+ assertEquals("Sentinel metric file count was wrong", 100,
+ SentinelConfig.totalMetricFileCount());
+ assertEquals("Sentinel metric file charset was wrong", "UTF-8",
+ SentinelConfig.charset());
+ assertEquals("Sentinel block page was wrong", "/error",
+ WebServletConfig.getBlockPage());
+ }
+
+ @Test
+ public void testFlowRestTemplate() {
+ assertEquals("RestTemplate interceptors size was wrong", 2,
+ restTemplate.getInterceptors().size());
+ assertEquals("RestTemplateWithBlockClass interceptors size was wrong", 1,
+ restTemplateWithBlockClass.getInterceptors().size());
+ ResponseEntity responseEntityBlock = restTemplateWithBlockClass.getForEntity(url,
+ String.class);
+ assertEquals("RestTemplateWithBlockClass Sentinel Block Message was wrong",
+ "Oops", responseEntityBlock.getBody());
+ assertEquals(
+ "RestTemplateWithBlockClass Sentinel Block Http Status Code was wrong",
+ HttpStatus.OK, responseEntityBlock.getStatusCode());
+ ResponseEntity responseEntityRaw = restTemplate.getForEntity(url, String.class);
+ assertEquals("RestTemplate Sentinel Block Message was wrong",
+ "RestTemplate request block by sentinel", responseEntityRaw.getBody());
+ assertEquals("RestTemplate Sentinel Block Http Status Code was wrong",
+ HttpStatus.OK, responseEntityRaw.getStatusCode());
+ }
+
+ @Test
+ public void testNormalRestTemplate() {
+ assertEquals("RestTemplateWithoutBlockClass interceptors size was wrong", 0,
+ restTemplateWithoutBlockClass.getInterceptors().size());
+ assertThatExceptionOfType(RestClientException.class).isThrownBy(() -> {
+ restTemplateWithoutBlockClass.getForEntity(url, String.class);
});
}
@Test
- public void testBeanPostProcessor() {
- this.contextRunner.run(context -> {
- assertThat(context.getBean("sentinelBeanPostProcessor")
- .getClass() == SentinelBeanPostProcessor.class).isTrue();
- });
- }
-
- @Test
- public void testProperties() {
- this.contextRunner.run(context -> {
- SentinelProperties sentinelProperties = context
- .getBean(SentinelProperties.class);
- assertThat(sentinelProperties.getTransport().getPort()).isEqualTo("8888");
- assertThat(sentinelProperties.getFilter().getUrlPatterns().size())
- .isEqualTo(2);
- assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0))
- .isEqualTo("/*");
- assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1))
- .isEqualTo("/test");
- });
- }
-
- @Test
- public void testRestTemplate() {
- this.contextRunner.run(context -> {
- assertThat(context.getBeansOfType(RestTemplate.class).size()).isEqualTo(2);
- RestTemplate restTemplate = context.getBean("restTemplateWithBlockClass",
- RestTemplate.class);
- assertThat(restTemplate.getInterceptors().size()).isEqualTo(1);
- assertThat(restTemplate.getInterceptors().get(0).getClass())
- .isEqualTo(SentinelProtectInterceptor.class);
- });
+ public void testFallbackRestTemplate() {
+ ResponseEntity responseEntity = restTemplateWithFallbackClass
+ .getForEntity(url + "/test", String.class);
+ assertEquals("RestTemplateWithFallbackClass Sentinel Message was wrong",
+ "Oops fallback", responseEntity.getBody());
+ assertEquals("RestTemplateWithFallbackClass Sentinel Http Status Code was wrong",
+ HttpStatus.OK, responseEntity.getStatusCode());
}
@Configuration
@@ -94,7 +286,9 @@ public class SentinelAutoConfigurationTests {
@Bean
@SentinelRestTemplate
RestTemplate restTemplate() {
- return new RestTemplate();
+ RestTemplate restTemplate = new RestTemplate();
+ restTemplate.getInterceptors().add(mock(ClientHttpRequestInterceptor.class));
+ return restTemplate;
}
@Bean
@@ -103,12 +297,38 @@ public class SentinelAutoConfigurationTests {
return new RestTemplate();
}
+ @Bean
+ @SentinelRestTemplate(fallbackClass = ExceptionUtil.class, fallback = "fallbackException")
+ RestTemplate restTemplateWithFallbackClass() {
+ return new RestTemplate();
+ }
+
+ @Bean
+ RestTemplate restTemplateWithoutBlockClass() {
+ return new RestTemplate();
+ }
+
}
- static class ExceptionUtil {
- public static void handleException(BlockException ex) {
+ public static class ExceptionUtil {
+ public static SentinelClientHttpResponse handleException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops");
+ }
+
+ public static SentinelClientHttpResponse fallbackException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops fallback");
}
}
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class })
+ public static class TestConfig {
+ }
+
}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java
new file mode 100644
index 00000000..b22f17e8
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelBeanAutowiredTests.java
@@ -0,0 +1,134 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
+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;
+import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelBeanAutowiredTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.filter.order=111" })
+public class SentinelBeanAutowiredTests {
+
+ @Autowired
+ private UrlCleaner urlCleaner;
+
+ @Autowired
+ private UrlBlockHandler urlBlockHandler;
+
+ @Autowired
+ private RequestOriginParser requestOriginParser;
+
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("UrlCleaner was not created", urlCleaner);
+ assertNotNull("UrlBlockHandler was not created", urlBlockHandler);
+ assertNotNull("RequestOriginParser was not created", requestOriginParser);
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+
+ checkUrlPattern();
+ }
+
+ private void checkUrlPattern() {
+ assertEquals("SentinelProperties filter order was wrong", 111,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 1,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ }
+
+ @Test
+ public void testBeanAutowired() {
+ assertEquals("UrlCleaner was not autowired", urlCleaner,
+ WebCallbackManager.getUrlCleaner());
+ assertEquals("UrlBlockHandler was not autowired", urlBlockHandler,
+ WebCallbackManager.getUrlBlockHandler());
+ assertEquals("RequestOriginParser was not autowired", requestOriginParser,
+ WebCallbackManager.getRequestOriginParser());
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class })
+ public static class TestConfig {
+
+ @Bean
+ public UrlCleaner urlCleaner() {
+ return new UrlCleaner() {
+ @Override
+ public String clean(String s) {
+ return s;
+ }
+ };
+ }
+
+ @Bean
+ public RequestOriginParser requestOriginParser() {
+ return new RequestOriginParser() {
+ @Override
+ public String parseOrigin(HttpServletRequest httpServletRequest) {
+ return httpServletRequest.getRemoteAddr();
+ }
+ };
+ }
+
+ @Bean
+ public UrlBlockHandler urlBlockHandler() {
+ return new UrlBlockHandler() {
+ @Override
+ public void blocked(HttpServletRequest httpServletRequest,
+ HttpServletResponse httpServletResponse, BlockException e)
+ throws IOException {
+ FilterUtil.blockRequest(httpServletRequest, httpServletResponse);
+ }
+ };
+ }
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java
new file mode 100644
index 00000000..13a6e322
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelDataSourceTests.java
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
+import org.springframework.cloud.alibaba.sentinel.datasource.RuleType;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelDataSourceTests.TestConfig.class }, properties = {
+ "spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json",
+ "spring.cloud.sentinel.datasource.ds1.file.data-type=json",
+ "spring.cloud.sentinel.datasource.ds1.file.rule-type=flow",
+
+ "spring.cloud.sentinel.datasource.ds2.file.file=classpath: degraderule.json",
+ "spring.cloud.sentinel.datasource.ds2.file.data-type=json",
+ "spring.cloud.sentinel.datasource.ds2.file.rule-type=degrade",
+
+ "spring.cloud.sentinel.datasource.ds3.file.file=classpath: authority.json",
+ "spring.cloud.sentinel.datasource.ds3.file.rule-type=authority",
+
+ "spring.cloud.sentinel.datasource.ds4.file.file=classpath: system.json",
+ "spring.cloud.sentinel.datasource.ds4.file.rule-type=system",
+
+ "spring.cloud.sentinel.datasource.ds5.file.file=classpath: param-flow.json",
+ "spring.cloud.sentinel.datasource.ds5.file.data-type=custom",
+ "spring.cloud.sentinel.datasource.ds5.file.converter-class=org.springframework.cloud.alibaba.sentinel.TestConverter",
+ "spring.cloud.sentinel.datasource.ds5.file.rule-type=param-flow" })
+public class SentinelDataSourceTests {
+
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("SentinelProperties was not created", sentinelProperties);
+
+ checkUrlPattern();
+ }
+
+ private void checkUrlPattern() {
+ assertEquals("SentinelProperties filter order was wrong", Integer.MIN_VALUE,
+ sentinelProperties.getFilter().getOrder());
+ assertEquals("SentinelProperties filter url pattern size was wrong", 1,
+ sentinelProperties.getFilter().getUrlPatterns().size());
+ assertEquals("SentinelProperties filter url pattern was wrong", "/*",
+ sentinelProperties.getFilter().getUrlPatterns().get(0));
+ }
+
+ @Test
+ public void testDataSource() {
+ assertEquals("DataSource size was wrong", 5,
+ sentinelProperties.getDatasource().size());
+ assertNull("DataSource ds1 apollo is not null",
+ sentinelProperties.getDatasource().get("ds1").getApollo());
+ assertNull("DataSource ds1 nacos is not null",
+ sentinelProperties.getDatasource().get("ds1").getNacos());
+ assertNull("DataSource ds1 zk is not null",
+ sentinelProperties.getDatasource().get("ds1").getZk());
+ assertNotNull("DataSource ds1 file is null",
+ sentinelProperties.getDatasource().get("ds1").getFile());
+
+ assertEquals("DataSource ds1 file dataType was wrong", "json",
+ sentinelProperties.getDatasource().get("ds1").getFile().getDataType());
+ assertEquals("DataSource ds1 file ruleType was wrong", RuleType.FLOW,
+ sentinelProperties.getDatasource().get("ds1").getFile().getRuleType());
+
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelAutoConfiguration.class,
+ SentinelWebAutoConfiguration.class })
+ public static class TestConfig {
+
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java
new file mode 100644
index 00000000..3f56f700
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelFeignTests.java
@@ -0,0 +1,178 @@
+/*
+ * 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;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeignAutoConfiguration;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
+
+/**
+ * @author Jim
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = { SentinelFeignTests.TestConfig.class }, properties = {
+ "feign.sentinel.enabled=true" })
+public class SentinelFeignTests {
+
+ @Autowired
+ private EchoService echoService;
+
+ @Autowired
+ private FooService fooService;
+
+ @Autowired
+ private BarService barService;
+
+ @Before
+ public void setUp() {
+ FlowRule rule1 = new FlowRule();
+ rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule1.setCount(0);
+ rule1.setResource("GET:http://test-service/echo/{str}");
+ rule1.setLimitApp("default");
+ rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule1.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRule rule2 = new FlowRule();
+ rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule2.setCount(0);
+ rule2.setResource("GET:http://foo-service/echo/{str}");
+ rule2.setLimitApp("default");
+ rule2.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule2.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRule rule3 = new FlowRule();
+ rule3.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ rule3.setCount(0);
+ rule3.setResource("GET:http://bar-service/bar");
+ rule3.setLimitApp("default");
+ rule3.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
+ rule3.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRuleManager.loadRules(Arrays.asList(rule1, rule2, rule3));
+ }
+
+ @Test
+ public void contextLoads() throws Exception {
+ assertNotNull("EchoService was not created", echoService);
+ assertNotNull("FooService was not created", fooService);
+ }
+
+ @Test
+ public void testFeignClient() {
+ assertEquals("Sentinel Feign Client fallback success", "echo fallback",
+ echoService.echo("test"));
+ assertEquals("Sentinel Feign Client fallbackFactory success", "foo fallback",
+ fooService.echo("test"));
+ assertThatExceptionOfType(Exception.class).isThrownBy(() -> {
+ barService.bar();
+ });
+
+ assertNotEquals("ToString method invoke was not in SentinelInvocationHandler",
+ echoService.toString(), fooService.toString());
+ assertNotEquals("HashCode method invoke was not in SentinelInvocationHandler",
+ echoService.hashCode(), fooService.hashCode());
+ assertFalse("Equals method invoke was not in SentinelInvocationHandler",
+ echoService.equals(fooService));
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @ImportAutoConfiguration({ SentinelFeignAutoConfiguration.class })
+ @EnableFeignClients
+ public static class TestConfig {
+
+ @Bean
+ public EchoServiceFallback echoServiceFallback() {
+ return new EchoServiceFallback();
+ }
+
+ @Bean
+ public CustomFallbackFactory customFallbackFactory() {
+ return new CustomFallbackFactory();
+ }
+
+ }
+
+ @FeignClient(value = "test-service", fallback = EchoServiceFallback.class)
+ public interface EchoService {
+ @RequestMapping(path = "echo/{str}")
+ String echo(@RequestParam("str") String param);
+ }
+
+ @FeignClient(value = "foo-service", fallbackFactory = CustomFallbackFactory.class)
+ public interface FooService {
+ @RequestMapping(path = "echo/{str}")
+ String echo(@RequestParam("str") String param);
+ }
+
+ @FeignClient(value = "bar-service")
+ public interface BarService {
+ @RequestMapping(path = "bar")
+ String bar();
+ }
+
+ public static class EchoServiceFallback implements EchoService {
+
+ @Override
+ public String echo(@RequestParam("str") String param) {
+ return "echo fallback";
+ }
+
+ }
+
+ public static class FooServiceFallback implements FooService {
+
+ @Override
+ public String echo(@RequestParam("str") String param) {
+ return "foo fallback";
+ }
+ }
+
+ public static class CustomFallbackFactory
+ implements feign.hystrix.FallbackFactory {
+
+ private FooService fooService = new FooServiceFallback();
+
+ @Override
+ public FooService create(Throwable throwable) {
+ return fooService;
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java
new file mode 100644
index 00000000..f43af925
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java
@@ -0,0 +1,248 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
+import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
+import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author Jim
+ */
+public class SentinelRestTemplateTests {
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkMethod() {
+ new AnnotationConfigApplicationContext(TestConfig1.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkClass() {
+ new AnnotationConfigApplicationContext(TestConfig2.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testblkMethod() {
+ new AnnotationConfigApplicationContext(TestConfig3.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testblkClass() {
+ new AnnotationConfigApplicationContext(TestConfig4.class);
+ }
+
+ @Test
+ public void testNormal() {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
+ TestConfig5.class);
+ assertEquals("RestTemplate size was wrong", 1,
+ context.getBeansOfType(RestTemplate.class).size());
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testBlkMethodExists() {
+ new AnnotationConfigApplicationContext(TestConfig6.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkMethodExists() {
+ new AnnotationConfigApplicationContext(TestConfig7.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testBlkReturnValue() {
+ new AnnotationConfigApplicationContext(TestConfig8.class);
+ }
+
+ @Test(expected = BeanCreationException.class)
+ public void testFbkReturnValue() {
+ new AnnotationConfigApplicationContext(TestConfig9.class);
+ }
+
+ @Configuration
+ public static class TestConfig1 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallback = "fbk")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig2 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = ExceptionUtil.class)
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig3 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandler = "blk")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig4 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class)
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig5 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException", fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig6 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException1")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig7 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException1")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig8 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(blockHandlerClass = SentinelRestTemplateTests.ExceptionUtil.class, blockHandler = "handleException2")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ @Configuration
+ public static class TestConfig9 {
+ @Bean
+ SentinelBeanPostProcessor sentinelBeanPostProcessor(
+ ApplicationContext applicationContext) {
+ return new SentinelBeanPostProcessor(applicationContext);
+ }
+
+ @Bean
+ @SentinelRestTemplate(fallbackClass = SentinelRestTemplateTests.ExceptionUtil.class, fallback = "fallbackException2")
+ RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+ }
+
+ public static class ExceptionUtil {
+ public static SentinelClientHttpResponse handleException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops");
+ }
+
+ public static void handleException2(HttpRequest request, byte[] body,
+ ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+
+ public static SentinelClientHttpResponse fallbackException(HttpRequest request,
+ byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ return new SentinelClientHttpResponse("Oops fallback");
+ }
+
+ public static void fallbackException2(HttpRequest request, byte[] body,
+ ClientHttpRequestExecution execution, BlockException ex) {
+ System.out.println("Oops: " + ex.getClass().getCanonicalName());
+ }
+ }
+
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java
new file mode 100644
index 00000000..e7247a41
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/TestConverter.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author Jim
+ */
+public class TestConverter implements Converter> {
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ @Override
+ public List convert(String s) {
+ try {
+ return objectMapper.readValue(s, new TypeReference>() {
+ });
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/authority.json b/spring-cloud-alibaba-sentinel/src/test/resources/authority.json
new file mode 100644
index 00000000..3fb4b249
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/authority.json
@@ -0,0 +1,17 @@
+[
+ {
+ "resource": "good",
+ "limitApp": "abc",
+ "strategy": 0
+ },
+ {
+ "resource": "bad",
+ "limitApp": "bcd",
+ "strategy": 1
+ },
+ {
+ "resource": "terrible",
+ "limitApp": "aaa",
+ "strategy": 1
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json b/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json
new file mode 100644
index 00000000..5977c5fc
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/degraderule.json
@@ -0,0 +1,16 @@
+[
+ {
+ "resource": "abc0",
+ "count": 20.0,
+ "grade": 0,
+ "passCount": 0,
+ "timeWindow": 10
+ },
+ {
+ "resource": "abc1",
+ "count": 15.0,
+ "grade": 0,
+ "passCount": 0,
+ "timeWindow": 10
+ }
+]
\ No newline at end of file
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json b/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json
new file mode 100644
index 00000000..d798f805
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/flowrule.json
@@ -0,0 +1,26 @@
+[
+ {
+ "resource": "resource",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ },
+ {
+ "resource": "p",
+ "controlBehavior": 0,
+ "count": 1,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ },
+ {
+ "resource": "http://www.taobao.com",
+ "controlBehavior": 0,
+ "count": 0,
+ "grade": 1,
+ "limitApp": "default",
+ "strategy": 0
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json b/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json
new file mode 100644
index 00000000..72e1c2dc
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/param-flow.json
@@ -0,0 +1,16 @@
+[
+ {
+ "resource": "hotResource",
+ "count": 0,
+ "grade": 1,
+ "limitApp": "default",
+ "paramIdx": 0,
+ "paramFlowItemList": [
+ {
+ "object": "2",
+ "classType": "int",
+ "count": 1
+ }
+ ]
+ }
+]
diff --git a/spring-cloud-alibaba-sentinel/src/test/resources/system.json b/spring-cloud-alibaba-sentinel/src/test/resources/system.json
new file mode 100644
index 00000000..7aa623a7
--- /dev/null
+++ b/spring-cloud-alibaba-sentinel/src/test/resources/system.json
@@ -0,0 +1,8 @@
+[
+ {
+ "highestSystemLoad": -1,
+ "qps": 100,
+ "avgRt": -1,
+ "maxThread": 10
+ }
+]