mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
Merge pull request #279 from fangjian0423/master
Refactor sentinel starter and add test case
This commit is contained in:
commit
4c53aa44f0
@ -42,6 +42,13 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||||
|
@ -23,6 +23,9 @@ public interface SentinelConstants {
|
|||||||
|
|
||||||
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
||||||
|
|
||||||
|
String BLOCK_TYPE = "block";
|
||||||
|
String FALLBACK_TYPE = "fallback";
|
||||||
|
|
||||||
// commercialization
|
// commercialization
|
||||||
|
|
||||||
String FLOW_DATASOURCE_NAME = "edas-flow";
|
String FLOW_DATASOURCE_NAME = "edas-flow";
|
||||||
|
@ -21,7 +21,6 @@ import java.util.Map;
|
|||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
|
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.validation.annotation.Validated;
|
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;
|
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* {@link ConfigurationProperties} for Sentinel.
|
||||||
|
*
|
||||||
* @author xiaojing
|
* @author xiaojing
|
||||||
* @author hengyunabc
|
* @author hengyunabc
|
||||||
* @author jiashuai.xie
|
* @author jiashuai.xie
|
||||||
@ -41,58 +42,51 @@ import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
|||||||
public class SentinelProperties {
|
public class SentinelProperties {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* earlier initialize heart-beat when the spring container starts <note> when the
|
* Earlier initialize heart-beat when the spring container starts when the transport
|
||||||
* transport dependency is on classpath ,the configuration is effective </note>
|
* dependency is on classpath, the configuration is effective.
|
||||||
*/
|
*/
|
||||||
private boolean eager = false;
|
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;
|
private boolean enabled = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'
|
* Configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'.
|
||||||
*/
|
*/
|
||||||
private Map<String, DataSourcePropertiesConfiguration> datasource = new TreeMap<>(
|
private Map<String, DataSourcePropertiesConfiguration> datasource = new TreeMap<>(
|
||||||
String.CASE_INSENSITIVE_ORDER);
|
String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* transport configuration about dashboard and client
|
* Transport configuration about dashboard and client.
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Transport transport = new Transport();
|
private Transport transport = new Transport();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* metric configuration about resource
|
* Metric configuration about resource.
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Metric metric = new Metric();
|
private Metric metric = new Metric();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* web servlet configuration <note> when the application is web ,the configuration is
|
* Web servlet configuration when the application is web, the configuration is
|
||||||
* effective </note>
|
* effective.
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Servlet servlet = new Servlet();
|
private Servlet servlet = new Servlet();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sentinel filter <note> when the application is web ,the configuration is effective
|
* Sentinel filter when the application is web, the configuration is effective.
|
||||||
* </note>
|
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Filter filter = new Filter();
|
private Filter filter = new Filter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flow configuration
|
* Sentinel Flow configuration.
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Flow flow = new Flow();
|
private Flow flow = new Flow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sentinel log configuration {@link LogBase}
|
* Sentinel log configuration {@link LogBase}.
|
||||||
*/
|
*/
|
||||||
@NestedConfigurationProperty
|
|
||||||
private Log log = new Log();
|
private Log log = new Log();
|
||||||
|
|
||||||
public boolean isEager() {
|
public boolean isEager() {
|
||||||
@ -170,7 +164,7 @@ public class SentinelProperties {
|
|||||||
public static class Flow {
|
public static class Flow {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the cold factor {@link SentinelConfig#COLD_FACTOR}
|
* The cold factor {@link SentinelConfig#COLD_FACTOR}.
|
||||||
*/
|
*/
|
||||||
private String coldFactor = "3";
|
private String coldFactor = "3";
|
||||||
|
|
||||||
@ -187,7 +181,7 @@ public class SentinelProperties {
|
|||||||
public static class Servlet {
|
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;
|
private String blockPage;
|
||||||
|
|
||||||
@ -203,17 +197,17 @@ public class SentinelProperties {
|
|||||||
public static class Metric {
|
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;
|
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;
|
private String fileTotalCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* charset when sentinel write or search metric file
|
* Charset when sentinel write or search metric file.
|
||||||
* {@link SentinelConfig#CHARSET}
|
* {@link SentinelConfig#CHARSET}
|
||||||
*/
|
*/
|
||||||
private String charset = "UTF-8";
|
private String charset = "UTF-8";
|
||||||
@ -246,22 +240,28 @@ public class SentinelProperties {
|
|||||||
public static class Transport {
|
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";
|
private String port = "8719";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sentinel dashboard address, won't try to connect dashboard when address is
|
* Sentinel dashboard address, won't try to connect dashboard when address is
|
||||||
* empty {@link TransportConfig#CONSOLE_SERVER}
|
* empty {@link TransportConfig#CONSOLE_SERVER}.
|
||||||
*/
|
*/
|
||||||
private String dashboard = "";
|
private String dashboard = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send heartbeat interval millisecond
|
* Send heartbeat interval millisecond
|
||||||
* {@link TransportConfig#HEARTBEAT_INTERVAL_MS}
|
* {@link TransportConfig#HEARTBEAT_INTERVAL_MS}.
|
||||||
*/
|
*/
|
||||||
private String heartbeatIntervalMs;
|
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() {
|
public String getHeartbeatIntervalMs() {
|
||||||
return heartbeatIntervalMs;
|
return heartbeatIntervalMs;
|
||||||
}
|
}
|
||||||
@ -286,17 +286,24 @@ public class SentinelProperties {
|
|||||||
this.dashboard = dashboard;
|
this.dashboard = dashboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClientIp() {
|
||||||
|
return clientIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientIp(String clientIp) {
|
||||||
|
this.clientIp = clientIp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Filter {
|
public static class Filter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sentinel filter chain order.
|
* Sentinel filter chain order.
|
||||||
*/
|
*/
|
||||||
private int order = Ordered.HIGHEST_PRECEDENCE;
|
private int order = Ordered.HIGHEST_PRECEDENCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL pattern for sentinel filter,default is /*
|
* URL pattern for sentinel filter, default is /*
|
||||||
*/
|
*/
|
||||||
private List<String> urlPatterns;
|
private List<String> urlPatterns;
|
||||||
|
|
||||||
@ -320,12 +327,12 @@ public class SentinelProperties {
|
|||||||
public static class Log {
|
public static class Log {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sentinel log base dir
|
* Sentinel log base dir.
|
||||||
*/
|
*/
|
||||||
private String dir;
|
private String dir;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* distinguish the log file by pid number
|
* Distinguish the log file by pid number.
|
||||||
*/
|
*/
|
||||||
private boolean switchPid = false;
|
private boolean switchPid = false;
|
||||||
|
|
||||||
|
@ -54,11 +54,6 @@ public class SentinelWebAutoConfiguration {
|
|||||||
|
|
||||||
SentinelProperties.Filter filterConfig = properties.getFilter();
|
SentinelProperties.Filter filterConfig = properties.getFilter();
|
||||||
|
|
||||||
if (null == filterConfig) {
|
|
||||||
filterConfig = new SentinelProperties.Filter();
|
|
||||||
properties.setFilter(filterConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterConfig.getUrlPatterns() == null
|
if (filterConfig.getUrlPatterns() == null
|
||||||
|| filterConfig.getUrlPatterns().isEmpty()) {
|
|| filterConfig.getUrlPatterns().isEmpty()) {
|
||||||
List<String> defaultPatterns = new ArrayList<>();
|
List<String> defaultPatterns = new ArrayList<>();
|
||||||
|
@ -22,6 +22,7 @@ import javax.annotation.PostConstruct;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
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.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
@ -29,10 +30,12 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.util.StringUtils;
|
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.UrlBlockHandler;
|
||||||
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
|
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.callback.WebCallbackManager;
|
||||||
@ -74,8 +77,20 @@ public class SentinelAutoConfiguration {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private Optional<UrlBlockHandler> urlBlockHandlerOptional;
|
private Optional<UrlBlockHandler> urlBlockHandlerOptional;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Optional<RequestOriginParser> requestOriginParserOptional;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
private void init() {
|
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))
|
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
|
||||||
&& StringUtils.hasText(projectName)) {
|
&& StringUtils.hasText(projectName)) {
|
||||||
System.setProperty(AppNameUtil.APP_NAME, projectName);
|
System.setProperty(AppNameUtil.APP_NAME, projectName);
|
||||||
@ -96,6 +111,11 @@ public class SentinelAutoConfiguration {
|
|||||||
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
|
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
|
||||||
properties.getTransport().getHeartbeatIntervalMs());
|
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))
|
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
|
||||||
&& StringUtils.hasText(properties.getMetric().getCharset())) {
|
&& StringUtils.hasText(properties.getMetric().getCharset())) {
|
||||||
System.setProperty(SentinelConfig.CHARSET,
|
System.setProperty(SentinelConfig.CHARSET,
|
||||||
@ -121,18 +141,10 @@ public class SentinelAutoConfiguration {
|
|||||||
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
|
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
|
||||||
WebServletConfig.setBlockPage(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);
|
urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
|
||||||
urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
|
urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
|
||||||
|
requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
|
||||||
|
|
||||||
// earlier initialize
|
// earlier initialize
|
||||||
if (properties.isEager()) {
|
if (properties.isEager()) {
|
||||||
@ -150,13 +162,15 @@ public class SentinelAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
|
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
|
||||||
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
|
public SentinelBeanPostProcessor sentinelBeanPostProcessor(
|
||||||
return new SentinelBeanPostProcessor();
|
ApplicationContext applicationContext) {
|
||||||
|
return new SentinelBeanPostProcessor(applicationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SentinelDataSourceHandler sentinelDataSourceHandler() {
|
public SentinelDataSourceHandler sentinelDataSourceHandler(
|
||||||
return new SentinelDataSourceHandler();
|
DefaultListableBeanFactory beanFactory) {
|
||||||
|
return new SentinelDataSourceHandler(beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class SentinelConverterConfiguration {
|
protected static class SentinelConverterConfiguration {
|
||||||
|
@ -16,22 +16,32 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
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.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.type.StandardMethodMetadata;
|
import org.springframework.core.type.StandardMethodMetadata;
|
||||||
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
|
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.util.StringUtils;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
|
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
|
||||||
*
|
*
|
||||||
@ -41,8 +51,14 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
*/
|
*/
|
||||||
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
||||||
|
|
||||||
@Autowired
|
private static final Logger logger = LoggerFactory
|
||||||
private ApplicationContext applicationContext;
|
.getLogger(SentinelBeanPostProcessor.class);
|
||||||
|
|
||||||
|
private final ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
public SentinelBeanPostProcessor(ApplicationContext applicationContext) {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
private ConcurrentHashMap<String, SentinelRestTemplate> cache = new ConcurrentHashMap<>();
|
private ConcurrentHashMap<String, SentinelRestTemplate> cache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@ -60,10 +76,74 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
|
|||||||
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
||||||
.getAnnotation(SentinelRestTemplate.class);
|
.getAnnotation(SentinelRestTemplate.class);
|
||||||
}
|
}
|
||||||
|
// check class and method validation
|
||||||
|
checkSentinelRestTemplate(sentinelRestTemplate, beanName);
|
||||||
cache.put(beanName, sentinelRestTemplate);
|
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,
|
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
|
||||||
Class<?> beanType) {
|
Class<?> beanType) {
|
||||||
return beanType == RestTemplate.class
|
return beanType == RestTemplate.class
|
||||||
@ -103,7 +183,7 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
|
|||||||
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
|
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
|
||||||
.getBean(interceptorBeanName.toString(),
|
.getBean(interceptorBeanName.toString(),
|
||||||
SentinelProtectInterceptor.class);
|
SentinelProtectInterceptor.class);
|
||||||
restTemplate.getInterceptors().add(sentinelProtectInterceptor);
|
restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
|
||||||
}
|
}
|
||||||
return bean;
|
return bean;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -12,10 +10,10 @@ import java.util.TreeMap;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
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.SentinelConstants;
|
||||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourceConstants;
|
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.config.NacosDataSourceProperties;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
||||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
||||||
import org.springframework.context.event.EventListener;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
@ -44,29 +41,28 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
|||||||
* @see JsonConverter
|
* @see JsonConverter
|
||||||
* @see XmlConverter
|
* @see XmlConverter
|
||||||
*/
|
*/
|
||||||
public class SentinelDataSourceHandler {
|
public class SentinelDataSourceHandler implements SmartInitializingSingleton {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory
|
private static final Logger logger = LoggerFactory
|
||||||
.getLogger(SentinelDataSourceHandler.class);
|
.getLogger(SentinelDataSourceHandler.class);
|
||||||
|
|
||||||
private List<String> dataTypeList = Arrays.asList("json", "xml");
|
private List<String> dataTypeList = Arrays.asList("json", "xml");
|
||||||
|
|
||||||
private List<String> dataSourceBeanNameList = Collections
|
private final String DATA_TYPE_FIELD = "dataType";
|
||||||
.synchronizedList(new ArrayList<>());
|
private final String CUSTOM_DATA_TYPE = "custom";
|
||||||
|
private final String CONVERTER_CLASS_FIELD = "converterClass";
|
||||||
|
|
||||||
private final String DATATYPE_FIELD = "dataType";
|
private final DefaultListableBeanFactory beanFactory;
|
||||||
private final String CUSTOM_DATATYPE = "custom";
|
|
||||||
private final String CONVERTERCLASS_FIELD = "converterClass";
|
public SentinelDataSourceHandler(DefaultListableBeanFactory beanFactory) {
|
||||||
|
this.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SentinelProperties sentinelProperties;
|
private SentinelProperties sentinelProperties;
|
||||||
|
|
||||||
@EventListener(classes = ApplicationStartedEvent.class)
|
@Override
|
||||||
public void buildDataSource(ApplicationStartedEvent event) throws Exception {
|
public void afterSingletonsInstantiated() {
|
||||||
|
|
||||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) event
|
|
||||||
.getApplicationContext().getAutowireCapableBeanFactory();
|
|
||||||
|
|
||||||
// commercialization
|
// commercialization
|
||||||
if (!StringUtils.isEmpty(System.getProperties()
|
if (!StringUtils.isEmpty(System.getProperties()
|
||||||
.getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
|
.getProperty(SentinelDataSourceConstants.NACOS_DATASOURCE_ENDPOINT))) {
|
||||||
@ -101,9 +97,8 @@ public class SentinelDataSourceHandler {
|
|||||||
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
|
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
|
||||||
.getValidDataSourceProperties();
|
.getValidDataSourceProperties();
|
||||||
abstractDataSourceProperties.preCheck(dataSourceName);
|
abstractDataSourceProperties.preCheck(dataSourceName);
|
||||||
registerBean(beanFactory, abstractDataSourceProperties,
|
registerBean(abstractDataSourceProperties, dataSourceName
|
||||||
dataSourceName + "-sentinel-" + validFields.get(0)
|
+ "-sentinel-" + validFields.get(0) + "-datasource");
|
||||||
+ "-datasource");
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
logger.error("[Sentinel Starter] DataSource " + dataSourceName
|
logger.error("[Sentinel Starter] DataSource " + dataSourceName
|
||||||
@ -112,8 +107,7 @@ public class SentinelDataSourceHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerBean(DefaultListableBeanFactory beanFactory,
|
private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
|
||||||
final AbstractDataSourceProperties dataSourceProperties,
|
|
||||||
String dataSourceName) {
|
String dataSourceName) {
|
||||||
|
|
||||||
Map<String, Object> propertyMap = Arrays
|
Map<String, Object> propertyMap = Arrays
|
||||||
@ -132,8 +126,8 @@ public class SentinelDataSourceHandler {
|
|||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
}, HashMap::putAll);
|
}, HashMap::putAll);
|
||||||
propertyMap.put(CONVERTERCLASS_FIELD, dataSourceProperties.getConverterClass());
|
propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceProperties.getConverterClass());
|
||||||
propertyMap.put(DATATYPE_FIELD, dataSourceProperties.getDataType());
|
propertyMap.put(DATA_TYPE_FIELD, dataSourceProperties.getDataType());
|
||||||
|
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||||
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
|
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
|
||||||
@ -141,81 +135,78 @@ public class SentinelDataSourceHandler {
|
|||||||
propertyMap.forEach((propertyName, propertyValue) -> {
|
propertyMap.forEach((propertyName, propertyValue) -> {
|
||||||
Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
|
Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
|
||||||
propertyName);
|
propertyName);
|
||||||
if (field != null) {
|
if (null == field) {
|
||||||
if (DATATYPE_FIELD.equals(propertyName)) {
|
return;
|
||||||
String dataType = StringUtils
|
}
|
||||||
.trimAllWhitespace(propertyValue.toString());
|
if (DATA_TYPE_FIELD.equals(propertyName)) {
|
||||||
if (CUSTOM_DATATYPE.equals(dataType)) {
|
String dataType = StringUtils.trimAllWhitespace(propertyValue.toString());
|
||||||
try {
|
if (CUSTOM_DATA_TYPE.equals(dataType)) {
|
||||||
if (StringUtils
|
try {
|
||||||
.isEmpty(dataSourceProperties.getConverterClass())) {
|
if (StringUtils
|
||||||
throw new RuntimeException(
|
.isEmpty(dataSourceProperties.getConverterClass())) {
|
||||||
"[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 "
|
throw new RuntimeException("[Sentinel Starter] DataSource "
|
||||||
+ dataSourceName + " dataType: " + propertyValue
|
+ dataSourceName
|
||||||
+ " is not support now. please using these types: "
|
+ "dataType is custom, please set converter-class "
|
||||||
+ dataTypeList.toString());
|
+ "property");
|
||||||
}
|
}
|
||||||
// converter type now support xml or json.
|
// construct custom Converter with 'converterClass'
|
||||||
// The bean name of these converters wrapped by
|
// configuration and register
|
||||||
// 'sentinel-{converterType}-{ruleType}-converter'
|
String customConvertBeanName = "sentinel-"
|
||||||
builder.addPropertyReference("converter",
|
+ dataSourceProperties.getConverterClass();
|
||||||
"sentinel-" + propertyValue.toString() + "-"
|
if (!this.beanFactory.containsBean(customConvertBeanName)) {
|
||||||
+ dataSourceProperties.getRuleType().getName()
|
this.beanFactory.registerBeanDefinition(customConvertBeanName,
|
||||||
+ "-converter");
|
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 {
|
else {
|
||||||
// wired properties
|
if (!dataTypeList.contains(
|
||||||
Optional.ofNullable(propertyValue)
|
StringUtils.trimAllWhitespace(propertyValue.toString()))) {
|
||||||
.ifPresent(v -> builder.addPropertyValue(propertyName, v));
|
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
|
// init in Spring
|
||||||
AbstractDataSource newDataSource = (AbstractDataSource) beanFactory
|
AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
|
||||||
.getBean(dataSourceName);
|
.getBean(dataSourceName);
|
||||||
|
|
||||||
logAndCheckRuleType(newDataSource, dataSourceName,
|
logAndCheckRuleType(newDataSource, dataSourceName,
|
||||||
@ -234,7 +225,6 @@ public class SentinelDataSourceHandler {
|
|||||||
DegradeRuleManager.register2Property(newDataSource.getProperty());
|
DegradeRuleManager.register2Property(newDataSource.getProperty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataSourceBeanNameList.add(dataSourceName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
|
private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName,
|
||||||
@ -282,9 +272,4 @@ public class SentinelDataSourceHandler {
|
|||||||
+ ruleClass.getSimpleName() + ">. Class: " + ruleConfig.getClass());
|
+ ruleClass.getSimpleName() + ">. Class: " + ruleConfig.getClass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getDataSourceBeanNameList() {
|
|
||||||
return dataSourceBeanNameList;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -29,7 +29,6 @@ import org.springframework.http.HttpRequest;
|
|||||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
import org.springframework.http.client.ClientHttpResponse;
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Entry;
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
import com.alibaba.csp.sentinel.SphU;
|
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.context.ContextUtil;
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interceptor using by SentinelRestTemplate
|
* Interceptor using by SentinelRestTemplate
|
||||||
*
|
*
|
||||||
* @author fangjian
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
*/
|
*/
|
||||||
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory
|
private static final Logger logger = LoggerFactory
|
||||||
.getLogger(SentinelProtectInterceptor.class);
|
.getLogger(SentinelProtectInterceptor.class);
|
||||||
|
|
||||||
private SentinelRestTemplate sentinelRestTemplate;
|
private final SentinelRestTemplate sentinelRestTemplate;
|
||||||
|
|
||||||
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) {
|
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate) {
|
||||||
this.sentinelRestTemplate = sentinelRestTemplate;
|
this.sentinelRestTemplate = sentinelRestTemplate;
|
||||||
@ -82,18 +80,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
|
|||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
return handleBlockException(request, body, execution, (BlockException) e);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -109,84 +96,49 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
|
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 };
|
Object[] args = new Object[] { request, body, execution, ex };
|
||||||
// handle degrade
|
// handle degrade
|
||||||
if (isDegradeFailure(ex)) {
|
if (isDegradeFailure(ex)) {
|
||||||
Method method = extractFallbackMethod(sentinelRestTemplate.fallback(),
|
Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(),
|
||||||
sentinelRestTemplate.fallbackClass());
|
sentinelRestTemplate.fallbackClass());
|
||||||
if (method != null) {
|
if (fallbackMethod != null) {
|
||||||
return (ClientHttpResponse) method.invoke(null, args);
|
return methodInvoke(fallbackMethod, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new SentinelClientHttpResponse();
|
return new SentinelClientHttpResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle block
|
// handle flow
|
||||||
Method blockHandler = extractBlockHandlerMethod(
|
Method blockHandler = extractBlockHandlerMethod(
|
||||||
sentinelRestTemplate.blockHandler(),
|
sentinelRestTemplate.blockHandler(),
|
||||||
sentinelRestTemplate.blockHandlerClass());
|
sentinelRestTemplate.blockHandlerClass());
|
||||||
if (blockHandler != null) {
|
if (blockHandler != null) {
|
||||||
return (ClientHttpResponse) blockHandler.invoke(null, args);
|
return methodInvoke(blockHandler, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new SentinelClientHttpResponse();
|
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) {
|
private Method extractFallbackMethod(String fallback, Class<?> fallbackClass) {
|
||||||
if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
|
return BlockClassRegistry.lookupFallback(fallbackClass, fallback);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
||||||
if (StringUtil.isBlank(block) || blockClass == void.class) {
|
return BlockClassRegistry.lookupBlockHandler(blockClass, block);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDegradeFailure(BlockException ex) {
|
private boolean isDegradeFailure(BlockException ex) {
|
||||||
|
@ -17,25 +17,20 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
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.adapter.servlet.config.WebServletConfig;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
import com.alibaba.csp.sentinel.log.LogBase;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
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.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.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.slots.system.SystemRuleManager;
|
||||||
|
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endpoint for Sentinel, contains ans properties and rules
|
* Endpoint for Sentinel, contains ans properties and rules
|
||||||
@ -44,43 +39,39 @@ import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
|||||||
@Endpoint(id = "sentinel")
|
@Endpoint(id = "sentinel")
|
||||||
public class SentinelEndpoint {
|
public class SentinelEndpoint {
|
||||||
|
|
||||||
@Autowired
|
private final SentinelProperties sentinelProperties;
|
||||||
private SentinelProperties sentinelProperties;
|
|
||||||
|
|
||||||
@Autowired
|
public SentinelEndpoint(SentinelProperties sentinelProperties) {
|
||||||
private SentinelDataSourceHandler dataSourceHandler;
|
this.sentinelProperties = sentinelProperties;
|
||||||
|
}
|
||||||
@Autowired
|
|
||||||
private ApplicationContext applicationContext;
|
|
||||||
|
|
||||||
@ReadOperation
|
@ReadOperation
|
||||||
public Map<String, Object> invoke() {
|
public Map<String, Object> invoke() {
|
||||||
final Map<String, Object> result = new HashMap<>();
|
final Map<String, Object> result = new HashMap<>();
|
||||||
|
if (sentinelProperties.isEnabled()) {
|
||||||
|
|
||||||
List<FlowRule> flowRules = FlowRuleManager.getRules();
|
result.put("logDir", LogBase.getLogBaseDir());
|
||||||
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
|
result.put("logUsePid", LogBase.isLogNameUsePid());
|
||||||
List<SystemRule> systemRules = SystemRuleManager.getRules();
|
result.put("blockPage", WebServletConfig.getBlockPage());
|
||||||
List<ParamFlowRule> paramFlowRules = ParamFlowRuleManager.getRules();
|
result.put("metricsFileSize", SentinelConfig.singleMetricFileSize());
|
||||||
result.put("properties", sentinelProperties);
|
result.put("metricsFileCharset", SentinelConfig.charset());
|
||||||
result.put("FlowRules", flowRules);
|
result.put("totalMetricsFileCount", SentinelConfig.totalMetricFileCount());
|
||||||
result.put("DegradeRules", degradeRules);
|
result.put("consoleServer", TransportConfig.getConsoleServer());
|
||||||
result.put("SystemRules", systemRules);
|
result.put("clientIp", TransportConfig.getHeartbeatClientIp());
|
||||||
result.put("ParamFlowRule", paramFlowRules);
|
result.put("heartbeatIntervalMs", TransportConfig.getHeartbeatIntervalMs());
|
||||||
result.put("datasources", new HashMap<String, Object>());
|
result.put("clientPort", TransportConfig.getPort());
|
||||||
dataSourceHandler.getDataSourceBeanNameList().forEach(dataSourceBeanName -> {
|
result.put("coldFactor", sentinelProperties.getFlow().getColdFactor());
|
||||||
ReadableDataSource dataSource = applicationContext.getBean(dataSourceBeanName,
|
result.put("filter", sentinelProperties.getFilter());
|
||||||
ReadableDataSource.class);
|
result.put("datasource", sentinelProperties.getDatasource());
|
||||||
try {
|
|
||||||
((HashMap) result.get("datasources")).put(dataSourceBeanName,
|
|
||||||
dataSource.loadConfig());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
((HashMap) result.get("datasources")).put(dataSourceBeanName,
|
|
||||||
"load error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
final Map<String, Object> 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ public class SentinelEndpointAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnEnabledEndpoint
|
@ConditionalOnEnabledEndpoint
|
||||||
public SentinelEndpoint sentinelEndPoint() {
|
public SentinelEndpoint sentinelEndPoint(SentinelProperties sentinelProperties) {
|
||||||
return new SentinelEndpoint();
|
return new SentinelEndpoint(sentinelProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
"defaultValue": "8719",
|
"defaultValue": "8719",
|
||||||
"description": "sentinel api port."
|
"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",
|
"name": "spring.cloud.sentinel.transport.dashboard",
|
||||||
"type": "java.lang.String",
|
"type": "java.lang.String",
|
||||||
|
@ -16,76 +16,268 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel;
|
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.junit.Test;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
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.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
|
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
|
||||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
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 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.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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
* @author jiashuai.xie
|
* @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 {
|
public class SentinelAutoConfigurationTests {
|
||||||
|
|
||||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
@Autowired
|
||||||
.withConfiguration(AutoConfigurations.of(SentinelAutoConfiguration.class,
|
private SentinelProperties sentinelProperties;
|
||||||
SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class))
|
|
||||||
.withPropertyValues("spring.cloud.sentinel.transport.port=8888")
|
@Autowired
|
||||||
.withPropertyValues("spring.cloud.sentinel.filter.order=123")
|
private FilterRegistrationBean filterRegistrationBean;
|
||||||
.withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
|
|
||||||
|
@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<String, Object> 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
|
@Test
|
||||||
public void testFilter() {
|
public void testFilter() {
|
||||||
this.contextRunner.run(context -> {
|
assertEquals("Sentinel Filter order was wrong", filterRegistrationBean.getOrder(),
|
||||||
assertThat(context.getBean("servletRequestListener")
|
123);
|
||||||
.getClass() == FilterRegistrationBean.class).isTrue();
|
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
|
@Test
|
||||||
public void testBeanPostProcessor() {
|
public void testFallbackRestTemplate() {
|
||||||
this.contextRunner.run(context -> {
|
ResponseEntity responseEntity = restTemplateWithFallbackClass
|
||||||
assertThat(context.getBean("sentinelBeanPostProcessor")
|
.getForEntity(url + "/test", String.class);
|
||||||
.getClass() == SentinelBeanPostProcessor.class).isTrue();
|
assertEquals("RestTemplateWithFallbackClass Sentinel Message was wrong",
|
||||||
});
|
"Oops fallback", responseEntity.getBody());
|
||||||
}
|
assertEquals("RestTemplateWithFallbackClass Sentinel Http Status Code was wrong",
|
||||||
|
HttpStatus.OK, responseEntity.getStatusCode());
|
||||||
@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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ -94,7 +286,9 @@ public class SentinelAutoConfigurationTests {
|
|||||||
@Bean
|
@Bean
|
||||||
@SentinelRestTemplate
|
@SentinelRestTemplate
|
||||||
RestTemplate restTemplate() {
|
RestTemplate restTemplate() {
|
||||||
return new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
|
restTemplate.getInterceptors().add(mock(ClientHttpRequestInterceptor.class));
|
||||||
|
return restTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -103,12 +297,38 @@ public class SentinelAutoConfigurationTests {
|
|||||||
return new RestTemplate();
|
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 class ExceptionUtil {
|
||||||
public static void handleException(BlockException ex) {
|
public static SentinelClientHttpResponse handleException(HttpRequest request,
|
||||||
|
byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
|
||||||
System.out.println("Oops: " + ex.getClass().getCanonicalName());
|
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 {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
@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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
@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<FooService> {
|
||||||
|
|
||||||
|
private FooService fooService = new FooServiceFallback();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FooService create(Throwable throwable) {
|
||||||
|
return fooService;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
|
*/
|
||||||
|
public class TestConverter implements Converter<String, List<ParamFlowRule>> {
|
||||||
|
private ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ParamFlowRule> convert(String s) {
|
||||||
|
try {
|
||||||
|
return objectMapper.readValue(s, new TypeReference<List<ParamFlowRule>>() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"resource": "good",
|
||||||
|
"limitApp": "abc",
|
||||||
|
"strategy": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "bad",
|
||||||
|
"limitApp": "bcd",
|
||||||
|
"strategy": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "terrible",
|
||||||
|
"limitApp": "aaa",
|
||||||
|
"strategy": 1
|
||||||
|
}
|
||||||
|
]
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,16 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"resource": "hotResource",
|
||||||
|
"count": 0,
|
||||||
|
"grade": 1,
|
||||||
|
"limitApp": "default",
|
||||||
|
"paramIdx": 0,
|
||||||
|
"paramFlowItemList": [
|
||||||
|
{
|
||||||
|
"object": "2",
|
||||||
|
"classType": "int",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"highestSystemLoad": -1,
|
||||||
|
"qps": 100,
|
||||||
|
"avgRt": -1,
|
||||||
|
"maxThread": 10
|
||||||
|
}
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user