mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
sentinel upgrade to 0.1.1 and add annotation support in RestTemplate with sentinel
This commit is contained in:
parent
9037c85de7
commit
8142f3f4d7
@ -16,7 +16,7 @@
|
|||||||
<description>Spring Cloud Alibaba Dependencies</description>
|
<description>Spring Cloud Alibaba Dependencies</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<sentinel.version>0.1.0</sentinel.version>
|
<sentinel.version>0.1.1</sentinel.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -41,6 +41,16 @@
|
|||||||
<artifactId>sentinel-transport-simple-http</artifactId>
|
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||||
<version>${sentinel.version}</version>
|
<version>${sentinel.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-annotation-aspectj</artifactId>
|
||||||
|
<version>${sentinel.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-dubbo-adapter</artifactId>
|
||||||
|
<version>${sentinel.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- Own dependencies autoconfigure -->
|
<!-- Own dependencies autoconfigure -->
|
||||||
|
@ -25,6 +25,16 @@
|
|||||||
<artifactId>sentinel-transport-simple-http</artifactId>
|
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-annotation-aspectj</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-dubbo-adapter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!--spring boot-->
|
<!--spring boot-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel;
|
package org.springframework.cloud.alibaba.sentinel;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -33,14 +34,13 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
|
||||||
import javax.servlet.Filter;
|
import com.alibaba.csp.sentinel.transport.config.TransportConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author xiaojing
|
* @author xiaojing
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnWebApplication
|
|
||||||
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
|
||||||
@EnableConfigurationProperties(SentinelProperties.class)
|
@EnableConfigurationProperties(SentinelProperties.class)
|
||||||
public class SentinelWebAutoConfiguration {
|
public class SentinelWebAutoConfiguration {
|
||||||
|
@ -14,17 +14,24 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.annotation;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author xiaojing
|
* @author fangjian
|
||||||
*/
|
*/
|
||||||
public class SentinelCustomAspectAutoConfiguration {
|
@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;
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SentinelAspect sentinelAspect() {
|
|
||||||
return new SentinelAspect();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Annotation to add Sentinel to custom method
|
|
||||||
* @author xiaojing
|
|
||||||
*/
|
|
||||||
@Target(ElementType.METHOD)
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface EnableSentinel {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return sentinel resource value
|
|
||||||
*/
|
|
||||||
String value();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Sentinel BlockException Handler name
|
|
||||||
*/
|
|
||||||
String handler() default "";
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author xiaojing
|
|
||||||
*/
|
|
||||||
public class HandlerUtil {
|
|
||||||
|
|
||||||
private static final ConcurrentHashMap<String, SentinelBlockHandler> map = new ConcurrentHashMap<>(
|
|
||||||
16);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* you should add your custom handler before use it
|
|
||||||
* @param name see {@link EnableSentinel#handler()}
|
|
||||||
* @param handler you custom handler
|
|
||||||
*/
|
|
||||||
public static void addHandler(String name, SentinelBlockHandler handler) {
|
|
||||||
map.put(name, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SentinelBlockHandler getHandler(String name) {
|
|
||||||
SentinelBlockHandler handler = map.get(name);
|
|
||||||
if (null == handler) {
|
|
||||||
throw new RuntimeException("cannot find handler name=<" + name
|
|
||||||
+ ",> did you forgot to invoke HandlerUtil.addHandler(name, handler) ?");
|
|
||||||
}
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
|
|
||||||
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 org.aspectj.lang.ProceedingJoinPoint;
|
|
||||||
import org.aspectj.lang.annotation.Around;
|
|
||||||
import org.aspectj.lang.annotation.Aspect;
|
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author xiaojing
|
|
||||||
*/
|
|
||||||
@Aspect
|
|
||||||
public class SentinelAspect {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SentinelAspect.class);
|
|
||||||
|
|
||||||
@Around("@annotation(org.springframework.cloud.alibaba.sentinel.custom.EnableSentinel)")
|
|
||||||
public Object customBlock(ProceedingJoinPoint pjp) throws Throwable {
|
|
||||||
SentinelEntry sentinelEntry = new SentinelEntry();
|
|
||||||
try {
|
|
||||||
beforeProceed(sentinelEntry, pjp);
|
|
||||||
return pjp.proceed();
|
|
||||||
}
|
|
||||||
catch (BlockException e) {
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
|
||||||
if (null != sentinelEntry.getHandler()
|
|
||||||
&& sentinelEntry.getHandler().length() > 0) {
|
|
||||||
return HandlerUtil.getHandler(sentinelEntry.getHandler()).handler(e);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
releaseContextResources(sentinelEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void beforeProceed(SentinelEntry sentinelEntry, ProceedingJoinPoint pjp)
|
|
||||||
throws BlockException {
|
|
||||||
Method method = getMethod(pjp);
|
|
||||||
|
|
||||||
int modifiers = method.getModifiers();
|
|
||||||
|
|
||||||
if (!Modifier.isPublic(modifiers) || "toString".equals(method.getName())
|
|
||||||
|| "hashCode".equals(method.getName())
|
|
||||||
|| "equals".equals(method.getName())
|
|
||||||
|| "finalize".equals(method.getName())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Annotation[] annotations = method.getDeclaredAnnotations();
|
|
||||||
for (Annotation annotation : annotations) {
|
|
||||||
if (annotation instanceof EnableSentinel) {
|
|
||||||
sentinelEntry.setKey(((EnableSentinel) annotation).value());
|
|
||||||
sentinelEntry.setHandler(((EnableSentinel) annotation).handler());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (null == sentinelEntry.getKey() || sentinelEntry.getKey().length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextUtil.enter(sentinelEntry.getKey());
|
|
||||||
sentinelEntry.setEntry(SphU.entry(sentinelEntry.getKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void releaseContextResources(SentinelEntry sentinelEntry) {
|
|
||||||
|
|
||||||
if (null == sentinelEntry.getEntry()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entry entry = sentinelEntry.getEntry();
|
|
||||||
entry.exit();
|
|
||||||
ContextUtil.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method getMethod(ProceedingJoinPoint joinPoint) {
|
|
||||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
||||||
return signature.getMethod();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiaojing
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SentinelAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public SentinelResourceAspect sentinelResourceAspect() {
|
||||||
|
return new SentinelResourceAspect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
@ConditionalOnClass(value = RestTemplate.class)
|
||||||
|
public SentinelBeanPostProcessor sentinelBeanPostProcessor() {
|
||||||
|
return new SentinelBeanPostProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sentinel Block Handler
|
|
||||||
* @author xiaojing
|
|
||||||
*/
|
|
||||||
public interface SentinelBlockHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* custom method to process BlockException
|
|
||||||
* @param e block exception when blocked by sentinel
|
|
||||||
* @return Object result processed by the handler
|
|
||||||
*/
|
|
||||||
Object handler(BlockException e);
|
|
||||||
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Entry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author xiaojing
|
|
||||||
*/
|
|
||||||
public class SentinelEntry {
|
|
||||||
|
|
||||||
private String key;
|
|
||||||
private String handler;
|
|
||||||
|
|
||||||
private Entry entry;
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(String key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHandler() {
|
|
||||||
return handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHandler(String handler) {
|
|
||||||
this.handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Entry getEntry() {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEntry(Entry entry) {
|
|
||||||
this.entry = entry;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
|
org.springframework.cloud.alibaba.sentinel.SentinelWebAutoConfiguration,\
|
||||||
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
|
org.springframework.cloud.alibaba.sentinel.endpoint.SentinelEndpointAutoConfiguration,\
|
||||||
org.springframework.cloud.alibaba.sentinel.custom.SentinelCustomAspectAutoConfiguration
|
org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration
|
||||||
|
@ -16,15 +16,21 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel;
|
package org.springframework.cloud.alibaba.sentinel;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAspect;
|
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
|
||||||
import org.springframework.cloud.alibaba.sentinel.custom.SentinelCustomAspectAutoConfiguration;
|
import org.springframework.cloud.alibaba.sentinel.custom.SentinelAutoConfiguration;
|
||||||
|
import org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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
|
* @author fangjian
|
||||||
@ -32,35 +38,76 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
public class SentinelAutoConfigurationTests {
|
public class SentinelAutoConfigurationTests {
|
||||||
|
|
||||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||||
.withConfiguration(
|
.withConfiguration(AutoConfigurations.of(SentinelAutoConfiguration.class,
|
||||||
AutoConfigurations.of(SentinelCustomAspectAutoConfiguration.class, SentinelWebAutoConfiguration.class))
|
SentinelWebAutoConfiguration.class, SentinelTestConfiguration.class))
|
||||||
.withPropertyValues("spring.cloud.sentinel.port=8888")
|
.withPropertyValues("spring.cloud.sentinel.port=8888")
|
||||||
.withPropertyValues("spring.cloud.sentinel.filter.order=123")
|
.withPropertyValues("spring.cloud.sentinel.filter.order=123")
|
||||||
.withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
|
.withPropertyValues("spring.cloud.sentinel.filter.urlPatterns=/*,/test");
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSentinelAspect() {
|
public void testFilter() {
|
||||||
this.contextRunner.run(context -> assertThat(context).hasSingleBean(SentinelAspect.class));
|
this.contextRunner.run(context -> {
|
||||||
|
assertThat(context.getBean("servletRequestListener")
|
||||||
|
.getClass() == FilterRegistrationBean.class).isTrue();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilter() {
|
public void testBeanPostProcessor() {
|
||||||
this.contextRunner.run(context -> {
|
this.contextRunner.run(context -> {
|
||||||
assertThat(context.getBean(
|
assertThat(context.getBean("sentinelBeanPostProcessor")
|
||||||
"servletRequestListener").getClass() == FilterRegistrationBean.class).isTrue();
|
.getClass() == SentinelBeanPostProcessor.class).isTrue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProperties() {
|
public void testProperties() {
|
||||||
this.contextRunner.run(context -> {
|
this.contextRunner.run(context -> {
|
||||||
SentinelProperties sentinelProperties = context.getBean(SentinelProperties.class);
|
SentinelProperties sentinelProperties = context
|
||||||
|
.getBean(SentinelProperties.class);
|
||||||
assertThat(sentinelProperties.getPort()).isEqualTo("8888");
|
assertThat(sentinelProperties.getPort()).isEqualTo("8888");
|
||||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().size()).isEqualTo(2);
|
assertThat(sentinelProperties.getFilter().getUrlPatterns().size())
|
||||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().get(0)).isEqualTo("/*");
|
.isEqualTo(2);
|
||||||
assertThat(sentinelProperties.getFilter().getUrlPatterns().get(1)).isEqualTo("/test");
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user