mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
SentinelRestTemplate block or fallback check in BeanPostProcessor, not runtime.
This commit is contained in:
parent
51209ca9c6
commit
4cbd4428fd
@ -23,6 +23,9 @@ public interface SentinelConstants {
|
|||||||
|
|
||||||
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
String PROPERTY_PREFIX = "spring.cloud.sentinel";
|
||||||
|
|
||||||
|
String BLOCK_TYPE = "block";
|
||||||
|
String FALLBACK_TYPE = "fallback";
|
||||||
|
|
||||||
// commercialization
|
// commercialization
|
||||||
|
|
||||||
String FLOW_DATASOURCE_NAME = "edas-flow";
|
String FLOW_DATASOURCE_NAME = "edas-flow";
|
||||||
|
@ -16,8 +16,12 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
@ -25,13 +29,20 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.cloud.alibaba.sentinel.SentinelConstants;
|
||||||
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.core.type.StandardMethodMetadata;
|
import org.springframework.core.type.StandardMethodMetadata;
|
||||||
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
|
import org.springframework.core.type.classreading.MethodMetadataReadingVisitor;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
|
* PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate
|
||||||
*
|
*
|
||||||
@ -41,6 +52,9 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
*/
|
*/
|
||||||
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(SentinelBeanPostProcessor.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
@ -60,10 +74,69 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces
|
|||||||
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
sentinelRestTemplate = beanDefinition.getResolvedFactoryMethod()
|
||||||
.getAnnotation(SentinelRestTemplate.class);
|
.getAnnotation(SentinelRestTemplate.class);
|
||||||
}
|
}
|
||||||
|
// check class and method validation
|
||||||
|
checkSentinelRestTemplate(sentinelRestTemplate, beanName);
|
||||||
cache.put(beanName, sentinelRestTemplate);
|
cache.put(beanName, sentinelRestTemplate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkSentinelRestTemplate(SentinelRestTemplate sentinelRestTemplate,
|
||||||
|
String beanName) {
|
||||||
|
checkBlock4RestTemplate(sentinelRestTemplate.blockHandlerClass(),
|
||||||
|
sentinelRestTemplate.blockHandler(), beanName,
|
||||||
|
SentinelConstants.BLOCK_TYPE);
|
||||||
|
checkBlock4RestTemplate(sentinelRestTemplate.fallbackClass(),
|
||||||
|
sentinelRestTemplate.fallback(), beanName,
|
||||||
|
SentinelConstants.FALLBACK_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBlock4RestTemplate(Class<?> blockClass, String blockMethod,
|
||||||
|
String beanName, String type) {
|
||||||
|
if (blockClass == void.class && StringUtils.isEmpty(blockMethod)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (blockClass != void.class && StringUtils.isEmpty(blockMethod)) {
|
||||||
|
logger.error(
|
||||||
|
"{} class property exists but {}"
|
||||||
|
+ " method property is not exists in bean[{}]",
|
||||||
|
type, type, beanName);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
else if (blockClass == void.class && !StringUtils.isEmpty(blockMethod)) {
|
||||||
|
logger.error(
|
||||||
|
"{} method property exists but {} class property is not exists in bean[{}]",
|
||||||
|
type, type, beanName);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
Class[] args = new Class[] { HttpRequest.class, byte[].class,
|
||||||
|
ClientHttpRequestExecution.class, BlockException.class };
|
||||||
|
Method foundMethod = ClassUtils.getStaticMethod(blockClass, blockMethod, args);
|
||||||
|
if (foundMethod == null) {
|
||||||
|
logger.error(
|
||||||
|
"{} method can not be found in bean[{}]. The right method signature is {}#{}{}, please check your class name, method name and arguments",
|
||||||
|
type, beanName, blockClass.getName(), blockMethod,
|
||||||
|
Arrays.toString(Arrays.stream(args)
|
||||||
|
.map(clazz -> clazz.getSimpleName()).toArray()));
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ClientHttpResponse.class.isAssignableFrom(foundMethod.getReturnType())) {
|
||||||
|
logger.error(
|
||||||
|
"{} method return value in bean[{}] is not ClientHttpResponse: {}#{}{}",
|
||||||
|
type, beanName, blockClass.getName(), blockMethod,
|
||||||
|
Arrays.toString(Arrays.stream(args)
|
||||||
|
.map(clazz -> clazz.getSimpleName()).toArray()));
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
if (type.equals(SentinelConstants.BLOCK_TYPE)) {
|
||||||
|
BlockClassRegistry.updateBlockHandlerFor(blockClass, blockMethod,
|
||||||
|
foundMethod);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BlockClassRegistry.updateFallbackFor(blockClass, blockMethod, foundMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
|
private boolean checkSentinelProtect(RootBeanDefinition beanDefinition,
|
||||||
Class<?> beanType) {
|
Class<?> beanType) {
|
||||||
return beanType == RestTemplate.class
|
return beanType == RestTemplate.class
|
||||||
|
@ -17,9 +17,9 @@
|
|||||||
package org.springframework.cloud.alibaba.sentinel.custom;
|
package org.springframework.cloud.alibaba.sentinel.custom;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -29,7 +29,6 @@ import org.springframework.http.HttpRequest;
|
|||||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
import org.springframework.http.client.ClientHttpResponse;
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.Entry;
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
import com.alibaba.csp.sentinel.SphU;
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
@ -37,12 +36,11 @@ import com.alibaba.csp.sentinel.Tracer;
|
|||||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interceptor using by SentinelRestTemplate
|
* Interceptor using by SentinelRestTemplate
|
||||||
*
|
*
|
||||||
* @author fangjian
|
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
|
||||||
*/
|
*/
|
||||||
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor {
|
||||||
|
|
||||||
@ -82,18 +80,7 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
|
|||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
return handleBlockException(request, body, execution, (BlockException) e);
|
||||||
return handleBlockException(request, body, execution,
|
|
||||||
(BlockException) e);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
if (ex instanceof IllegalStateException) {
|
|
||||||
throw (IllegalStateException) ex;
|
|
||||||
}
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"sentinel handle BlockException error: " + ex.getMessage(),
|
|
||||||
ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@ -109,84 +96,49 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
|
private ClientHttpResponse handleBlockException(HttpRequest request, byte[] body,
|
||||||
ClientHttpRequestExecution execution, BlockException ex) throws Exception {
|
ClientHttpRequestExecution execution, BlockException ex) {
|
||||||
Object[] args = new Object[] { request, body, execution, ex };
|
Object[] args = new Object[] { request, body, execution, ex };
|
||||||
// handle degrade
|
// handle degrade
|
||||||
if (isDegradeFailure(ex)) {
|
if (isDegradeFailure(ex)) {
|
||||||
Method method = extractFallbackMethod(sentinelRestTemplate.fallback(),
|
Method fallbackMethod = extractFallbackMethod(sentinelRestTemplate.fallback(),
|
||||||
sentinelRestTemplate.fallbackClass());
|
sentinelRestTemplate.fallbackClass());
|
||||||
if (method != null) {
|
if (fallbackMethod != null) {
|
||||||
return (ClientHttpResponse) method.invoke(null, args);
|
return methodInvoke(fallbackMethod, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new SentinelClientHttpResponse();
|
return new SentinelClientHttpResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle block
|
// handle flow
|
||||||
Method blockHandler = extractBlockHandlerMethod(
|
Method blockHandler = extractBlockHandlerMethod(
|
||||||
sentinelRestTemplate.blockHandler(),
|
sentinelRestTemplate.blockHandler(),
|
||||||
sentinelRestTemplate.blockHandlerClass());
|
sentinelRestTemplate.blockHandlerClass());
|
||||||
if (blockHandler != null) {
|
if (blockHandler != null) {
|
||||||
return (ClientHttpResponse) blockHandler.invoke(null, args);
|
return methodInvoke(blockHandler, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new SentinelClientHttpResponse();
|
return new SentinelClientHttpResponse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientHttpResponse methodInvoke(Method method, Object... args) {
|
||||||
|
try {
|
||||||
|
return (ClientHttpResponse) method.invoke(null, args);
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Method extractFallbackMethod(String fallback, Class<?> fallbackClass) {
|
private Method extractFallbackMethod(String fallback, Class<?> fallbackClass) {
|
||||||
if (StringUtil.isBlank(fallback) || fallbackClass == void.class) {
|
return BlockClassRegistry.lookupFallback(fallbackClass, fallback);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Method cachedMethod = BlockClassRegistry.lookupFallback(fallbackClass, fallback);
|
|
||||||
Class[] args = new Class[] { HttpRequest.class, byte[].class,
|
|
||||||
ClientHttpRequestExecution.class, BlockException.class };
|
|
||||||
if (cachedMethod == null) {
|
|
||||||
cachedMethod = ClassUtils.getStaticMethod(fallbackClass, fallback, args);
|
|
||||||
if (cachedMethod != null) {
|
|
||||||
if (!ClientHttpResponse.class
|
|
||||||
.isAssignableFrom(cachedMethod.getReturnType())) {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"the return type of method [%s] in class [%s] is not ClientHttpResponse in degrade",
|
|
||||||
cachedMethod.getName(), fallbackClass.getCanonicalName()));
|
|
||||||
}
|
|
||||||
BlockClassRegistry.updateFallbackFor(fallbackClass, fallback,
|
|
||||||
cachedMethod);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"Cannot find method [%s] in class [%s] with parameters %s in degrade",
|
|
||||||
fallback, fallbackClass.getCanonicalName(), Arrays.asList(args)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cachedMethod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
private Method extractBlockHandlerMethod(String block, Class<?> blockClass) {
|
||||||
if (StringUtil.isBlank(block) || blockClass == void.class) {
|
return BlockClassRegistry.lookupBlockHandler(blockClass, block);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Method cachedMethod = BlockClassRegistry.lookupBlockHandler(blockClass, block);
|
|
||||||
Class[] args = new Class[] { HttpRequest.class, byte[].class,
|
|
||||||
ClientHttpRequestExecution.class, BlockException.class };
|
|
||||||
if (cachedMethod == null) {
|
|
||||||
cachedMethod = ClassUtils.getStaticMethod(blockClass, block, args);
|
|
||||||
if (cachedMethod != null) {
|
|
||||||
if (!ClientHttpResponse.class
|
|
||||||
.isAssignableFrom(cachedMethod.getReturnType())) {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"the return type of method [%s] in class [%s] is not ClientHttpResponse in flow control",
|
|
||||||
cachedMethod.getName(), blockClass.getCanonicalName()));
|
|
||||||
}
|
|
||||||
BlockClassRegistry.updateBlockHandlerFor(blockClass, block, cachedMethod);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"Cannot find method [%s] in class [%s] with parameters %s in flow control",
|
|
||||||
block, blockClass.getCanonicalName(), Arrays.asList(args)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cachedMethod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDegradeFailure(BlockException ex) {
|
private boolean isDegradeFailure(BlockException ex) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user