mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
#734 -> update maven coordinates
This commit is contained in:
@@ -1,188 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba</artifactId>
|
||||
<version>0.9.1.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
|
||||
<name>Spring Cloud Alibaba Sentinel</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-annotation-aspectj</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-dubbo-adapter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-web-servlet</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-spring-webflux-adapter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-cluster-server-default</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-cluster-client-default</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||
<artifactId>jackson-dataformat-xml</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--spring boot-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${jacoco.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jacoco-initialize</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>jacoco-site</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
*/
|
||||
public interface SentinelConstants {
|
||||
|
||||
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
||||
|
||||
String BLOCK_TYPE = "block";
|
||||
String FALLBACK_TYPE = "fallback";
|
||||
|
||||
// commercialization
|
||||
|
||||
String FLOW_DATASOURCE_NAME = "edas-flow";
|
||||
String DEGRADE_DATASOURCE_NAME = "edas-degrade";
|
||||
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.log.LogBase;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties} for Sentinel.
|
||||
*
|
||||
* @author xiaojing
|
||||
* @author hengyunabc
|
||||
* @author jiashuai.xie
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX)
|
||||
@Validated
|
||||
public class SentinelProperties {
|
||||
|
||||
/**
|
||||
* Earlier initialize heart-beat when the spring container starts when the transport
|
||||
* dependency is on classpath, the configuration is effective.
|
||||
*/
|
||||
private boolean eager = false;
|
||||
|
||||
/**
|
||||
* Enable sentinel auto configure, the default value is true.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Configurations about datasource, like 'nacos', 'apollo', 'file', 'zookeeper'.
|
||||
*/
|
||||
private Map<String, DataSourcePropertiesConfiguration> datasource = new TreeMap<>(
|
||||
String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
/**
|
||||
* Transport configuration about dashboard and client.
|
||||
*/
|
||||
private Transport transport = new Transport();
|
||||
|
||||
/**
|
||||
* Metric configuration about resource.
|
||||
*/
|
||||
private Metric metric = new Metric();
|
||||
|
||||
/**
|
||||
* Web servlet configuration when the application is web, the configuration is
|
||||
* effective.
|
||||
*/
|
||||
private Servlet servlet = new Servlet();
|
||||
|
||||
/**
|
||||
* Sentinel filter when the application is web, the configuration is effective.
|
||||
*/
|
||||
private Filter filter = new Filter();
|
||||
|
||||
/**
|
||||
* Sentinel Flow configuration.
|
||||
*/
|
||||
private Flow flow = new Flow();
|
||||
|
||||
/**
|
||||
* Sentinel log configuration {@link LogBase}.
|
||||
*/
|
||||
private Log log = new Log();
|
||||
|
||||
public boolean isEager() {
|
||||
return eager;
|
||||
}
|
||||
|
||||
public void setEager(boolean eager) {
|
||||
this.eager = eager;
|
||||
}
|
||||
|
||||
public Flow getFlow() {
|
||||
return flow;
|
||||
}
|
||||
|
||||
public void setFlow(Flow flow) {
|
||||
this.flow = flow;
|
||||
}
|
||||
|
||||
public Transport getTransport() {
|
||||
return transport;
|
||||
}
|
||||
|
||||
public void setTransport(Transport transport) {
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
public Metric getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(Metric metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public Servlet getServlet() {
|
||||
return servlet;
|
||||
}
|
||||
|
||||
public void setServlet(Servlet servlet) {
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(Filter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
public Map<String, DataSourcePropertiesConfiguration> getDatasource() {
|
||||
return datasource;
|
||||
}
|
||||
|
||||
public void setDatasource(Map<String, DataSourcePropertiesConfiguration> datasource) {
|
||||
this.datasource = datasource;
|
||||
}
|
||||
|
||||
public Log getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
public void setLog(Log log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public static class Flow {
|
||||
|
||||
/**
|
||||
* The cold factor {@link SentinelConfig#COLD_FACTOR}.
|
||||
*/
|
||||
private String coldFactor = "3";
|
||||
|
||||
public String getColdFactor() {
|
||||
return coldFactor;
|
||||
}
|
||||
|
||||
public void setColdFactor(String coldFactor) {
|
||||
this.coldFactor = coldFactor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Servlet {
|
||||
|
||||
/**
|
||||
* The process page when the flow control is triggered.
|
||||
*/
|
||||
private String blockPage;
|
||||
|
||||
public String getBlockPage() {
|
||||
return blockPage;
|
||||
}
|
||||
|
||||
public void setBlockPage(String blockPage) {
|
||||
this.blockPage = blockPage;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Metric {
|
||||
|
||||
/**
|
||||
* The metric file size {@link SentinelConfig#SINGLE_METRIC_FILE_SIZE}.
|
||||
*/
|
||||
private String fileSingleSize;
|
||||
|
||||
/**
|
||||
* The total metric file count {@link SentinelConfig#TOTAL_METRIC_FILE_COUNT}.
|
||||
*/
|
||||
private String fileTotalCount;
|
||||
|
||||
/**
|
||||
* Charset when sentinel write or search metric file.
|
||||
* {@link SentinelConfig#CHARSET}
|
||||
*/
|
||||
private String charset = "UTF-8";
|
||||
|
||||
public String getFileSingleSize() {
|
||||
return fileSingleSize;
|
||||
}
|
||||
|
||||
public void setFileSingleSize(String fileSingleSize) {
|
||||
this.fileSingleSize = fileSingleSize;
|
||||
}
|
||||
|
||||
public String getFileTotalCount() {
|
||||
return fileTotalCount;
|
||||
}
|
||||
|
||||
public void setFileTotalCount(String fileTotalCount) {
|
||||
this.fileTotalCount = fileTotalCount;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Transport {
|
||||
|
||||
/**
|
||||
* Sentinel api port, default value is 8719 {@link TransportConfig#SERVER_PORT}.
|
||||
*/
|
||||
private String port = "8719";
|
||||
|
||||
/**
|
||||
* Sentinel dashboard address, won't try to connect dashboard when address is
|
||||
* empty {@link TransportConfig#CONSOLE_SERVER}.
|
||||
*/
|
||||
private String dashboard = "";
|
||||
|
||||
/**
|
||||
* Send heartbeat interval millisecond
|
||||
* {@link TransportConfig#HEARTBEAT_INTERVAL_MS}.
|
||||
*/
|
||||
private String heartbeatIntervalMs;
|
||||
|
||||
/**
|
||||
* Get heartbeat client local ip. If the client ip not configured, it will be the
|
||||
* address of local host.
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
public String getHeartbeatIntervalMs() {
|
||||
return heartbeatIntervalMs;
|
||||
}
|
||||
|
||||
public void setHeartbeatIntervalMs(String heartbeatIntervalMs) {
|
||||
this.heartbeatIntervalMs = heartbeatIntervalMs;
|
||||
}
|
||||
|
||||
public String getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(String port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getDashboard() {
|
||||
return dashboard;
|
||||
}
|
||||
|
||||
public void setDashboard(String dashboard) {
|
||||
this.dashboard = dashboard;
|
||||
}
|
||||
|
||||
public String getClientIp() {
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
public void setClientIp(String clientIp) {
|
||||
this.clientIp = clientIp;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Filter {
|
||||
|
||||
/**
|
||||
* Sentinel filter chain order.
|
||||
*/
|
||||
private int order = Ordered.HIGHEST_PRECEDENCE;
|
||||
|
||||
/**
|
||||
* URL pattern for sentinel filter, default is /*
|
||||
*/
|
||||
private List<String> urlPatterns;
|
||||
|
||||
/**
|
||||
* Enable to instance
|
||||
* {@link com.alibaba.csp.sentinel.adapter.servlet.CommonFilter}.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public List<String> getUrlPatterns() {
|
||||
return urlPatterns;
|
||||
}
|
||||
|
||||
public void setUrlPatterns(List<String> urlPatterns) {
|
||||
this.urlPatterns = urlPatterns;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Log {
|
||||
|
||||
/**
|
||||
* Sentinel log base dir.
|
||||
*/
|
||||
private String dir;
|
||||
|
||||
/**
|
||||
* Distinguish the log file by pid number.
|
||||
*/
|
||||
private boolean switchPid = false;
|
||||
|
||||
public String getDir() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public void setDir(String dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public boolean isSwitchPid() {
|
||||
return switchPid;
|
||||
}
|
||||
|
||||
public void setSwitchPid(boolean switchPid) {
|
||||
this.switchPid = switchPid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnClass(CommonFilter.class)
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SentinelProperties.class)
|
||||
public class SentinelWebAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SentinelWebAutoConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
private SentinelProperties properties;
|
||||
|
||||
@Autowired
|
||||
private Optional<UrlCleaner> urlCleanerOptional;
|
||||
|
||||
@Autowired
|
||||
private Optional<UrlBlockHandler> urlBlockHandlerOptional;
|
||||
|
||||
@Autowired
|
||||
private Optional<RequestOriginParser> requestOriginParserOptional;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
|
||||
urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
|
||||
requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
|
||||
public FilterRegistrationBean sentinelFilter() {
|
||||
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
|
||||
|
||||
SentinelProperties.Filter filterConfig = properties.getFilter();
|
||||
|
||||
if (filterConfig.getUrlPatterns() == null
|
||||
|| filterConfig.getUrlPatterns().isEmpty()) {
|
||||
List<String> defaultPatterns = new ArrayList<>();
|
||||
defaultPatterns.add("/*");
|
||||
filterConfig.setUrlPatterns(defaultPatterns);
|
||||
}
|
||||
|
||||
registration.addUrlPatterns(filterConfig.getUrlPatterns().toArray(new String[0]));
|
||||
Filter filter = new CommonFilter();
|
||||
registration.setFilter(filter);
|
||||
registration.setOrder(filterConfig.getOrder());
|
||||
log.info(
|
||||
"[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.",
|
||||
filterConfig.getUrlPatterns());
|
||||
return registration;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.SentinelWebFluxFilter;
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.BlockRequestHandler;
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.WebFluxCallbackManager;
|
||||
import com.alibaba.csp.sentinel.adapter.spring.webflux.exception.SentinelBlockExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@ConditionalOnClass(SentinelReactorTransformer.class)
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SentinelProperties.class)
|
||||
public class SentinelWebFluxAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SentinelWebFluxAutoConfiguration.class);
|
||||
|
||||
private final List<ViewResolver> viewResolvers;
|
||||
private final ServerCodecConfigurer serverCodecConfigurer;
|
||||
|
||||
@Autowired
|
||||
private Optional<BlockRequestHandler> blockRequestHandler;
|
||||
|
||||
public SentinelWebFluxAutoConfiguration(
|
||||
ObjectProvider<List<ViewResolver>> viewResolvers,
|
||||
ServerCodecConfigurer serverCodecConfigurer) {
|
||||
this.viewResolvers = viewResolvers.getIfAvailable(Collections::emptyList);
|
||||
this.serverCodecConfigurer = serverCodecConfigurer;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
blockRequestHandler.ifPresent(WebFluxCallbackManager::setBlockHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(-2)
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
|
||||
public SentinelBlockExceptionHandler sentinelBlockExceptionHandler() {
|
||||
return new SentinelBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(-1)
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", matchIfMissing = true)
|
||||
public SentinelWebFluxFilter sentinelWebFluxFilter() {
|
||||
log.info("[Sentinel Starter] register Sentinel SentinelWebFluxFilter");
|
||||
return new SentinelWebFluxFilter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
*/
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface SentinelRestTemplate {
|
||||
|
||||
String blockHandler() default "";
|
||||
|
||||
Class<?> blockHandlerClass() default void.class;
|
||||
|
||||
String fallback() default "";
|
||||
|
||||
Class<?> fallbackClass() default void.class;
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
*/
|
||||
final class BlockClassRegistry {
|
||||
|
||||
private static final Map<String, Method> FALLBACK_MAP = new ConcurrentHashMap<>();
|
||||
private static final Map<String, Method> BLOCK_HANDLER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static Method lookupFallback(Class<?> clazz, String name) {
|
||||
return FALLBACK_MAP.get(getKey(clazz, name));
|
||||
}
|
||||
|
||||
static Method lookupBlockHandler(Class<?> clazz, String name) {
|
||||
return BLOCK_HANDLER_MAP.get(getKey(clazz, name));
|
||||
}
|
||||
|
||||
static void updateFallbackFor(Class<?> clazz, String name, Method method) {
|
||||
if (clazz == null || StringUtil.isBlank(name)) {
|
||||
throw new IllegalArgumentException("Bad argument");
|
||||
}
|
||||
FALLBACK_MAP.put(getKey(clazz, name), method);
|
||||
}
|
||||
|
||||
static void updateBlockHandlerFor(Class<?> clazz, String name, Method method) {
|
||||
if (clazz == null || StringUtil.isBlank(name)) {
|
||||
throw new IllegalArgumentException("Bad argument");
|
||||
}
|
||||
BLOCK_HANDLER_MAP.put(getKey(clazz, name), method);
|
||||
}
|
||||
|
||||
private static String getKey(Class<?> clazz, String name) {
|
||||
return String.format("%s:%s", clazz.getCanonicalName(), name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
|
||||
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.init.InitExecutor;
|
||||
import com.alibaba.csp.sentinel.log.LogBase;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import com.alibaba.csp.sentinel.util.AppNameUtil;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
* @author jiashuai.xie
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SentinelProperties.class)
|
||||
public class SentinelAutoConfiguration {
|
||||
|
||||
@Value("${project.name:${spring.application.name:}}")
|
||||
private String projectName;
|
||||
|
||||
@Autowired
|
||||
private SentinelProperties properties;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR))
|
||||
&& StringUtils.hasText(properties.getLog().getDir())) {
|
||||
System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID))
|
||||
&& properties.getLog().isSwitchPid()) {
|
||||
System.setProperty(LogBase.LOG_NAME_USE_PID,
|
||||
String.valueOf(properties.getLog().isSwitchPid()));
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME))
|
||||
&& StringUtils.hasText(projectName)) {
|
||||
System.setProperty(AppNameUtil.APP_NAME, projectName);
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))
|
||||
&& StringUtils.hasText(properties.getTransport().getPort())) {
|
||||
System.setProperty(TransportConfig.SERVER_PORT,
|
||||
properties.getTransport().getPort());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))
|
||||
&& StringUtils.hasText(properties.getTransport().getDashboard())) {
|
||||
System.setProperty(TransportConfig.CONSOLE_SERVER,
|
||||
properties.getTransport().getDashboard());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS))
|
||||
&& StringUtils
|
||||
.hasText(properties.getTransport().getHeartbeatIntervalMs())) {
|
||||
System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS,
|
||||
properties.getTransport().getHeartbeatIntervalMs());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP))
|
||||
&& StringUtils.hasText(properties.getTransport().getClientIp())) {
|
||||
System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP,
|
||||
properties.getTransport().getClientIp());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET))
|
||||
&& StringUtils.hasText(properties.getMetric().getCharset())) {
|
||||
System.setProperty(SentinelConfig.CHARSET,
|
||||
properties.getMetric().getCharset());
|
||||
}
|
||||
if (StringUtils
|
||||
.isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE))
|
||||
&& StringUtils.hasText(properties.getMetric().getFileSingleSize())) {
|
||||
System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE,
|
||||
properties.getMetric().getFileSingleSize());
|
||||
}
|
||||
if (StringUtils
|
||||
.isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT))
|
||||
&& StringUtils.hasText(properties.getMetric().getFileTotalCount())) {
|
||||
System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT,
|
||||
properties.getMetric().getFileTotalCount());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR))
|
||||
&& StringUtils.hasText(properties.getFlow().getColdFactor())) {
|
||||
System.setProperty(SentinelConfig.COLD_FACTOR,
|
||||
properties.getFlow().getColdFactor());
|
||||
}
|
||||
if (StringUtils.hasText(properties.getServlet().getBlockPage())) {
|
||||
WebServletConfig.setBlockPage(properties.getServlet().getBlockPage());
|
||||
}
|
||||
|
||||
// earlier initialize
|
||||
if (properties.isEager()) {
|
||||
InitExecutor.doInit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SentinelResourceAspect sentinelResourceAspect() {
|
||||
return new SentinelResourceAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
|
||||
@ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true", matchIfMissing = true)
|
||||
public SentinelBeanPostProcessor sentinelBeanPostProcessor(
|
||||
ApplicationContext applicationContext) {
|
||||
return new SentinelBeanPostProcessor(applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SentinelDataSourceHandler sentinelDataSourceHandler(
|
||||
DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties,
|
||||
Environment env) {
|
||||
return new SentinelDataSourceHandler(beanFactory, sentinelProperties, env);
|
||||
}
|
||||
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@Configuration
|
||||
protected static class SentinelConverterConfiguration {
|
||||
|
||||
@Configuration
|
||||
protected static class SentinelJsonConfiguration {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public SentinelJsonConfiguration() {
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
|
||||
false);
|
||||
}
|
||||
|
||||
@Bean("sentinel-json-flow-converter")
|
||||
public JsonConverter jsonFlowConverter() {
|
||||
return new JsonConverter(objectMapper, FlowRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-json-degrade-converter")
|
||||
public JsonConverter jsonDegradeConverter() {
|
||||
return new JsonConverter(objectMapper, DegradeRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-json-system-converter")
|
||||
public JsonConverter jsonSystemConverter() {
|
||||
return new JsonConverter(objectMapper, SystemRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-json-authority-converter")
|
||||
public JsonConverter jsonAuthorityConverter() {
|
||||
return new JsonConverter(objectMapper, AuthorityRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-json-param-flow-converter")
|
||||
public JsonConverter jsonParamFlowConverter() {
|
||||
return new JsonConverter(objectMapper, ParamFlowRule.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(XmlMapper.class)
|
||||
@Configuration
|
||||
protected static class SentinelXmlConfiguration {
|
||||
|
||||
private XmlMapper xmlMapper = new XmlMapper();
|
||||
|
||||
public SentinelXmlConfiguration() {
|
||||
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
|
||||
false);
|
||||
}
|
||||
|
||||
@Bean("sentinel-xml-flow-converter")
|
||||
public XmlConverter xmlFlowConverter() {
|
||||
return new XmlConverter(xmlMapper, FlowRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-xml-degrade-converter")
|
||||
public XmlConverter xmlDegradeConverter() {
|
||||
return new XmlConverter(xmlMapper, DegradeRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-xml-system-converter")
|
||||
public XmlConverter xmlSystemConverter() {
|
||||
return new XmlConverter(xmlMapper, SystemRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-xml-authority-converter")
|
||||
public XmlConverter xmlAuthorityConverter() {
|
||||
return new XmlConverter(xmlMapper, AuthorityRule.class);
|
||||
}
|
||||
|
||||
@Bean("sentinel-xml-param-flow-converter")
|
||||
public XmlConverter xmlParamFlowConverter() {
|
||||
return new XmlConverter(xmlMapper, ParamFlowRule.class);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelConstants;
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
/**
|
||||
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
* @see SentinelRestTemplate
|
||||
* @see SentinelProtectInterceptor
|
||||
*/
|
||||
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SentinelBeanPostProcessor.class);
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public SentinelBeanPostProcessor(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<String, SentinelRestTemplate> cache = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
|
||||
Class<?> beanType, String beanName) {
|
||||
if (checkSentinelProtect(beanDefinition, beanType)) {
|
||||
SentinelRestTemplate sentinelRestTemplate;
|
||||
if (beanDefinition.getSource() instanceof StandardMethodMetadata) {
|
||||
sentinelRestTemplate = ((StandardMethodMetadata) beanDefinition
|
||||
.getSource()).getIntrospectedMethod()
|
||||
.getAnnotation(SentinelRestTemplate.class);
|
||||
}
|
||||
else {
|
||||
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
||||
.getAnnotation(SentinelRestTemplate.class);
|
||||
}
|
||||
// check class and method validation
|
||||
checkSentinelRestTemplate(sentinelRestTemplate, beanName);
|
||||
cache.put(beanName, sentinelRestTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSentinelRestTemplate(SentinelRestTemplate sentinelRestTemplate,
|
||||
String beanName) {
|
||||
checkBlock4RestTemplate(sentinelRestTemplate.blockHandlerClass(),
|
||||
sentinelRestTemplate.blockHandler(), beanName,
|
||||
SentinelConstants.BLOCK_TYPE);
|
||||
checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(),
|
||||
sentinelRestTemplate.fallback(), beanName,
|
||||
SentinelConstants.FALLBACK_TYPE);
|
||||
}
|
||||
|
||||
private void checkBlock4RestTemplate(Class<?> blockClass, String blockMethod,
|
||||
String beanName, String type) {
|
||||
if (blockClass == void.class && StringUtils.isEmpty(blockMethod)) {
|
||||
return;
|
||||
}
|
||||
if (blockClass != void.class && StringUtils.isEmpty(blockMethod)) {
|
||||
log.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)) {
|
||||
log.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) {
|
||||
log.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())) {
|
||||
log.error(
|
||||
"{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}",
|
||||
type, beanName, blockClass.getName(), blockMethod, argsStr);
|
||||
throw new IllegalArgumentException(type + " method return value in bean["
|
||||
+ beanName + "] is not ClientHttpResponse: " + blockClass.getName()
|
||||
+ "#" + blockMethod + argsStr);
|
||||
}
|
||||
if (type.equals(SentinelConstants.BLOCK_TYPE)) {
|
||||
BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod,
|
||||
foundMethod);
|
||||
}
|
||||
else {
|
||||
BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
|
||||
Class<?> beanType) {
|
||||
return beanType == RestTemplate.class
|
||||
&& (checkStandardMethodMetadata(beanDefinition)
|
||||
|| checkMethodMetadataReadingVisitor(beanDefinition));
|
||||
}
|
||||
|
||||
private boolean checkStandardMethodMetadata(RootBeanDefinition beanDefinition) {
|
||||
return beanDefinition.getSource() instanceof StandardMethodMetadata
|
||||
&& ((StandardMethodMetadata) beanDefinition.getSource())
|
||||
.isAnnotated(SentinelRestTemplate.class.getName());
|
||||
}
|
||||
|
||||
private boolean checkMethodMetadataReadingVisitor(RootBeanDefinition beanDefinition) {
|
||||
return beanDefinition.getSource() instanceof MethodMetadataReadingVisitor
|
||||
&& ((MethodMetadataReadingVisitor) beanDefinition.getSource())
|
||||
.isAnnotated(SentinelRestTemplate.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (cache.containsKey(beanName)) {
|
||||
// add interceptor for each RestTemplate with @SentinelRestTemplate annotation
|
||||
StringBuilder interceptorBeanNamePrefix = new StringBuilder();
|
||||
SentinelRestTemplate sentinelRestTemplate = cache.get(beanName);
|
||||
interceptorBeanNamePrefix
|
||||
.append(StringUtils.uncapitalize(
|
||||
SentinelProtectInterceptor.class.getSimpleName()))
|
||||
.append("_")
|
||||
.append(sentinelRestTemplate.blockHandlerClass().getSimpleName())
|
||||
.append(sentinelRestTemplate.blockHandler()).append("_")
|
||||
.append(sentinelRestTemplate.fallbackClass().getSimpleName())
|
||||
.append(sentinelRestTemplate.fallback());
|
||||
RestTemplate restTemplate = (RestTemplate) bean;
|
||||
String interceptorBeanName = interceptorBeanNamePrefix + "@"
|
||||
+ bean.toString();
|
||||
registerBean(interceptorBeanName, sentinelRestTemplate, (RestTemplate) bean);
|
||||
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
|
||||
.getBean(interceptorBeanName, SentinelProtectInterceptor.class);
|
||||
restTemplate.getInterceptors().add(0, sentinelProtectInterceptor);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void registerBean(String interceptorBeanName,
|
||||
SentinelRestTemplate sentinelRestTemplate, RestTemplate restTemplate) {
|
||||
// register SentinelProtectInterceptor bean
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
|
||||
.getAutowireCapableBeanFactory();
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SentinelProtectInterceptor.class);
|
||||
beanDefinitionBuilder.addConstructorArgValue(sentinelRestTemplate);
|
||||
beanDefinitionBuilder.addConstructorArgValue(restTemplate);
|
||||
BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
|
||||
.getRawBeanDefinition();
|
||||
beanFactory.registerBeanDefinition(interceptorBeanName,
|
||||
interceptorBeanDefinition);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* <p>
|
||||
* support @EnableCircuitBreaker ,Do nothing
|
||||
*/
|
||||
@Configuration
|
||||
public class SentinelCircuitBreakerConfiguration {
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||
|
||||
/**
|
||||
* Sentinel {@link ReadableDataSource} Handler Handle the configurations of
|
||||
* 'spring.cloud.sentinel.datasource'
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
* @see SentinelProperties#datasource
|
||||
* @see JsonConverter
|
||||
* @see XmlConverter
|
||||
*/
|
||||
public class SentinelDataSourceHandler implements SmartInitializingSingleton {
|
||||
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(SentinelDataSourceHandler.class);
|
||||
|
||||
private List<String> dataTypeList = Arrays.asList("json", "xml");
|
||||
|
||||
private final String DATA_TYPE_FIELD = "dataType";
|
||||
private final String CUSTOM_DATA_TYPE = "custom";
|
||||
private final String CONVERTER_CLASS_FIELD = "converterClass";
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private final SentinelProperties sentinelProperties;
|
||||
|
||||
private final Environment env;
|
||||
|
||||
public SentinelDataSourceHandler(DefaultListableBeanFactory beanFactory,
|
||||
SentinelProperties sentinelProperties, Environment env) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.sentinelProperties = sentinelProperties;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
sentinelProperties.getDatasource()
|
||||
.forEach((dataSourceName, dataSourceProperties) -> {
|
||||
try {
|
||||
List<String> validFields = dataSourceProperties.getValidField();
|
||||
if (validFields.size() != 1) {
|
||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
||||
+ " multi datasource active and won't loaded: "
|
||||
+ dataSourceProperties.getValidField());
|
||||
return;
|
||||
}
|
||||
AbstractDataSourceProperties abstractDataSourceProperties = dataSourceProperties
|
||||
.getValidDataSourceProperties();
|
||||
abstractDataSourceProperties.setEnv(env);
|
||||
abstractDataSourceProperties.preCheck(dataSourceName);
|
||||
registerBean(abstractDataSourceProperties, dataSourceName
|
||||
+ "-sentinel-" + validFields.get(0) + "-datasource");
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
||||
+ " build error: " + e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void registerBean(final AbstractDataSourceProperties dataSourceProperties,
|
||||
String dataSourceName) {
|
||||
|
||||
Map<String, Object> propertyMap = Arrays
|
||||
.stream(dataSourceProperties.getClass().getDeclaredFields())
|
||||
.collect(HashMap::new, (m, v) -> {
|
||||
try {
|
||||
v.setAccessible(true);
|
||||
m.put(v.getName(), v.get(dataSourceProperties));
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
||||
+ " field: " + v.getName() + " invoke error");
|
||||
throw new RuntimeException(
|
||||
"[Sentinel Starter] DataSource " + dataSourceName
|
||||
+ " field: " + v.getName() + " invoke error",
|
||||
e);
|
||||
}
|
||||
}, HashMap::putAll);
|
||||
propertyMap.put(CONVERTER_CLASS_FIELD, dataSourceProperties.getConverterClass());
|
||||
propertyMap.put(DATA_TYPE_FIELD, dataSourceProperties.getDataType());
|
||||
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(dataSourceProperties.getFactoryBeanName());
|
||||
|
||||
propertyMap.forEach((propertyName, propertyValue) -> {
|
||||
Field field = ReflectionUtils.findField(dataSourceProperties.getClass(),
|
||||
propertyName);
|
||||
if (null == field) {
|
||||
return;
|
||||
}
|
||||
if (DATA_TYPE_FIELD.equals(propertyName)) {
|
||||
String dataType = StringUtils.trimAllWhitespace(propertyValue.toString());
|
||||
if (CUSTOM_DATA_TYPE.equals(dataType)) {
|
||||
try {
|
||||
if (StringUtils
|
||||
.isEmpty(dataSourceProperties.getConverterClass())) {
|
||||
throw new RuntimeException("[Sentinel Starter] DataSource "
|
||||
+ dataSourceName
|
||||
+ "dataType is custom, please set converter-class "
|
||||
+ "property");
|
||||
}
|
||||
// construct custom Converter with 'converterClass'
|
||||
// configuration and register
|
||||
String customConvertBeanName = "sentinel-"
|
||||
+ dataSourceProperties.getConverterClass();
|
||||
if (!this.beanFactory.containsBean(customConvertBeanName)) {
|
||||
this.beanFactory.registerBeanDefinition(customConvertBeanName,
|
||||
BeanDefinitionBuilder
|
||||
.genericBeanDefinition(
|
||||
Class.forName(dataSourceProperties
|
||||
.getConverterClass()))
|
||||
.getBeanDefinition());
|
||||
}
|
||||
builder.addPropertyReference("converter", customConvertBeanName);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
log.error("[Sentinel Starter] DataSource " + dataSourceName
|
||||
+ " handle "
|
||||
+ dataSourceProperties.getClass().getSimpleName()
|
||||
+ " error, class name: "
|
||||
+ dataSourceProperties.getConverterClass());
|
||||
throw new RuntimeException("[Sentinel Starter] DataSource "
|
||||
+ dataSourceName + " handle "
|
||||
+ dataSourceProperties.getClass().getSimpleName()
|
||||
+ " error, class name: "
|
||||
+ dataSourceProperties.getConverterClass(), e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!dataTypeList.contains(
|
||||
StringUtils.trimAllWhitespace(propertyValue.toString()))) {
|
||||
throw new RuntimeException("[Sentinel Starter] DataSource "
|
||||
+ dataSourceName + " dataType: " + propertyValue
|
||||
+ " is not support now. please using these types: "
|
||||
+ dataTypeList.toString());
|
||||
}
|
||||
// converter type now support xml or json.
|
||||
// The bean name of these converters wrapped by
|
||||
// 'sentinel-{converterType}-{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));
|
||||
}
|
||||
});
|
||||
|
||||
this.beanFactory.registerBeanDefinition(dataSourceName,
|
||||
builder.getBeanDefinition());
|
||||
// init in Spring
|
||||
AbstractDataSource newDataSource = (AbstractDataSource) this.beanFactory
|
||||
.getBean(dataSourceName);
|
||||
|
||||
// register property in RuleManager
|
||||
dataSourceProperties.postRegister(newDataSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||
import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||
|
||||
/**
|
||||
* Interceptor using by SentinelRestTemplate
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private final SentinelRestTemplate sentinelRestTemplate;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public SentinelProtectInterceptor(SentinelRestTemplate sentinelRestTemplate,
|
||||
RestTemplate restTemplate) {
|
||||
this.sentinelRestTemplate = sentinelRestTemplate;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
URI uri = request.getURI();
|
||||
String hostResource = request.getMethod().toString() + ":" + uri.getScheme()
|
||||
+ "://" + uri.getHost()
|
||||
+ (uri.getPort() == -1 ? "" : ":" + uri.getPort());
|
||||
String hostWithPathResource = hostResource + uri.getPath();
|
||||
boolean entryWithPath = true;
|
||||
if (hostResource.equals(hostWithPathResource)) {
|
||||
entryWithPath = false;
|
||||
}
|
||||
Entry hostEntry = null, hostWithPathEntry = null;
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
hostEntry = SphU.entry(hostResource, EntryType.OUT);
|
||||
if (entryWithPath) {
|
||||
hostWithPathEntry = SphU.entry(hostWithPathResource, EntryType.OUT);
|
||||
}
|
||||
response = execution.execute(request, body);
|
||||
if (this.restTemplate.getErrorHandler().hasError(response)) {
|
||||
Tracer.trace(
|
||||
new IllegalStateException("RestTemplate ErrorHandler has error"));
|
||||
}
|
||||
}
|
||||
catch (Throwable e) {
|
||||
if (!BlockException.isBlockException(e)) {
|
||||
Tracer.trace(e);
|
||||
}
|
||||
else {
|
||||
return handleBlockException(request, body, execution, (BlockException) e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (hostWithPathEntry != null) {
|
||||
hostWithPathEntry.exit();
|
||||
}
|
||||
if (hostEntry != null) {
|
||||
hostEntry.exit();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution, BlockException ex) {
|
||||
Object[] args = new Object[] { request, body, execution, ex };
|
||||
// handle degrade
|
||||
if (isDegradeFailure(ex)) {
|
||||
Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(),
|
||||
sentinelRestTemplate.fallbackClass());
|
||||
if (fallbackMethod != null) {
|
||||
return methodInvoke(fallbackMethod, args);
|
||||
}
|
||||
else {
|
||||
return new SentinelClientHttpResponse();
|
||||
}
|
||||
}
|
||||
// handle flow
|
||||
Method blockHandler = extractBlockHandlerMethod(
|
||||
sentinelRestTemplate.blockHandler(),
|
||||
sentinelRestTemplate.blockHandlerClass());
|
||||
if (blockHandler != null) {
|
||||
return methodInvoke(blockHandler, args);
|
||||
}
|
||||
else {
|
||||
return new SentinelClientHttpResponse();
|
||||
}
|
||||
}
|
||||
|
||||
private ClientHttpResponse methodInvoke(Method method, Object... args) {
|
||||
try {
|
||||
return (ClientHttpResponse) method.invoke(null, args);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Method extractFallbackMethod(String fallback, Class<?> fallbackClass) {
|
||||
return BlockClassRegistry.lookupFallback(fallbackClass, fallback);
|
||||
}
|
||||
|
||||
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
||||
return BlockClassRegistry.lookupBlockHandler(blockClass, block);
|
||||
}
|
||||
|
||||
private boolean isDegradeFailure(BlockException ex) {
|
||||
return ex instanceof DegradeException;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.log.LogBase;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import com.alibaba.csp.sentinel.util.AppNameUtil;
|
||||
|
||||
/**
|
||||
* Endpoint for Sentinel, contains ans properties and rules
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Endpoint(id = "sentinel")
|
||||
public class SentinelEndpoint {
|
||||
|
||||
private final SentinelProperties sentinelProperties;
|
||||
|
||||
public SentinelEndpoint(SentinelProperties sentinelProperties) {
|
||||
this.sentinelProperties = sentinelProperties;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> invoke() {
|
||||
final Map<String, Object> result = new HashMap<>();
|
||||
if (sentinelProperties.isEnabled()) {
|
||||
|
||||
result.put("appName", AppNameUtil.getAppName());
|
||||
result.put("logDir", LogBase.getLogBaseDir());
|
||||
result.put("logUsePid", LogBase.isLogNameUsePid());
|
||||
result.put("blockPage", WebServletConfig.getBlockPage());
|
||||
result.put("metricsFileSize", SentinelConfig.singleMetricFileSize());
|
||||
result.put("metricsFileCharset", SentinelConfig.charset());
|
||||
result.put("totalMetricsFileCount", SentinelConfig.totalMetricFileCount());
|
||||
result.put("consoleServer", TransportConfig.getConsoleServer());
|
||||
result.put("clientIp", TransportConfig.getHeartbeatClientIp());
|
||||
result.put("heartbeatIntervalMs", TransportConfig.getHeartbeatIntervalMs());
|
||||
result.put("clientPort", TransportConfig.getPort());
|
||||
result.put("coldFactor", sentinelProperties.getFlow().getColdFactor());
|
||||
result.put("filter", sentinelProperties.getFilter());
|
||||
result.put("datasource", sentinelProperties.getDatasource());
|
||||
|
||||
final Map<String, Object> rules = new HashMap<>();
|
||||
result.put("rules", rules);
|
||||
rules.put("flowRules", FlowRuleManager.getRules());
|
||||
rules.put("degradeRules", DegradeRuleManager.getRules());
|
||||
rules.put("systemRules", SystemRuleManager.getRules());
|
||||
rules.put("authorityRule", AuthorityRuleManager.getRules());
|
||||
rules.put("paramFlowRule", ParamFlowRuleManager.getRules());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* @author hengyunabc
|
||||
*/
|
||||
@ConditionalOnClass(Endpoint.class)
|
||||
@EnableConfigurationProperties({ SentinelProperties.class })
|
||||
public class SentinelEndpointAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public SentinelEndpoint sentinelEndPoint(SentinelProperties sentinelProperties) {
|
||||
return new SentinelEndpoint(sentinelProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledHealthIndicator("sentinel")
|
||||
public SentinelHealthIndicator sentinelHealthIndicator(DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties) {
|
||||
return new SentinelHealthIndicator(beanFactory, sentinelProperties);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
||||
import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider;
|
||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link HealthIndicator} for Sentinel, which checks the status of
|
||||
* Sentinel Dashboard and DataSource.
|
||||
*
|
||||
* <p>
|
||||
* Check the status of Sentinel Dashboard by sending a heartbeat message to it.
|
||||
* If return true, it's OK.
|
||||
*
|
||||
* Check the status of Sentinel DataSource by calling loadConfig method of {@link AbstractDataSource}.
|
||||
* If no Exception thrown, it's OK.
|
||||
*
|
||||
* If Dashboard and DataSource are both OK, the health status is UP.
|
||||
*</p>
|
||||
*
|
||||
* <p>
|
||||
* Note:
|
||||
* If Sentinel isn't enabled, the health status is up.
|
||||
* If Sentinel Dashboard isn't configured, it's OK and mark the status of Dashboard with UNKNOWN.
|
||||
* More informations are provided in details.
|
||||
* </p>
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
public class SentinelHealthIndicator extends AbstractHealthIndicator {
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private SentinelProperties sentinelProperties;
|
||||
|
||||
public SentinelHealthIndicator(DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.sentinelProperties = sentinelProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doHealthCheck(Health.Builder builder) throws Exception {
|
||||
Map<String, Object> detailMap = new HashMap<>();
|
||||
|
||||
// If sentinel isn't enabled, set the status up and set the enabled to false in detail
|
||||
if (!sentinelProperties.isEnabled()) {
|
||||
detailMap.put("enabled", false);
|
||||
builder.up().withDetails(detailMap);
|
||||
return;
|
||||
}
|
||||
|
||||
detailMap.put("enabled", true);
|
||||
|
||||
// Check health of Dashboard
|
||||
boolean dashboardUp = true;
|
||||
String consoleServer = TransportConfig.getConsoleServer();
|
||||
if (StringUtils.isEmpty(consoleServer)) {
|
||||
// If Dashboard isn't configured, it's OK and mark the status of Dashboard with UNKNOWN.
|
||||
detailMap.put("dashboard", new Status(Status.UNKNOWN.getCode(), "dashboard isn't configured"));
|
||||
} else {
|
||||
// If Dashboard is configured, send a heartbeat message to it and check the result
|
||||
HeartbeatSender heartbeatSender = HeartbeatSenderProvider.getHeartbeatSender();
|
||||
boolean result = heartbeatSender.sendHeartbeat();
|
||||
if (result) {
|
||||
detailMap.put("dashboard", Status.UP);
|
||||
} else {
|
||||
// If failed to send heartbeat message, means that the Dashboard is DOWN
|
||||
dashboardUp = false;
|
||||
detailMap.put("dashboard", new Status(Status.DOWN.getCode(), consoleServer + " can't be connected"));
|
||||
}
|
||||
}
|
||||
|
||||
// Check health of DataSource
|
||||
boolean dataSourceUp = true;
|
||||
Map<String, Object> dataSourceDetailMap = new HashMap<>();
|
||||
detailMap.put("dataSource", dataSourceDetailMap);
|
||||
|
||||
// Get all DataSources and each call loadConfig to check if it's OK
|
||||
// If no Exception thrown, it's OK
|
||||
// Note:
|
||||
// Even if the dynamic config center is down, the loadConfig() might return successfully
|
||||
// e.g. for Nacos client, it might retrieve from the local cache)
|
||||
// But in most circumstances it's okay
|
||||
Map<String, AbstractDataSource> dataSourceMap = beanFactory.getBeansOfType(AbstractDataSource.class);
|
||||
for (Map.Entry<String, AbstractDataSource> dataSourceMapEntry : dataSourceMap.entrySet()) {
|
||||
String dataSourceBeanName = dataSourceMapEntry.getKey();
|
||||
AbstractDataSource dataSource = dataSourceMapEntry.getValue();
|
||||
try {
|
||||
dataSource.loadConfig();
|
||||
dataSourceDetailMap.put(dataSourceBeanName, Status.UP);
|
||||
} catch (Exception e) {
|
||||
// If one DataSource failed to loadConfig, means that the DataSource is DOWN
|
||||
dataSourceUp = false;
|
||||
dataSourceDetailMap.put(dataSourceBeanName, new Status(Status.DOWN.getCode(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
// If Dashboard and DataSource are both OK, the health status is UP
|
||||
if (dashboardUp && dataSourceUp) {
|
||||
builder.up().withDetails(detailMap);
|
||||
} else {
|
||||
builder.down().withDetails(detailMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.feign;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.MethodMetadata;
|
||||
|
||||
/**
|
||||
*
|
||||
* Using static field {@link SentinelContractHolder#METADATA_MAP} to hold
|
||||
* {@link MethodMetadata} data
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class SentinelContractHolder implements Contract {
|
||||
|
||||
private final Contract delegate;
|
||||
|
||||
/**
|
||||
* map key is constructed by ClassFullName + configKey. configKey is constructed by
|
||||
* {@link feign.Feign#configKey}
|
||||
*/
|
||||
public final static Map<String, MethodMetadata> METADATA_MAP = new HashMap<>();
|
||||
|
||||
public SentinelContractHolder(Contract delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
|
||||
List<MethodMetadata> metadatas = delegate.parseAndValidatateMetadata(targetType);
|
||||
metadatas.forEach(metadata -> METADATA_MAP
|
||||
.put(targetType.getName() + metadata.configKey(), metadata));
|
||||
return metadatas;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.feign;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.cloud.openfeign.FeignContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import feign.Contract;
|
||||
import feign.Feign;
|
||||
import feign.InvocationHandlerFactory;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
|
||||
/**
|
||||
* {@link Feign.Builder} like {@link HystrixFeign.Builder}
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class SentinelFeign {
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder extends Feign.Builder
|
||||
implements ApplicationContextAware {
|
||||
|
||||
private Contract contract = new Contract.Default();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private FeignContext feignContext;
|
||||
|
||||
@Override
|
||||
public Feign.Builder invocationHandlerFactory(
|
||||
InvocationHandlerFactory invocationHandlerFactory) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder contract(Contract contract) {
|
||||
this.contract = contract;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Feign build() {
|
||||
super.invocationHandlerFactory(new InvocationHandlerFactory() {
|
||||
@Override
|
||||
public InvocationHandler create(Target target,
|
||||
Map<Method, MethodHandler> dispatch) {
|
||||
// using reflect get fallback and fallbackFactory properties from
|
||||
// FeignClientFactoryBean because FeignClientFactoryBean is a package
|
||||
// level class, we can not use it in our package
|
||||
Object feignClientFactoryBean = Builder.this.applicationContext
|
||||
.getBean("&" + target.type().getName());
|
||||
|
||||
Class fallback = (Class) getFieldValue(feignClientFactoryBean,
|
||||
"fallback");
|
||||
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
|
||||
"fallbackFactory");
|
||||
String name = (String) getFieldValue(feignClientFactoryBean, "name");
|
||||
|
||||
Object fallbackInstance;
|
||||
FallbackFactory fallbackFactoryInstance;
|
||||
// check fallback and fallbackFactory properties
|
||||
if (void.class != fallback) {
|
||||
fallbackInstance = getFromContext(name, "fallback", fallback,
|
||||
target.type());
|
||||
return new SentinelInvocationHandler(target, dispatch,
|
||||
new FallbackFactory.Default(fallbackInstance));
|
||||
}
|
||||
if (void.class != fallbackFactory) {
|
||||
fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
|
||||
"fallbackFactory", fallbackFactory,
|
||||
FallbackFactory.class);
|
||||
return new SentinelInvocationHandler(target, dispatch,
|
||||
fallbackFactoryInstance);
|
||||
}
|
||||
return new SentinelInvocationHandler(target, dispatch);
|
||||
}
|
||||
|
||||
private Object getFromContext(String name, String type,
|
||||
Class fallbackType, Class targetType) {
|
||||
Object fallbackInstance = feignContext.getInstance(name,
|
||||
fallbackType);
|
||||
if (fallbackInstance == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"No %s instance of type %s found for feign client %s",
|
||||
type, fallbackType, name));
|
||||
}
|
||||
|
||||
if (!targetType.isAssignableFrom(fallbackType)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
|
||||
type, fallbackType, targetType, name));
|
||||
}
|
||||
return fallbackInstance;
|
||||
}
|
||||
});
|
||||
|
||||
super.contract(new SentinelContractHolder(contract));
|
||||
return super.build();
|
||||
}
|
||||
|
||||
private Object getFieldValue(Object instance, String fieldName) {
|
||||
Field field = ReflectionUtils.findField(instance.getClass(), fieldName);
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
return field.get(instance);
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
feignContext = this.applicationContext.getBean(FeignContext.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.feign;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
|
||||
import feign.Feign;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ SphU.class, Feign.class })
|
||||
public class SentinelFeignAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(name = "feign.sentinel.enabled")
|
||||
public Feign.Builder feignSentinelBuilder() {
|
||||
return SentinelFeign.builder();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.feign;
|
||||
|
||||
import static feign.Util.checkNotNull;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.InvocationHandlerFactory.MethodHandler;
|
||||
import feign.MethodMetadata;
|
||||
import feign.Target;
|
||||
import feign.hystrix.FallbackFactory;
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} handle invocation that protected by Sentinel
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class SentinelInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Target<?> target;
|
||||
private final Map<Method, MethodHandler> dispatch;
|
||||
|
||||
private FallbackFactory fallbackFactory;
|
||||
private Map<Method, Method> fallbackMethodMap;
|
||||
|
||||
SentinelInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
|
||||
FallbackFactory fallbackFactory) {
|
||||
this.target = checkNotNull(target, "target");
|
||||
this.dispatch = checkNotNull(dispatch, "dispatch");
|
||||
this.fallbackFactory = fallbackFactory;
|
||||
this.fallbackMethodMap = toFallbackMethod(dispatch);
|
||||
}
|
||||
|
||||
SentinelInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch) {
|
||||
this.target = checkNotNull(target, "target");
|
||||
this.dispatch = checkNotNull(dispatch, "dispatch");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final Object proxy, final Method method, final Object[] args)
|
||||
throws Throwable {
|
||||
if ("equals".equals(method.getName())) {
|
||||
try {
|
||||
Object otherHandler = args.length > 0 && args[0] != null
|
||||
? Proxy.getInvocationHandler(args[0])
|
||||
: null;
|
||||
return equals(otherHandler);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ("hashCode".equals(method.getName())) {
|
||||
return hashCode();
|
||||
}
|
||||
else if ("toString".equals(method.getName())) {
|
||||
return toString();
|
||||
}
|
||||
|
||||
Object result;
|
||||
MethodHandler methodHandler = this.dispatch.get(method);
|
||||
// only handle by HardCodedTarget
|
||||
if (target instanceof Target.HardCodedTarget) {
|
||||
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
|
||||
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
|
||||
.get(hardCodedTarget.type().getName()
|
||||
+ Feign.configKey(hardCodedTarget.type(), method));
|
||||
// resource default is HttpMethod:protocol://url
|
||||
String resourceName = methodMetadata.template().method().toUpperCase() + ":"
|
||||
+ hardCodedTarget.url() + methodMetadata.template().path();
|
||||
Entry entry = null;
|
||||
try {
|
||||
ContextUtil.enter(resourceName);
|
||||
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
|
||||
result = methodHandler.invoke(args);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// fallback handle
|
||||
if (!BlockException.isBlockException(ex)) {
|
||||
Tracer.trace(ex);
|
||||
}
|
||||
if (fallbackFactory != null) {
|
||||
try {
|
||||
Object fallbackResult = fallbackMethodMap.get(method)
|
||||
.invoke(fallbackFactory.create(ex), args);
|
||||
return fallbackResult;
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
// shouldn't happen as method is public due to being an interface
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new AssertionError(e.getCause());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// throw exception if fallbackFactory is null
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (entry != null) {
|
||||
entry.exit(1, args);
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// other target type using default strategy
|
||||
result = methodHandler.invoke(args);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof SentinelInvocationHandler) {
|
||||
SentinelInvocationHandler other = (SentinelInvocationHandler) obj;
|
||||
return target.equals(other.target);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return target.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return target.toString();
|
||||
}
|
||||
|
||||
static Map<Method, Method> toFallbackMethod(Map<Method, MethodHandler> dispatch) {
|
||||
Map<Method, Method> result = new LinkedHashMap<>();
|
||||
for (Method method : dispatch.keySet()) {
|
||||
method.setAccessible(true);
|
||||
result.put(method, method);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.rest;
|
||||
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.AbstractClientHttpResponse;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Using by {@link SentinelRestTemplate} and {@link SentinelProtectInterceptor}
|
||||
*
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
*/
|
||||
public class SentinelClientHttpResponse extends AbstractClientHttpResponse {
|
||||
|
||||
private String blockResponse = "RestTemplate request block by sentinel";
|
||||
|
||||
public SentinelClientHttpResponse() {
|
||||
}
|
||||
|
||||
public SentinelClientHttpResponse(String blockResponse) {
|
||||
this.blockResponse = blockResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawStatusCode() throws IOException {
|
||||
return HttpStatus.OK.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusText() throws IOException {
|
||||
return blockResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// nothing do
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getBody() throws IOException {
|
||||
return new ByteArrayInputStream(blockResponse.getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
Map<String, List<String>> headers = new HashMap<>();
|
||||
headers.put(HttpHeaders.CONTENT_TYPE,
|
||||
Arrays.asList(MediaType.APPLICATION_JSON_UTF8_VALUE));
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(headers);
|
||||
return httpHeaders;
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.cloud.sentinel.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "enable or disable sentinel auto configure."
|
||||
},
|
||||
{
|
||||
"name": "resttemplate.sentinel.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "enable or disable @SentinelRestTemplate."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.eager",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": false,
|
||||
"description": "earlier initialize heart-beat when the spring container starts when the transport dependency is on classpath, the configuration is effective."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.transport.port",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "8719",
|
||||
"description": "sentinel api port."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.transport.clientIp",
|
||||
"type": "java.lang.String",
|
||||
"description": "sentinel client ip connect to dashboard."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.transport.dashboard",
|
||||
"type": "java.lang.String",
|
||||
"description": "sentinel dashboard address, won't try to connect dashboard when address is empty."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.transport.heartbeatIntervalMs",
|
||||
"type": "java.lang.String",
|
||||
"description": "send heartbeat interval millisecond."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.filter.order",
|
||||
"type": "java.lang.Integer",
|
||||
"defaultValue": "Integer.MIN_VALUE",
|
||||
"description": "sentinel filter chain order, will be set to FilterRegistrationBean."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.filter.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "Enable to instance com.alibaba.csp.sentinel.adapter.servlet.CommonFilter."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.metric.charset",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "UTF-8",
|
||||
"description": "charset when sentinel write or search metric file."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.metric.fileSingleSize",
|
||||
"type": "java.lang.String",
|
||||
"description": "the metric file size."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.metric.fileTotalCount",
|
||||
"type": "java.lang.String",
|
||||
"description": "the total metric file count."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.log.dir",
|
||||
"type": "java.lang.String",
|
||||
"description": "log base directory."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.log.switch-pid",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": false,
|
||||
"description": "log file should with pid."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.servlet.blockPage",
|
||||
"type": "java.lang.String",
|
||||
"description": "the process page when the flow control is triggered."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.flow.coldFactor",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "3",
|
||||
"description": "sentinel the cold factor."
|
||||
},
|
||||
{
|
||||
"name": "management.health.sentinel.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to enable sentinel health check.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"defaultValue": "false",
|
||||
"name": "feign.sentinel.enabled",
|
||||
"description": "If true, an OpenFeign client will be wrapped with a Sentinel circuit breaker.",
|
||||
"type": "java.lang.Boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.SentinelWebFluxAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.feign.SentinelFeignAutoConfiguration
|
||||
|
||||
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
|
||||
org.springframework.cloud.alibaba.sentinel.custom.SentinelCircuitBreakerConfiguration
|
||||
@@ -1,337 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
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.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpoint;
|
||||
import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.log.LogBase;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||
* @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 {
|
||||
|
||||
@Autowired
|
||||
private SentinelProperties sentinelProperties;
|
||||
|
||||
@Autowired
|
||||
private FilterRegistrationBean filterRegistrationBean;
|
||||
|
||||
@Autowired
|
||||
private SentinelBeanPostProcessor sentinelBeanPostProcessor;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplateWithBlockClass;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplateWithoutBlockClass;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplateWithFallbackClass;
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String flowUrl = "http://localhost:" + port + "/flow";
|
||||
|
||||
private String degradeUrl = "http://localhost:" + port + "/degrade";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
FlowRule rule = new FlowRule();
|
||||
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||
rule.setCount(0);
|
||||
rule.setResource("GET:" + flowUrl);
|
||||
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("GET:" + degradeUrl);
|
||||
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
|
||||
public void testFilter() {
|
||||
assertEquals("Sentinel Filter order was wrong", filterRegistrationBean.getOrder(),
|
||||
123);
|
||||
assertEquals("Sentinel Filter url-pattern was wrong",
|
||||
filterRegistrationBean.getUrlPatterns().size(), 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelSystemProperties() {
|
||||
assertEquals("Sentinel log pid was wrong", true, LogBase.isLogNameUsePid());
|
||||
assertEquals("Sentinel transport console server was wrong",
|
||||
"http://localhost:8080", TransportConfig.getConsoleServer());
|
||||
assertEquals("Sentinel transport port was wrong", "9999",
|
||||
TransportConfig.getPort());
|
||||
assertEquals("Sentinel transport heartbeatIntervalMs was wrong", 20000l,
|
||||
TransportConfig.getHeartbeatIntervalMs().longValue());
|
||||
assertEquals("Sentinel transport clientIp was wrong", "1.1.1.1",
|
||||
TransportConfig.getHeartbeatClientIp());
|
||||
assertEquals("Sentinel metric file size was wrong", 9999,
|
||||
SentinelConfig.singleMetricFileSize());
|
||||
assertEquals("Sentinel metric file count was wrong", 100,
|
||||
SentinelConfig.totalMetricFileCount());
|
||||
assertEquals("Sentinel metric file charset was wrong", "UTF-8",
|
||||
SentinelConfig.charset());
|
||||
assertEquals("Sentinel block page was wrong", "/error",
|
||||
WebServletConfig.getBlockPage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlowRestTemplate() {
|
||||
assertEquals("RestTemplate interceptors size was wrong", 2,
|
||||
restTemplate.getInterceptors().size());
|
||||
assertEquals("RestTemplateWithBlockClass interceptors size was wrong", 1,
|
||||
restTemplateWithBlockClass.getInterceptors().size());
|
||||
ResponseEntity responseEntityBlock = restTemplateWithBlockClass
|
||||
.getForEntity(flowUrl, 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(flowUrl,
|
||||
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(flowUrl, String.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFallbackRestTemplate() {
|
||||
ResponseEntity responseEntity = restTemplateWithFallbackClass
|
||||
.getForEntity(degradeUrl, String.class);
|
||||
assertEquals("RestTemplateWithFallbackClass Sentinel Message was wrong",
|
||||
"Oops fallback", responseEntity.getBody());
|
||||
assertEquals("RestTemplateWithFallbackClass Sentinel Http Status Code was wrong",
|
||||
HttpStatus.OK, responseEntity.getStatusCode());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SentinelTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@SentinelRestTemplate
|
||||
RestTemplate restTemplate() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
restTemplate.getInterceptors().add(mock(ClientHttpRequestInterceptor.class));
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SentinelRestTemplate(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")
|
||||
RestTemplate restTemplateWithBlockClass() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SentinelRestTemplate(fallbackClass = ExceptionUtil.class, fallback = "fallbackException")
|
||||
RestTemplate restTemplateWithFallbackClass() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestTemplate restTemplateWithoutBlockClass() {
|
||||
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 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 {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
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;
|
||||
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 javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
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;
|
||||
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 java.util.Arrays;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
@Autowired
|
||||
private BazService bazService;
|
||||
|
||||
@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);
|
||||
FlowRule rule4 = new FlowRule();
|
||||
rule4.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||
rule4.setCount(0);
|
||||
rule4.setResource("GET:http://baz-service/baz");
|
||||
rule4.setLimitApp("default");
|
||||
rule4.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
|
||||
rule4.setStrategy(RuleConstant.STRATEGY_DIRECT);
|
||||
FlowRuleManager.loadRules(Arrays.asList(rule1, rule2, rule3,rule4));
|
||||
}
|
||||
|
||||
@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();
|
||||
});
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(() -> {
|
||||
bazService.baz();
|
||||
});
|
||||
|
||||
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 interface BazService {
|
||||
@RequestMapping(path = "baz")
|
||||
String baz();
|
||||
}
|
||||
|
||||
@FeignClient(value = "baz-service")
|
||||
public interface BazClient extends BazService {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
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 static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalWithoutParam() {
|
||||
new AnnotationConfigApplicationContext(TestConfig10.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();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class TestConfig10 {
|
||||
@Bean
|
||||
SentinelBeanPostProcessor sentinelBeanPostProcessor(
|
||||
ApplicationContext applicationContext) {
|
||||
return new SentinelBeanPostProcessor(applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SentinelRestTemplate
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SentinelRestTemplate
|
||||
RestTemplate restTemplate2() {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel;
|
||||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
|
||||
import com.alibaba.csp.sentinel.heartbeat.HeartbeatSenderProvider;
|
||||
import com.alibaba.csp.sentinel.transport.HeartbeatSender;
|
||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Test cases for {@link SentinelHealthIndicator}.
|
||||
*
|
||||
* @author cdfive
|
||||
*/
|
||||
public class SentinelHealthIndicatorTests {
|
||||
|
||||
private SentinelHealthIndicator sentinelHealthIndicator;
|
||||
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private SentinelProperties sentinelProperties;
|
||||
|
||||
private HeartbeatSender heartbeatSender;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
beanFactory = mock(DefaultListableBeanFactory.class);
|
||||
sentinelProperties = mock(SentinelProperties.class);
|
||||
sentinelHealthIndicator = new SentinelHealthIndicator(beanFactory, sentinelProperties);
|
||||
|
||||
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "");
|
||||
|
||||
heartbeatSender = mock(HeartbeatSender.class);
|
||||
Field heartbeatSenderField = ReflectionUtils.findField(HeartbeatSenderProvider.class, "heartbeatSender");
|
||||
heartbeatSenderField.setAccessible(true);
|
||||
ReflectionUtils.setField(heartbeatSenderField, null, heartbeatSender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelNotEnabled() {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(false);
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails().get("enabled")).isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelDashboardNotConfigured() {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(true);
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
assertThat(health.getDetails().get("dashboard")).isEqualTo(Status.UNKNOWN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelDashboardConfiguredSuccess() throws Exception {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(true);
|
||||
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080");
|
||||
when(heartbeatSender.sendHeartbeat()).thenReturn(true);
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelDashboardConfiguredFailed() throws Exception {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(true);
|
||||
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080");
|
||||
when(heartbeatSender.sendHeartbeat()).thenReturn(false);
|
||||
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
|
||||
assertThat(health.getDetails().get("dashboard")).isEqualTo(new Status(Status.DOWN.getCode(), "localhost:8080 can't be connected"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelDataSourceSuccess() throws Exception {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(true);
|
||||
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080");
|
||||
when(heartbeatSender.sendHeartbeat()).thenReturn(true);
|
||||
|
||||
Map<String, AbstractDataSource> dataSourceMap = new HashMap<>();
|
||||
|
||||
FileRefreshableDataSource fileDataSource1 = mock(FileRefreshableDataSource.class);
|
||||
dataSourceMap.put("ds1-sentinel-file-datasource", fileDataSource1);
|
||||
|
||||
FileRefreshableDataSource fileDataSource2 = mock(FileRefreshableDataSource.class);
|
||||
dataSourceMap.put("ds2-sentinel-file-datasource", fileDataSource2);
|
||||
|
||||
when(beanFactory.getBeansOfType(AbstractDataSource.class)).thenReturn(dataSourceMap);
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.UP);
|
||||
Map<String, Status> dataSourceDetailMap = (Map<String, Status>) health.getDetails().get("dataSource");
|
||||
assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource")).isEqualTo(Status.UP);
|
||||
assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource")).isEqualTo(Status.UP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelDataSourceFailed() throws Exception {
|
||||
when(sentinelProperties.isEnabled()).thenReturn(true);
|
||||
SentinelConfig.setConfig(TransportConfig.CONSOLE_SERVER, "localhost:8080");
|
||||
when(heartbeatSender.sendHeartbeat()).thenReturn(true);
|
||||
|
||||
Map<String, AbstractDataSource> dataSourceMap = new HashMap<>();
|
||||
|
||||
FileRefreshableDataSource fileDataSource1 = mock(FileRefreshableDataSource.class);
|
||||
dataSourceMap.put("ds1-sentinel-file-datasource", fileDataSource1);
|
||||
|
||||
FileRefreshableDataSource fileDataSource2 = mock(FileRefreshableDataSource.class);
|
||||
when(fileDataSource2.loadConfig()).thenThrow(new RuntimeException("fileDataSource2 error"));
|
||||
dataSourceMap.put("ds2-sentinel-file-datasource", fileDataSource2);
|
||||
|
||||
when(beanFactory.getBeansOfType(AbstractDataSource.class)).thenReturn(dataSourceMap);
|
||||
|
||||
Health health = sentinelHealthIndicator.health();
|
||||
|
||||
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
|
||||
Map<String, Status> dataSourceDetailMap = (Map<String, Status>) health.getDetails().get("dataSource");
|
||||
assertThat(dataSourceDetailMap.get("ds1-sentinel-file-datasource")).isEqualTo(Status.UP);
|
||||
assertThat(dataSourceDetailMap.get("ds2-sentinel-file-datasource")).isEqualTo(new Status(Status.DOWN.getCode(), "fileDataSource2 error"));
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
[
|
||||
{
|
||||
"resource": "good",
|
||||
"limitApp": "abc",
|
||||
"strategy": 0
|
||||
},
|
||||
{
|
||||
"resource": "bad",
|
||||
"limitApp": "bcd",
|
||||
"strategy": 1
|
||||
},
|
||||
{
|
||||
"resource": "terrible",
|
||||
"limitApp": "aaa",
|
||||
"strategy": 1
|
||||
}
|
||||
]
|
||||
@@ -1,16 +0,0 @@
|
||||
[
|
||||
{
|
||||
"resource": "abc0",
|
||||
"count": 20.0,
|
||||
"grade": 0,
|
||||
"passCount": 0,
|
||||
"timeWindow": 10
|
||||
},
|
||||
{
|
||||
"resource": "abc1",
|
||||
"count": 15.0,
|
||||
"grade": 0,
|
||||
"passCount": 0,
|
||||
"timeWindow": 10
|
||||
}
|
||||
]
|
||||
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
@@ -1,16 +0,0 @@
|
||||
[
|
||||
{
|
||||
"resource": "hotResource",
|
||||
"count": 0,
|
||||
"grade": 1,
|
||||
"limitApp": "default",
|
||||
"paramIdx": 0,
|
||||
"paramFlowItemList": [
|
||||
{
|
||||
"object": "2",
|
||||
"classType": "int",
|
||||
"count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,8 +0,0 @@
|
||||
[
|
||||
{
|
||||
"highestSystemLoad": -1,
|
||||
"qps": 100,
|
||||
"avgRt": -1,
|
||||
"maxThread": 10
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user