mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
refactor directory tree
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 PROPERTY_ITEM_SEPARATOR = ".";
|
||||
|
||||
String PROPERTY_DATASOURCE_NAME = "datasource";
|
||||
|
||||
String PROPERTY_DATASOURCE_PREFIX = PROPERTY_PREFIX + PROPERTY_ITEM_SEPARATOR
|
||||
+ PROPERTY_DATASOURCE_NAME;
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
* @author hengyunabc
|
||||
*/
|
||||
@ConfigurationProperties(prefix = SentinelConstants.PROPERTY_PREFIX)
|
||||
public class SentinelProperties {
|
||||
|
||||
/**
|
||||
* Enable sentinel auto configure, the default value is true
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* sentinel api port,default value is 8721
|
||||
*/
|
||||
private String port = "8721";
|
||||
|
||||
/**
|
||||
* Sentinel dashboard address, won't try to connect dashboard when address is empty
|
||||
*/
|
||||
private String dashboard = "";
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private Filter filter;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
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 Filter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void setFilter(Filter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 javax.servlet.Filter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(SentinelProperties.class)
|
||||
public class SentinelWebAutoConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(SentinelWebAutoConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
private SentinelProperties properties;
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean servletRequestListener() {
|
||||
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
|
||||
|
||||
SentinelProperties.Filter filterConfig = properties.getFilter();
|
||||
|
||||
if (null == filterConfig) {
|
||||
filterConfig = new SentinelProperties.Filter();
|
||||
properties.setFilter(filterConfig);
|
||||
}
|
||||
|
||||
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());
|
||||
logger.info("[Sentinel Starter] register Sentinel with urlPatterns: {}.",
|
||||
filterConfig.getUrlPatterns());
|
||||
return registration;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* An annotation to inject {@link com.alibaba.csp.sentinel.datasource.DataSource} instance
|
||||
* into a Spring Bean. The Properties of DataSource bean get from config files with
|
||||
* specific prefix.
|
||||
*
|
||||
* @author fangjian
|
||||
* @see com.alibaba.csp.sentinel.datasource.DataSource
|
||||
*/
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface SentinelDataSource {
|
||||
|
||||
@AliasFor("prefix")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor("value")
|
||||
String prefix() default "";
|
||||
|
||||
String name() default ""; // spring bean name
|
||||
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
*/
|
||||
@Target({ ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface SentinelProtect {
|
||||
|
||||
String blockHandler() default "";
|
||||
|
||||
Class<?> blockHandlerClass() default void.class;
|
||||
|
||||
String fallback() default "";
|
||||
|
||||
Class<?> fallbackClass() default void.class;
|
||||
|
||||
}
|
@@ -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 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);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||
import com.alibaba.csp.sentinel.util.AppNameUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.SentinelDataSourcePostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author xiaojing
|
||||
*/
|
||||
@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(AppNameUtil.APP_NAME))) {
|
||||
System.setProperty(AppNameUtil.APP_NAME, projectName);
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT))) {
|
||||
System.setProperty(TransportConfig.SERVER_PORT, properties.getPort());
|
||||
}
|
||||
if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER))) {
|
||||
System.setProperty(TransportConfig.CONSOLE_SERVER, properties.getDashboard());
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SentinelResourceAspect sentinelResourceAspect() {
|
||||
return new SentinelResourceAspect();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
|
||||
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
|
||||
return new SentinelBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SentinelDataSourcePostProcessor sentinelDataSourcePostProcessor() {
|
||||
return new SentinelDataSourcePostProcessor();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* PostProcessor handle @SentinelProtect Annotation, add interceptor for RestTemplate
|
||||
*
|
||||
* @author fangjian
|
||||
* @see SentinelProtect
|
||||
* @see SentinelProtectInterceptor
|
||||
*/
|
||||
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ConcurrentHashMap<String, SentinelProtect> cache = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
|
||||
Class<?> beanType, String beanName) {
|
||||
if (checkSentinelProtect(beanDefinition, beanType)) {
|
||||
SentinelProtect sentinelProtect = ((StandardMethodMetadata) beanDefinition
|
||||
.getSource()).getIntrospectedMethod()
|
||||
.getAnnotation(SentinelProtect.class);
|
||||
cache.put(beanName, sentinelProtect);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
|
||||
Class<?> beanType) {
|
||||
return beanType == RestTemplate.class
|
||||
&& beanDefinition.getSource() instanceof StandardMethodMetadata
|
||||
&& ((StandardMethodMetadata) beanDefinition.getSource())
|
||||
.isAnnotated(SentinelProtect.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (cache.containsKey(beanName)) {
|
||||
// add interceptor for each RestTemplate with @SentinelProtect annotation
|
||||
StringBuilder interceptorBeanName = new StringBuilder();
|
||||
SentinelProtect sentinelProtect = cache.get(beanName);
|
||||
interceptorBeanName
|
||||
.append(StringUtils.uncapitalize(
|
||||
SentinelProtectInterceptor.class.getSimpleName()))
|
||||
.append("_")
|
||||
.append(sentinelProtect.blockHandlerClass().getSimpleName())
|
||||
.append(sentinelProtect.blockHandler()).append("_")
|
||||
.append(sentinelProtect.fallbackClass().getSimpleName())
|
||||
.append(sentinelProtect.fallback());
|
||||
RestTemplate restTemplate = (RestTemplate) bean;
|
||||
registerBean(interceptorBeanName.toString(), sentinelProtect);
|
||||
SentinelProtectInterceptor sentinelProtectInterceptor = applicationContext
|
||||
.getBean(interceptorBeanName.toString(),
|
||||
SentinelProtectInterceptor.class);
|
||||
restTemplate.getInterceptors().add(sentinelProtectInterceptor);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void registerBean(String interceptorBeanName,
|
||||
SentinelProtect sentinelProtect) {
|
||||
// register SentinelProtectInterceptor bean
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
|
||||
.getAutowireCapableBeanFactory();
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(SentinelProtectInterceptor.class);
|
||||
beanDefinitionBuilder.addConstructorArgValue(sentinelProtect);
|
||||
BeanDefinition interceptorBeanDefinition = beanDefinitionBuilder
|
||||
.getRawBeanDefinition();
|
||||
beanFactory.registerBeanDefinition(interceptorBeanName,
|
||||
interceptorBeanDefinition);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.Method;
|
||||
import java.net.URI;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Interceptor using by SentinelProtect and SentinelProtectInterceptor
|
||||
*
|
||||
* @author fangjian
|
||||
*/
|
||||
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(SentinelProtectInterceptor.class);
|
||||
|
||||
private SentinelProtect sentinelProtect;
|
||||
|
||||
public SentinelProtectInterceptor(SentinelProtect sentinelProtect) {
|
||||
this.sentinelProtect = sentinelProtect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
URI uri = request.getURI();
|
||||
String hostResource = uri.getScheme() + "://" + uri.getHost() + ":"
|
||||
+ (uri.getPort() == -1 ? 80 : uri.getPort());
|
||||
String hostWithPathResource = hostResource + uri.getPath();
|
||||
Entry hostEntry = null, hostWithPathEntry = null;
|
||||
ClientHttpResponse response = null;
|
||||
try {
|
||||
ContextUtil.enter(hostWithPathResource);
|
||||
hostWithPathEntry = SphU.entry(hostWithPathResource);
|
||||
hostEntry = SphU.entry(hostResource);
|
||||
response = execution.execute(request, body);
|
||||
}
|
||||
catch (BlockException e) {
|
||||
logger.error("RestTemplate block", e);
|
||||
try {
|
||||
handleBlockException(e);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("sentinel handle BlockException error.", e);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (hostEntry != null) {
|
||||
hostEntry.exit();
|
||||
}
|
||||
if (hostWithPathEntry != null) {
|
||||
hostWithPathEntry.exit();
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private void handleBlockException(BlockException ex) throws Exception {
|
||||
Object[] args = new Object[] { ex };
|
||||
// handle degrade
|
||||
if (isDegradeFailure(ex)) {
|
||||
Method method = extractFallbackMethod(sentinelProtect.fallback(),
|
||||
sentinelProtect.fallbackClass());
|
||||
if (method != null) {
|
||||
method.invoke(null, args);
|
||||
}
|
||||
}
|
||||
// handle block
|
||||
Method blockHandler = extractBlockHandlerMethod(sentinelProtect.blockHandler(),
|
||||
sentinelProtect.blockHandlerClass());
|
||||
if (blockHandler != null) {
|
||||
blockHandler.invoke(null, args);
|
||||
}
|
||||
}
|
||||
|
||||
private Method extractFallbackMethod(String fallback, Class<?> fallbackClass) {
|
||||
if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
|
||||
return null;
|
||||
}
|
||||
Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback);
|
||||
if (cachedMethod == null) {
|
||||
cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback,
|
||||
BlockException.class);
|
||||
BlockClassRegistry.updateFallbackFor(fallbackClass, fallback, cachedMethod);
|
||||
}
|
||||
return cachedMethod;
|
||||
}
|
||||
|
||||
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
||||
if (StringUtil.isBlank(block) || blockClass == void.class) {
|
||||
return null;
|
||||
}
|
||||
Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block);
|
||||
if (cachedMethod == null) {
|
||||
cachedMethod = ClassUtils.getStaticMethod(blockClass, block,
|
||||
BlockException.class);
|
||||
BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod);
|
||||
}
|
||||
return cachedMethod;
|
||||
}
|
||||
|
||||
private boolean isDegradeFailure(BlockException ex) {
|
||||
return ex instanceof DegradeException;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.datasource;
|
||||
|
||||
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.DataSource;
|
||||
|
||||
/**
|
||||
* {@link DataSource} Loader
|
||||
*
|
||||
* @author fangjian
|
||||
*/
|
||||
public class DataSourceLoader {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class);
|
||||
|
||||
private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties";
|
||||
|
||||
private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX
|
||||
+ PROPERTIES_RESOURCE_LOCATION;
|
||||
|
||||
private final static ConcurrentMap<String, Class<? extends DataSource>> dataSourceClassesCache = new ConcurrentHashMap<String, Class<? extends DataSource>>(
|
||||
4);
|
||||
|
||||
static void loadAllDataSourceClassesCache() {
|
||||
Map<String, Class<? extends DataSource>> dataSourceClassesMap = loadAllDataSourceClassesCache(
|
||||
ALL_PROPERTIES_RESOURCES_LOCATION);
|
||||
|
||||
dataSourceClassesCache.putAll(dataSourceClassesMap);
|
||||
}
|
||||
|
||||
static Map<String, Class<? extends DataSource>> loadAllDataSourceClassesCache(
|
||||
String resourcesLocation) {
|
||||
|
||||
Map<String, Class<? extends DataSource>> dataSourcesMap = new HashMap<String, Class<? extends DataSource>>(
|
||||
4);
|
||||
|
||||
ClassLoader classLoader = DataSourceLoader.class.getClassLoader();
|
||||
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
try {
|
||||
|
||||
Resource[] resources = resolver.getResources(resourcesLocation);
|
||||
|
||||
for (Resource resource : resources) {
|
||||
if (resource.exists()) {
|
||||
Properties properties = PropertiesLoaderUtils
|
||||
.loadProperties(resource);
|
||||
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
|
||||
|
||||
String type = (String) entry.getKey();
|
||||
String className = (String) entry.getValue();
|
||||
|
||||
if (!ClassUtils.isPresent(className, classLoader)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Sentinel DataSource implementation [ type : "
|
||||
+ type + ": , class : " + className
|
||||
+ " , url : " + resource.getURL()
|
||||
+ "] was not present in current classpath , "
|
||||
+ "thus loading will be ignored , please add dependency if required !");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Assert.isTrue(!dataSourcesMap.containsKey(type),
|
||||
"The duplicated type[" + type
|
||||
+ "] of SentinelDataSource were found in "
|
||||
+ "resource [" + resource.getURL() + "]");
|
||||
|
||||
Class<?> dataSourceClass = ClassUtils.resolveClassName(className,
|
||||
classLoader);
|
||||
Assert.isAssignable(DataSource.class, dataSourceClass);
|
||||
|
||||
dataSourcesMap.put(type,
|
||||
(Class<? extends DataSource>) dataSourceClass);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Sentinel DataSource implementation [ type : "
|
||||
+ type + ": , class : " + className
|
||||
+ "] was loaded.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return dataSourcesMap;
|
||||
}
|
||||
|
||||
public static Class<? extends DataSource> loadClass(String type)
|
||||
throws IllegalArgumentException {
|
||||
|
||||
Class<? extends DataSource> dataSourceClass = dataSourceClassesCache.get(type);
|
||||
|
||||
if (dataSourceClass == null) {
|
||||
if (dataSourceClassesCache.isEmpty()) {
|
||||
loadAllDataSourceClassesCache();
|
||||
dataSourceClass = dataSourceClassesCache.get(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataSourceClass == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Sentinel DataSource implementation [ type : " + type
|
||||
+ " ] can't be found!");
|
||||
}
|
||||
|
||||
return dataSourceClass;
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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.datasource;
|
||||
|
||||
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
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.SentinelDataSource;
|
||||
import org.springframework.cloud.alibaba.sentinel.util.PropertySourcesUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.ConfigParser;
|
||||
|
||||
/**
|
||||
* {@link SentinelDataSource @SentinelDataSource} Post Processor
|
||||
*
|
||||
* @author fangjian
|
||||
* @see com.alibaba.csp.sentinel.datasource.DataSource
|
||||
* @see SentinelDataSource
|
||||
*/
|
||||
public class SentinelDataSourcePostProcessor
|
||||
extends InstantiationAwareBeanPostProcessorAdapter
|
||||
implements MergedBeanDefinitionPostProcessor {
|
||||
|
||||
private static final Logger logger = LoggerFactory
|
||||
.getLogger(SentinelDataSourcePostProcessor.class);
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private final Map<String, List<SentinelDataSourceField>> dataSourceFieldCache = new ConcurrentHashMap<>(
|
||||
64);
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
|
||||
Class<?> beanType, String beanName) {
|
||||
// find all fields using by @SentinelDataSource annotation
|
||||
ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() {
|
||||
@Override
|
||||
public void doWith(Field field)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
SentinelDataSource annotation = getAnnotation(field,
|
||||
SentinelDataSource.class);
|
||||
if (annotation != null) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn(
|
||||
"@SentinelDataSource annotation is not supported on static fields: "
|
||||
+ field);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (dataSourceFieldCache.containsKey(beanName)) {
|
||||
dataSourceFieldCache.get(beanName)
|
||||
.add(new SentinelDataSourceField(annotation, field));
|
||||
}
|
||||
else {
|
||||
List<SentinelDataSourceField> list = new ArrayList<>();
|
||||
list.add(new SentinelDataSourceField(annotation, field));
|
||||
dataSourceFieldCache.put(beanName, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
|
||||
PropertyDescriptor[] pds, Object bean, String beanName)
|
||||
throws BeanCreationException {
|
||||
if (dataSourceFieldCache.containsKey(beanName)) {
|
||||
List<SentinelDataSourceField> sentinelDataSourceFields = dataSourceFieldCache
|
||||
.get(beanName);
|
||||
sentinelDataSourceFields.forEach(sentinelDataSourceField -> {
|
||||
try {
|
||||
// construct DataSource field annotated by @SentinelDataSource
|
||||
Field field = sentinelDataSourceField.getField();
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
String dataSourceBeanName = constructDataSource(
|
||||
sentinelDataSourceField.getSentinelDataSource());
|
||||
field.set(bean, applicationContext.getBean(dataSourceBeanName));
|
||||
}
|
||||
catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
return pvs;
|
||||
}
|
||||
|
||||
private String constructDataSource(SentinelDataSource annotation) {
|
||||
String prefix = annotation.value();
|
||||
if (StringUtils.isEmpty(prefix)) {
|
||||
prefix = SentinelConstants.PROPERTY_DATASOURCE_PREFIX;
|
||||
}
|
||||
Map<String, Object> propertyMap = PropertySourcesUtils
|
||||
.getSubProperties(environment.getPropertySources(), prefix);
|
||||
String alias = propertyMap.get("type").toString();
|
||||
Class dataSourceClass = DataSourceLoader.loadClass(alias);
|
||||
|
||||
String beanName = StringUtils.isEmpty(annotation.name())
|
||||
? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix
|
||||
: annotation.name();
|
||||
if (applicationContext.containsBean(beanName)) {
|
||||
return beanName;
|
||||
}
|
||||
|
||||
Class targetClass = null;
|
||||
// if alias exists in SentinelDataSourceRegistry, wired properties into
|
||||
// FactoryBean
|
||||
if (SentinelDataSourceRegistry.checkFactoryBean(alias)) {
|
||||
targetClass = SentinelDataSourceRegistry.getFactoryBean(alias);
|
||||
}
|
||||
else {
|
||||
// if alias not exists in SentinelDataSourceRegistry, wired properties into
|
||||
// raw class
|
||||
targetClass = dataSourceClass;
|
||||
}
|
||||
|
||||
registerDataSource(beanName, targetClass, propertyMap);
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
private void registerDataSource(String beanName, Class targetClass,
|
||||
Map<String, Object> propertyMap) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(targetClass);
|
||||
for (String propertyName : propertyMap.keySet()) {
|
||||
Field field = ReflectionUtils.findField(targetClass, propertyName);
|
||||
if (field != null) {
|
||||
if (field.getType().isAssignableFrom(ConfigParser.class)) {
|
||||
// ConfigParser get from ApplicationContext
|
||||
builder.addPropertyReference(propertyName,
|
||||
propertyMap.get(propertyName).toString());
|
||||
}
|
||||
else {
|
||||
// wired properties
|
||||
builder.addPropertyValue(propertyName, propertyMap.get(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
|
||||
.getAutowireCapableBeanFactory();
|
||||
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
class SentinelDataSourceField {
|
||||
private SentinelDataSource sentinelDataSource;
|
||||
private Field field;
|
||||
|
||||
public SentinelDataSourceField(SentinelDataSource sentinelDataSource,
|
||||
Field field) {
|
||||
this.sentinelDataSource = sentinelDataSource;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public SentinelDataSource getSentinelDataSource() {
|
||||
return sentinelDataSource;
|
||||
}
|
||||
|
||||
public void setSentinelDataSource(SentinelDataSource sentinelDataSource) {
|
||||
this.sentinelDataSource = sentinelDataSource;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void setField(Field field) {
|
||||
this.field = field;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.datasource;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.FileRefreshableDataSourceFactoryBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.NacosDataSourceFactoryBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ZookeeperDataSourceFactoryBean;
|
||||
|
||||
/**
|
||||
* Registry to save DataSource FactoryBean
|
||||
*
|
||||
* @author fangjian
|
||||
* @see com.alibaba.csp.sentinel.datasource.DataSource
|
||||
* @see FileRefreshableDataSourceFactoryBean
|
||||
* @see ZookeeperDataSourceFactoryBean
|
||||
* @see NacosDataSourceFactoryBean
|
||||
* @see ApolloDataSourceFactoryBean
|
||||
*/
|
||||
public class SentinelDataSourceRegistry {
|
||||
|
||||
private static ConcurrentHashMap<String, Class<? extends FactoryBean>> cache = new ConcurrentHashMap<>(
|
||||
32);
|
||||
|
||||
static {
|
||||
SentinelDataSourceRegistry.registerFactoryBean("file",
|
||||
FileRefreshableDataSourceFactoryBean.class);
|
||||
SentinelDataSourceRegistry.registerFactoryBean("zk",
|
||||
ZookeeperDataSourceFactoryBean.class);
|
||||
SentinelDataSourceRegistry.registerFactoryBean("nacos",
|
||||
NacosDataSourceFactoryBean.class);
|
||||
SentinelDataSourceRegistry.registerFactoryBean("apollo",
|
||||
ApolloDataSourceFactoryBean.class);
|
||||
}
|
||||
|
||||
public static synchronized void registerFactoryBean(String alias,
|
||||
Class<? extends FactoryBean> clazz) {
|
||||
cache.putIfAbsent(alias, clazz);
|
||||
cache.put(alias, clazz);
|
||||
}
|
||||
|
||||
public static Class<? extends FactoryBean> getFactoryBean(String alias) {
|
||||
return cache.get(alias);
|
||||
}
|
||||
|
||||
public static boolean checkFactoryBean(String alias) {
|
||||
return cache.containsKey(alias);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.ConfigParser;
|
||||
import com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
* @see ApolloDataSource
|
||||
*/
|
||||
public class ApolloDataSourceFactoryBean implements FactoryBean<ApolloDataSource> {
|
||||
|
||||
private String namespaceName;
|
||||
private String flowRulesKey;
|
||||
private String defaultFlowRuleValue;
|
||||
private ConfigParser configParser;
|
||||
|
||||
@Override
|
||||
public ApolloDataSource getObject() throws Exception {
|
||||
return new ApolloDataSource(namespaceName, flowRulesKey, defaultFlowRuleValue,
|
||||
configParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ApolloDataSource.class;
|
||||
}
|
||||
|
||||
public String getNamespaceName() {
|
||||
return namespaceName;
|
||||
}
|
||||
|
||||
public void setNamespaceName(String namespaceName) {
|
||||
this.namespaceName = namespaceName;
|
||||
}
|
||||
|
||||
public String getFlowRulesKey() {
|
||||
return flowRulesKey;
|
||||
}
|
||||
|
||||
public void setFlowRulesKey(String flowRulesKey) {
|
||||
this.flowRulesKey = flowRulesKey;
|
||||
}
|
||||
|
||||
public String getDefaultFlowRuleValue() {
|
||||
return defaultFlowRuleValue;
|
||||
}
|
||||
|
||||
public void setDefaultFlowRuleValue(String defaultFlowRuleValue) {
|
||||
this.defaultFlowRuleValue = defaultFlowRuleValue;
|
||||
}
|
||||
|
||||
public ConfigParser getConfigParser() {
|
||||
return configParser;
|
||||
}
|
||||
|
||||
public void setConfigParser(ConfigParser configParser) {
|
||||
this.configParser = configParser;
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.ConfigParser;
|
||||
import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
* @see FileRefreshableDataSource
|
||||
*/
|
||||
public class FileRefreshableDataSourceFactoryBean
|
||||
implements FactoryBean<FileRefreshableDataSource> {
|
||||
|
||||
private String file;
|
||||
private String charset;
|
||||
private long recommendRefreshMs;
|
||||
private int bufSize;
|
||||
private ConfigParser configParser;
|
||||
|
||||
@Override
|
||||
public FileRefreshableDataSource getObject() throws Exception {
|
||||
return new FileRefreshableDataSource(new File(file), configParser,
|
||||
recommendRefreshMs, bufSize, Charset.forName(charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return FileRefreshableDataSource.class;
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void setFile(String file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
public long getRecommendRefreshMs() {
|
||||
return recommendRefreshMs;
|
||||
}
|
||||
|
||||
public void setRecommendRefreshMs(long recommendRefreshMs) {
|
||||
this.recommendRefreshMs = recommendRefreshMs;
|
||||
}
|
||||
|
||||
public int getBufSize() {
|
||||
return bufSize;
|
||||
}
|
||||
|
||||
public void setBufSize(int bufSize) {
|
||||
this.bufSize = bufSize;
|
||||
}
|
||||
|
||||
public ConfigParser getConfigParser() {
|
||||
return configParser;
|
||||
}
|
||||
|
||||
public void setConfigParser(ConfigParser configParser) {
|
||||
this.configParser = configParser;
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.ConfigParser;
|
||||
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
* @see NacosDataSource
|
||||
*/
|
||||
public class NacosDataSourceFactoryBean implements FactoryBean<NacosDataSource> {
|
||||
|
||||
private String serverAddr;
|
||||
private String groupId;
|
||||
private String dataId;
|
||||
private ConfigParser configParser;
|
||||
|
||||
@Override
|
||||
public NacosDataSource getObject() throws Exception {
|
||||
return new NacosDataSource(serverAddr, groupId, dataId, configParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return NacosDataSource.class;
|
||||
}
|
||||
|
||||
public String getServerAddr() {
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
public void setServerAddr(String serverAddr) {
|
||||
this.serverAddr = serverAddr;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getDataId() {
|
||||
return dataId;
|
||||
}
|
||||
|
||||
public void setDataId(String dataId) {
|
||||
this.dataId = dataId;
|
||||
}
|
||||
|
||||
public ConfigParser getConfigParser() {
|
||||
return configParser;
|
||||
}
|
||||
|
||||
public void setConfigParser(ConfigParser configParser) {
|
||||
this.configParser = configParser;
|
||||
}
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
package org.springframework.cloud.alibaba.sentinel.datasource.factorybean;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.ConfigParser;
|
||||
import com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
* @see ZookeeperDataSource
|
||||
*/
|
||||
public class ZookeeperDataSourceFactoryBean implements FactoryBean<ZookeeperDataSource> {
|
||||
|
||||
private String serverAddr;
|
||||
|
||||
private String path;
|
||||
|
||||
private String groupId;
|
||||
private String dataId;
|
||||
|
||||
private ConfigParser configParser;
|
||||
|
||||
@Override
|
||||
public ZookeeperDataSource getObject() throws Exception {
|
||||
if (StringUtils.isNotEmpty(groupId) && StringUtils.isNotEmpty(dataId)) {
|
||||
// the path will be /{groupId}/{dataId}
|
||||
return new ZookeeperDataSource(serverAddr, groupId, dataId, configParser);
|
||||
}
|
||||
else {
|
||||
// using path directly
|
||||
return new ZookeeperDataSource(serverAddr, path, configParser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return ZookeeperDataSource.class;
|
||||
}
|
||||
|
||||
public String getServerAddr() {
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
public void setServerAddr(String serverAddr) {
|
||||
this.serverAddr = serverAddr;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getDataId() {
|
||||
return dataId;
|
||||
}
|
||||
|
||||
public void setDataId(String dataId) {
|
||||
this.dataId = dataId;
|
||||
}
|
||||
|
||||
public ConfigParser getConfigParser() {
|
||||
return configParser;
|
||||
}
|
||||
|
||||
public void setConfigParser(ConfigParser configParser) {
|
||||
this.configParser = configParser;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRule;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
|
||||
import org.springframework.cloud.alibaba.sentinel.SentinelProperties;
|
||||
|
||||
/**
|
||||
* Endpoint for Sentinel, contains ans properties and rules
|
||||
* @author xiaojing
|
||||
*/
|
||||
@Endpoint(id = "sentinel")
|
||||
public class SentinelEndpoint {
|
||||
|
||||
@Autowired
|
||||
private SentinelProperties sentinelProperties;
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> invoke() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
List<FlowRule> flowRules = FlowRuleManager.getRules();
|
||||
List<DegradeRule> degradeRules = DegradeRuleManager.getRules();
|
||||
List<SystemRule> systemRules = SystemRuleManager.getRules();
|
||||
result.put("properties", sentinelProperties);
|
||||
result.put("FlowRules", flowRules);
|
||||
result.put("DegradeRules", degradeRules);
|
||||
result.put("SystemRules", systemRules);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@@ -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 org.springframework.cloud.alibaba.sentinel.endpoint;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
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() {
|
||||
return new SentinelEndpoint();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* {@link PropertySources} Utilities
|
||||
*
|
||||
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
|
||||
*/
|
||||
public abstract class PropertySourcesUtils {
|
||||
|
||||
/**
|
||||
* Get Sub {@link Properties}
|
||||
*
|
||||
* @param propertySources {@link PropertySource} Iterable
|
||||
* @param prefix the prefix of property name
|
||||
* @return Map
|
||||
* @see Properties
|
||||
*/
|
||||
public static Map<String, Object> getSubProperties(Iterable<PropertySource<?>> propertySources, String prefix) {
|
||||
|
||||
Map<String, Object> subProperties = new LinkedHashMap<String, Object>();
|
||||
|
||||
String normalizedPrefix = normalizePrefix(prefix);
|
||||
|
||||
for (PropertySource<?> source : propertySources) {
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
for (String name : ((EnumerablePropertySource<?>) source).getPropertyNames()) {
|
||||
if (!subProperties.containsKey(name) && name.startsWith(normalizedPrefix)) {
|
||||
String subName = name.substring(normalizedPrefix.length());
|
||||
if (!subProperties.containsKey(subName)) { // take first one
|
||||
Object value = source.getProperty(name);
|
||||
subProperties.put(subName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return subProperties;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the prefix
|
||||
*
|
||||
* @param prefix the prefix
|
||||
* @return the prefix
|
||||
*/
|
||||
public static String normalizePrefix(String prefix) {
|
||||
return prefix.endsWith(".") ? prefix : prefix + ".";
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.cloud.sentinel.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"defaultValue": true,
|
||||
"description": "enable or disable sentinel auto configure."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.port",
|
||||
"type": "java.lang.String",
|
||||
"defaultValue": "8721",
|
||||
"description": "sentinel api port."
|
||||
},
|
||||
{
|
||||
"name": "spring.cloud.sentinel.dashboard",
|
||||
"type": "java.lang.String",
|
||||
"description": "sentinel dashboard address, won't try to connect dashboard when address is empty."
|
||||
},
|
||||
{
|
||||
"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.urlPatterns",
|
||||
"type": "java.util.List",
|
||||
"description": "URL pattern for Sentinel filter, default contains '/*'."
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
nacos = com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource
|
||||
file =com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource
|
||||
apollo = com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource
|
||||
zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource
|
@@ -0,0 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
|
||||
org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration
|
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
|
||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelProtectInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
/**
|
||||
* @author fangjian
|
||||
*/
|
||||
public class SentinelAutoConfigurationTests {
|
||||
|
||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(SentinelAutoConfiguration.class,
|
||||
SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class))
|
||||
.withPropertyValues("spring.cloud.sentinel.port=8888")
|
||||
.withPropertyValues("spring.cloud.sentinel.filter.order=123")
|
||||
.withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
|
||||
|
||||
@Test
|
||||
public void testFilter() {
|
||||
this.contextRunner.run(context -> {
|
||||
assertThat(context.getBean("servletRequestListener")
|
||||
.getClass() == FilterRegistrationBean.class).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeanPostProcessor() {
|
||||
this.contextRunner.run(context -> {
|
||||
assertThat(context.getBean("sentinelBeanPostProcessor")
|
||||
.getClass() == SentinelBeanPostProcessor.class).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperties() {
|
||||
this.contextRunner.run(context -> {
|
||||
SentinelProperties sentinelProperties = context
|
||||
.getBean(SentinelProperties.class);
|
||||
assertThat(sentinelProperties.getPort()).isEqualTo("8888");
|
||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().size())
|
||||
.isEqualTo(2);
|
||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0))
|
||||
.isEqualTo("/*");
|
||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1))
|
||||
.isEqualTo("/test");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestTemplate() {
|
||||
this.contextRunner.run(context -> {
|
||||
assertThat(context.getBeansOfType(RestTemplate.class).size()).isEqualTo(2);
|
||||
RestTemplate restTemplate = context.getBean("restTemplateWithBlockClass",
|
||||
RestTemplate.class);
|
||||
assertThat(restTemplate.getInterceptors().size()).isEqualTo(1);
|
||||
assertThat(restTemplate.getInterceptors().get(0).getClass())
|
||||
.isEqualTo(SentinelProtectInterceptor.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SentinelTestConfiguration {
|
||||
|
||||
@Bean
|
||||
@SentinelProtect
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SentinelProtect(blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")
|
||||
RestTemplate restTemplateWithBlockClass() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ExceptionUtil {
|
||||
public static void handleException(BlockException ex) {
|
||||
System.out.println("Oops: " + ex.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user