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

#734 -> update pkg name

This commit is contained in:
fangjian0423
2019-07-08 22:42:08 +08:00
parent aa03ddbbe2
commit 52ac888164
477 changed files with 1584 additions and 1259 deletions

View File

@@ -0,0 +1,34 @@
/*
* 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 com.alibaba.cloud.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";
}

View File

@@ -0,0 +1,371 @@
/*
* 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 com.alibaba.cloud.sentinel;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.springframework.boot.context.properties.ConfigurationProperties;
import com.alibaba.cloud.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;
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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 com.alibaba.cloud.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;
}
}

View File

@@ -0,0 +1,92 @@
/*
* 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 com.alibaba.cloud.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();
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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 com.alibaba.cloud.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;
}

View File

@@ -0,0 +1,59 @@
/*
* 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 com.alibaba.cloud.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);
}
}

View File

@@ -0,0 +1,242 @@
/*
* 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 com.alibaba.cloud.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 com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter;
import com.alibaba.cloud.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);
}
}
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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 com.alibaba.cloud.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 com.alibaba.cloud.sentinel.SentinelConstants;
import com.alibaba.cloud.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);
}
}

View File

@@ -0,0 +1,12 @@
package com.alibaba.cloud.sentinel.custom;
import org.springframework.context.annotation.Configuration;
/**
* @author lengleng
* <p>
* support @EnableCircuitBreaker ,Do nothing
*/
@Configuration
public class SentinelCircuitBreakerConfiguration {
}

View File

@@ -0,0 +1,191 @@
package com.alibaba.cloud.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 com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.datasource.config.AbstractDataSourceProperties;
import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter;
import com.alibaba.cloud.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);
}
}

View File

@@ -0,0 +1,150 @@
/*
* 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 com.alibaba.cloud.sentinel.custom;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import com.alibaba.cloud.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;
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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 com.alibaba.cloud.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 com.alibaba.cloud.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;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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 com.alibaba.cloud.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 com.alibaba.cloud.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);
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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 com.alibaba.cloud.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 com.alibaba.cloud.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);
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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 com.alibaba.cloud.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;
}
}

View File

@@ -0,0 +1,149 @@
/*
* 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 com.alibaba.cloud.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);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.cloud.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();
}
}

View File

@@ -0,0 +1,172 @@
/*
* 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 com.alibaba.cloud.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;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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 com.alibaba.cloud.sentinel.rest;
import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import com.alibaba.cloud.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;
}
}