From 82578e1fdf07ee4694fde973820056bdb9ff69e1 Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Tue, 23 Apr 2019 23:17:03 +0800
Subject: [PATCH 01/29] Refactor and improve fallback support for
@SentinelResource annotation (#693)
- Refactor the semantics and logic for blockHandler and fallback
- Add `fallbackClass` support for fallback in global classes
- Add `defaultFallback` support
- Update test cases and demo
Signed-off-by: Eric Zhao
---
.../sentinel/annotation/SentinelResource.java | 21 ++
.../aop/controller/DemoController.java | 14 +-
.../annotation/aop/service/ExceptionUtil.java | 6 +
.../annotation/aop/service/TestService.java | 2 +
.../aop/service/TestServiceImpl.java | 25 ++-
.../sentinel-annotation-aspectj/README.md | 23 +-
.../AbstractSentinelAspectSupport.java | 208 ++++++++++++------
.../aspectj/ResourceMetadataRegistry.java | 16 +-
.../aspectj/SentinelResourceAspect.java | 12 +-
.../SentinelAnnotationIntegrationTest.java | 53 ++++-
.../integration/service/FooService.java | 24 +-
.../aspectj/integration/service/FooUtil.java | 6 +
12 files changed, 317 insertions(+), 93 deletions(-)
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
index e4f07a66a3..a0fa11d675 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java
@@ -60,6 +60,27 @@
*/
String fallback() default "";
+ /**
+ * The {@code defaultFallback} is used as the default universal fallback method.
+ * It should not accept any parameters, and the return type should be compatible
+ * with the original method.
+ *
+ * @return name of the default fallback method, empty by default
+ * @since 1.6.0
+ */
+ String defaultFallback() default "";
+
+ /**
+ * The {@code fallback} is located in the same class with the original method by default.
+ * However, if some methods share the same signature and intend to set the same fallback,
+ * then users can set the class where the fallback function exists. Note that the shared fallback method
+ * must be static.
+ *
+ * @return the class where the fallback method is located (only single class)
+ * @since 1.6.0
+ */
+ Class>[] fallbackClass() default {};
+
/**
* @return the list of exception classes to trace, {@link Throwable} by default
* @since 1.5.1
diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java
index 6288fb8118..d2ba5a8697 100644
--- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java
+++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java
@@ -19,6 +19,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -31,8 +33,16 @@ public class DemoController {
private TestService service;
@GetMapping("/foo")
- public String foo() throws Exception {
+ public String apiFoo(@RequestParam(required = false) Long t) throws Exception {
+ if (t == null) {
+ t = System.currentTimeMillis();
+ }
service.test();
- return service.hello(System.currentTimeMillis());
+ return service.hello(t);
+ }
+
+ @GetMapping("/baz/{name}")
+ public String apiBaz(@PathVariable("name") String name) {
+ return service.helloAnother(name);
}
}
diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java
index e589f85b2e..388563c3f9 100644
--- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java
+++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java
@@ -23,6 +23,12 @@
public final class ExceptionUtil {
public static void handleException(BlockException ex) {
+ // Handler method that handles BlockException when blocked.
+ // The method parameter list should match original method, with the last additional
+ // parameter with type BlockException. The return type should be same as the original method.
+ // The block handler method should be located in the same class with original method by default.
+ // If you want to use method in other classes, you can set the blockHandlerClass
+ // with corresponding Class (Note the method in other classes must be static).
System.out.println("Oops: " + ex.getClass().getCanonicalName());
}
}
diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java
index c8a2d1856e..0395212287 100644
--- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java
+++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java
@@ -23,4 +23,6 @@ public interface TestService {
void test();
String hello(long s);
+
+ String helloAnother(String name);
}
diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java
index ab1437e66f..9c793be7dc 100644
--- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java
+++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java
@@ -33,14 +33,35 @@ public void test() {
}
@Override
- @SentinelResource(value = "hello", blockHandler = "exceptionHandler")
+ @SentinelResource(value = "hello", fallback = "helloFallback")
public String hello(long s) {
+ if (s < 0) {
+ throw new IllegalArgumentException("invalid arg");
+ }
return String.format("Hello at %d", s);
}
- public String exceptionHandler(long s, BlockException ex) {
+ @Override
+ @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback",
+ exceptionsToIgnore = {IllegalStateException.class})
+ public String helloAnother(String name) {
+ if (name == null || "bad".equals(name)) {
+ throw new IllegalArgumentException("oops");
+ }
+ if ("foo".equals(name)) {
+ throw new IllegalStateException("oops");
+ }
+ return "Hello, " + name;
+ }
+
+ public String helloFallback(long s, Throwable ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
+
+ public String defaultFallback() {
+ System.out.println("Go to default fallback");
+ return "default_fallback";
+ }
}
diff --git a/sentinel-extension/sentinel-annotation-aspectj/README.md b/sentinel-extension/sentinel-annotation-aspectj/README.md
index 15e7e0fb32..00967d25e6 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/README.md
+++ b/sentinel-extension/sentinel-annotation-aspectj/README.md
@@ -9,26 +9,31 @@ The `@SentinelResource` annotation indicates a resource definition, including:
- `value`: Resource name, required (cannot be empty)
- `entryType`: Resource entry type (inbound or outbound), `EntryType.OUT` by default
-- `fallback`: Fallback method when degraded (optional). The fallback method should be located in the same class with original method. The signature of the fallback method should match the original method (parameter types and return type).
-- `blockHandler`: Handler method that handles `BlockException` when blocked. The signature should match original method, with the last additional parameter type `BlockException`. The block handler method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `blockHandlerClass` with corresponding `Class` (Note the method in other classes must be *static*).
-- `exceptionsToTrace`: List of business exception classes to trace and record (since 1.5.1).
+- `fallback` (refactored since 1.6.0): Fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). The fallback method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The method signature requirement:
+ - The return type should match the origin method;
+ - The parameter list should match the origin method, and an additional `Throwable` parameter can be provided to get the actual exception.
+- `defaultFallback` (since 1.6.0): The default fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). Its intended to be a universal common fallback method. The method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The default fallback method signature requirement:
+ - The return type should match the origin method;
+ - parameter list should be empty, and an additional `Throwable` parameter can be provided to get the actual exception.
+- `blockHandler`: Handler method that handles `BlockException` when blocked. The parameter list of the method should match original method, with the last additional parameter type `BlockException`. The return type should be same as the original method. The `blockHandler` method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `blockHandlerClass` with corresponding `Class` (Note the method in other classes must be *static*).
+- `exceptionsToIgnore` (since 1.6.0): List of business exception classes that should not be traced and caught in fallback.
+- `exceptionsToTrace` (since 1.5.1): List of business exception classes to trace and record. In most cases, using `exceptionsToIgnore` is better. If both `exceptionsToTrace` and `exceptionsToIgnore` are present, only `exceptionsToIgnore` will be activated.
For example:
```java
-@SentinelResource(value = "abc", fallback = "doFallback", blockHandler = "handleException")
+@SentinelResource(value = "abc", fallback = "doFallback")
public String doSomething(long i) {
return "Hello " + i;
}
-public String doFallback(long i) {
+public String doFallback(long i, Throwable t) {
// Return fallback value.
- return "Oops, degraded";
+ return "fallback";
}
-public String handleException(long i, BlockException ex) {
- // Handle the block exception here.
- return null;
+public String defaultFallback(Throwable t) {
+ return "default_fallback";
}
```
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java
index dce92b8e49..13ef92d6c2 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java
@@ -19,7 +19,6 @@
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.util.MethodUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
@@ -37,103 +36,127 @@
*/
public abstract class AbstractSentinelAspectSupport {
- protected String getResourceName(String resourceName, /*@NonNull*/ Method method) {
- // If resource name is present in annotation, use this value.
- if (StringUtil.isNotBlank(resourceName)) {
- return resourceName;
- }
- // Parse name of target method.
- return MethodUtil.resolveMethodName(method);
- }
-
- protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)
- throws Exception {
- return handleBlockException(pjp, annotation.fallback(), annotation.blockHandler(),
- annotation.blockHandlerClass(), ex);
- }
-
- protected Object handleBlockException(ProceedingJoinPoint pjp, String fallback, String blockHandler,
- Class>[] blockHandlerClass, BlockException ex) throws Exception {
- // Execute fallback for degrading if configured.
- Object[] originArgs = pjp.getArgs();
- if (isDegradeFailure(ex)) {
- Method method = extractFallbackMethod(pjp, fallback);
- if (method != null) {
- return method.invoke(pjp.getTarget(), originArgs);
- }
- }
- // Execute block handler if configured.
- Method blockHandlerMethod = extractBlockHandlerMethod(pjp, blockHandler, blockHandlerClass);
- if (blockHandlerMethod != null) {
- // Construct args.
- Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
- args[args.length - 1] = ex;
- if (isStatic(blockHandlerMethod)) {
- return blockHandlerMethod.invoke(null, args);
- }
- return blockHandlerMethod.invoke(pjp.getTarget(), args);
- }
- // If no block handler is present, then directly throw the exception.
- throw ex;
+ protected void traceException(Throwable ex) {
+ Tracer.trace(ex);
}
protected void traceException(Throwable ex, SentinelResource annotation) {
Class extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
// The ignore list will be checked first.
- if (exceptionsToIgnore.length > 0 && isIgnoredException(ex, exceptionsToIgnore)) {
+ if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
return;
}
- if (isTracedException(ex, annotation.exceptionsToTrace())) {
- Tracer.trace(ex);
+ if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
+ traceException(ex);
}
}
/**
- * Check whether the exception is in tracked list of exception classes.
+ * Check whether the exception is in provided list of exception classes.
*
- * @param ex
- * provided throwable
- * @param exceptionsToTrace
- * list of exceptions to trace
- * @return true if it should be traced, otherwise false
+ * @param ex provided throwable
+ * @param exceptions list of exceptions
+ * @return true if it is in the list, otherwise false
*/
- private boolean isTracedException(Throwable ex, Class extends Throwable>[] exceptionsToTrace) {
- if (exceptionsToTrace == null) {
+ protected boolean exceptionBelongsTo(Throwable ex, Class extends Throwable>[] exceptions) {
+ if (exceptions == null) {
return false;
}
- for (Class extends Throwable> exceptionToTrace : exceptionsToTrace) {
- if (exceptionToTrace.isAssignableFrom(ex.getClass())) {
+ for (Class extends Throwable> exceptionClass : exceptions) {
+ if (exceptionClass.isAssignableFrom(ex.getClass())) {
return true;
}
}
return false;
}
- private boolean isIgnoredException(Throwable ex, Class extends Throwable>[] exceptionsToIgnore) {
- if (exceptionsToIgnore == null) {
- return false;
+ protected String getResourceName(String resourceName, /*@NonNull*/ Method method) {
+ // If resource name is present in annotation, use this value.
+ if (StringUtil.isNotBlank(resourceName)) {
+ return resourceName;
}
- for (Class extends Throwable> exceptionToIgnore : exceptionsToIgnore) {
- if (exceptionToIgnore.isAssignableFrom(ex.getClass())) {
- return true;
+ // Parse name of target method.
+ return MethodUtil.resolveMethodName(method);
+ }
+
+ protected Object handleFallback(ProceedingJoinPoint pjp, SentinelResource annotation, Throwable ex)
+ throws Throwable {
+ return handleFallback(pjp, annotation.fallback(), annotation.defaultFallback(), annotation.fallbackClass(), ex);
+ }
+
+ protected Object handleFallback(ProceedingJoinPoint pjp, String fallback, String defaultFallback,
+ Class>[] fallbackClass, Throwable ex) throws Throwable {
+ Object[] originArgs = pjp.getArgs();
+
+ // Execute fallback function if configured.
+ Method fallbackMethod = extractFallbackMethod(pjp, fallback, fallbackClass);
+ if (fallbackMethod != null) {
+ // Construct args.
+ int paramCount = fallbackMethod.getParameterTypes().length;
+ Object[] args;
+ if (paramCount == originArgs.length) {
+ args = originArgs;
+ } else {
+ args = Arrays.copyOf(originArgs, originArgs.length + 1);
+ args[args.length - 1] = ex;
}
+ if (isStatic(fallbackMethod)) {
+ return fallbackMethod.invoke(null, args);
+ }
+ return fallbackMethod.invoke(pjp.getTarget(), args);
}
- return false;
+ // If fallback is absent, we'll try the defaultFallback if provided.
+ return handleDefaultFallback(pjp, defaultFallback, fallbackClass, ex);
}
- private boolean isDegradeFailure(/*@NonNull*/ BlockException ex) {
- return ex instanceof DegradeException;
+ protected Object handleDefaultFallback(ProceedingJoinPoint pjp, String defaultFallback,
+ Class>[] fallbackClass, Throwable ex) throws Throwable {
+ // Execute the default fallback function if configured.
+ Method fallbackMethod = extractDefaultFallbackMethod(pjp, defaultFallback, fallbackClass);
+ if (fallbackMethod != null) {
+ // Construct args.
+ Object[] args = fallbackMethod.getParameterTypes().length == 0 ? new Object[0] : new Object[] {ex};
+ if (isStatic(fallbackMethod)) {
+ return fallbackMethod.invoke(null, args);
+ }
+ return fallbackMethod.invoke(pjp.getTarget(), args);
+ }
+
+ // If no any fallback is present, then directly throw the exception.
+ throw ex;
}
- private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName) {
+ protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)
+ throws Throwable {
+
+ // Execute block handler if configured.
+ Method blockHandlerMethod = extractBlockHandlerMethod(pjp, annotation.blockHandler(),
+ annotation.blockHandlerClass());
+ if (blockHandlerMethod != null) {
+ Object[] originArgs = pjp.getArgs();
+ // Construct args.
+ Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);
+ args[args.length - 1] = ex;
+ if (isStatic(blockHandlerMethod)) {
+ return blockHandlerMethod.invoke(null, args);
+ }
+ return blockHandlerMethod.invoke(pjp.getTarget(), args);
+ }
+
+ // If no block handler is present, then go to fallback.
+ return handleFallback(pjp, annotation, ex);
+ }
+
+ private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName, Class>[] locationClass) {
if (StringUtil.isBlank(fallbackName)) {
return null;
}
- Class> clazz = pjp.getTarget().getClass();
+ boolean mustStatic = locationClass != null && locationClass.length >= 1;
+ Class> clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass();
MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName);
if (m == null) {
// First time, resolve the fallback.
- Method method = resolveFallbackInternal(pjp, fallbackName);
+ Method method = resolveFallbackInternal(pjp, fallbackName, clazz, mustStatic);
// Cache the method instance.
ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method);
return method;
@@ -144,10 +167,53 @@ private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackNam
return m.getMethod();
}
- private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name) {
+ private Method extractDefaultFallbackMethod(ProceedingJoinPoint pjp, String defaultFallback,
+ Class>[] locationClass) {
+ if (StringUtil.isBlank(defaultFallback)) {
+ return null;
+ }
+ boolean mustStatic = locationClass != null && locationClass.length >= 1;
+ Class> clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass();
+
+ MethodWrapper m = ResourceMetadataRegistry.lookupDefaultFallback(clazz, defaultFallback);
+ if (m == null) {
+ // First time, resolve the default fallback.
+ Class> originReturnType = resolveMethod(pjp).getReturnType();
+ // Default fallback allows two kinds of parameter list.
+ // One is empty parameter list.
+ Class>[] defaultParamTypes = new Class>[0];
+ // The other is a single parameter {@link Throwable} to get relevant exception info.
+ Class>[] paramTypeWithException = new Class>[] {Throwable.class};
+ // We first find the default fallback with empty parameter list.
+ Method method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, defaultParamTypes);
+ // If default fallback with empty params is absent, we then try to find the other one.
+ if (method == null) {
+ method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, paramTypeWithException);
+ }
+ // Cache the method instance.
+ ResourceMetadataRegistry.updateDefaultFallbackFor(clazz, defaultFallback, method);
+ return method;
+ }
+ if (!m.isPresent()) {
+ return null;
+ }
+ return m.getMethod();
+ }
+
+ private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name, Class> clazz,
+ boolean mustStatic) {
Method originMethod = resolveMethod(pjp);
- Class>[] parameterTypes = originMethod.getParameterTypes();
- return findMethod(false, pjp.getTarget().getClass(), name, originMethod.getReturnType(), parameterTypes);
+ // Fallback function allows two kinds of parameter list.
+ Class>[] defaultParamTypes = originMethod.getParameterTypes();
+ Class>[] paramTypesWithException = Arrays.copyOf(defaultParamTypes, defaultParamTypes.length + 1);
+ paramTypesWithException[paramTypesWithException.length - 1] = Throwable.class;
+ // We first find the fallback matching the signature of origin method.
+ Method method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), defaultParamTypes);
+ // If fallback matching the origin method is absent, we then try to find the other one.
+ if (method == null) {
+ method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), paramTypesWithException);
+ }
+ return method;
}
private Method extractBlockHandlerMethod(ProceedingJoinPoint pjp, String name, Class>[] locationClass) {
@@ -195,8 +261,8 @@ private Method findMethod(boolean mustStatic, Class> clazz, String name, Class
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (name.equals(method.getName()) && checkStatic(mustStatic, method)
- && returnType.isAssignableFrom(method.getReturnType())
- && Arrays.equals(parameterTypes, method.getParameterTypes())) {
+ && returnType.isAssignableFrom(method.getReturnType())
+ && Arrays.equals(parameterTypes, method.getParameterTypes())) {
RecordLog.info("Resolved method [{0}] in class [{1}]", name, clazz.getCanonicalName());
return method;
@@ -209,7 +275,7 @@ private Method findMethod(boolean mustStatic, Class> clazz, String name, Class
} else {
String methodType = mustStatic ? " static" : "";
RecordLog.warn("Cannot find{0} method [{1}] in class [{2}] with parameters {3}",
- methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes));
+ methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes));
return null;
}
}
@@ -223,7 +289,7 @@ protected Method resolveMethod(ProceedingJoinPoint joinPoint) {
Class> targetClass = joinPoint.getTarget().getClass();
Method method = getDeclaredMethodFor(targetClass, signature.getName(),
- signature.getMethod().getParameterTypes());
+ signature.getMethod().getParameterTypes());
if (method == null) {
throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName());
}
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java
index 25bb1bfd6c..c4988130a8 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java
@@ -28,13 +28,18 @@
*/
final class ResourceMetadataRegistry {
- private static final Map FALLBACK_MAP = new ConcurrentHashMap();
- private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap();
+ private static final Map FALLBACK_MAP = new ConcurrentHashMap<>();
+ private static final Map DEFAULT_FALLBACK_MAP = new ConcurrentHashMap<>();
+ private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap<>();
static MethodWrapper lookupFallback(Class> clazz, String name) {
return FALLBACK_MAP.get(getKey(clazz, name));
}
+ static MethodWrapper lookupDefaultFallback(Class> clazz, String name) {
+ return DEFAULT_FALLBACK_MAP.get(getKey(clazz, name));
+ }
+
static MethodWrapper lookupBlockHandler(Class> clazz, String name) {
return BLOCK_HANDLER_MAP.get(getKey(clazz, name));
}
@@ -46,6 +51,13 @@ static void updateFallbackFor(Class> clazz, String name, Method method) {
FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method));
}
+ static void updateDefaultFallbackFor(Class> clazz, String name, Method method) {
+ if (clazz == null || StringUtil.isBlank(name)) {
+ throw new IllegalArgumentException("Bad argument");
+ }
+ DEFAULT_FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method));
+ }
+
static void updateBlockHandlerFor(Class> clazz, String name, Method method) {
if (clazz == null || StringUtil.isBlank(name)) {
throw new IllegalArgumentException("Bad argument");
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java
index 39f9a99676..0f5e9a9703 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java
@@ -58,7 +58,17 @@ public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwab
} catch (BlockException ex) {
return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
- traceException(ex, annotation);
+ Class extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
+ // The ignore list will be checked first.
+ if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
+ throw ex;
+ }
+ if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
+ traceException(ex, annotation);
+ return handleFallback(pjp, annotation, ex);
+ }
+
+ // No fallback function can handle the exception, so throw it out.
throw ex;
} finally {
if (entry != null) {
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java
index 986c064ccc..bf127cc932 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java
@@ -99,6 +99,56 @@ public void testAnnotationExceptionsToIgnore() {
}
}
+ @Test
+ public void testFallbackWithNoParams() throws Exception {
+ assertThat(fooService.fooWithFallback(1)).isEqualTo("Hello for 1");
+ String resourceName = "apiFooWithFallback";
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
+ assertThat(cn).isNotNull();
+ assertThat(cn.passQps()).isPositive();
+
+ // Fallback should be ignored for this.
+ try {
+ fooService.fooWithFallback(5758);
+ fail("should not reach here");
+ } catch (IllegalAccessException e) {
+ assertThat(cn.exceptionQps()).isZero();
+ }
+
+ // Fallback should take effect.
+ assertThat(fooService.fooWithFallback(5763)).isEqualTo("eee...");
+ assertThat(cn.exceptionQps()).isPositive();
+ assertThat(cn.blockQps()).isZero();
+
+ FlowRuleManager.loadRules(Collections.singletonList(
+ new FlowRule(resourceName).setCount(0)
+ ));
+ // Fallback should not take effect for BlockException, as blockHandler is configured.
+ assertThat(fooService.fooWithFallback(2221)).isEqualTo("Oops, 2221");
+ assertThat(cn.blockQps()).isPositive();
+ }
+
+ @Test
+ public void testDefaultFallbackWithSingleParam() {
+ assertThat(fooService.anotherFoo(1)).isEqualTo("Hello for 1");
+ String resourceName = "apiAnotherFooWithDefaultFallback";
+ ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName);
+ assertThat(cn).isNotNull();
+ assertThat(cn.passQps()).isPositive();
+
+ // Default fallback should take effect.
+ assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT);
+ assertThat(cn.exceptionQps()).isPositive();
+ assertThat(cn.blockQps()).isZero();
+
+ FlowRuleManager.loadRules(Collections.singletonList(
+ new FlowRule(resourceName).setCount(0)
+ ));
+ // Default fallback should also take effect for BlockException.
+ assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT);
+ assertThat(cn.blockQps()).isPositive();
+ }
+
@Test
public void testNormalBlockHandlerAndFallback() throws Exception {
assertThat(fooService.foo(1)).isEqualTo("Hello for 1");
@@ -107,9 +157,6 @@ public void testNormalBlockHandlerAndFallback() throws Exception {
assertThat(cn).isNotNull();
assertThat(cn.passQps()).isPositive();
- // Test for fallback.
- assertThat(fooService.foo(9527)).isEqualTo("eee...");
-
// Test for biz exception.
try {
fooService.foo(5758);
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java
index 25bb98beb4..a8dfe1bc3f 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java
@@ -29,12 +29,21 @@
@Service
public class FooService {
- @SentinelResource(value = "apiFoo", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc",
+ @SentinelResource(value = "apiFoo", blockHandler = "fooBlockHandler",
exceptionsToTrace = {IllegalArgumentException.class})
public String foo(int i) throws Exception {
- if (i == 9527) {
- throw new DegradeException("ggg");
+ if (i == 5758) {
+ throw new IllegalAccessException();
}
+ if (i == 5763) {
+ throw new IllegalArgumentException();
+ }
+ return "Hello for " + i;
+ }
+
+ @SentinelResource(value = "apiFooWithFallback", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc",
+ exceptionsToTrace = {IllegalArgumentException.class})
+ public String fooWithFallback(int i) throws Exception {
if (i == 5758) {
throw new IllegalAccessException();
}
@@ -44,6 +53,15 @@ public String foo(int i) throws Exception {
return "Hello for " + i;
}
+ @SentinelResource(value = "apiAnotherFooWithDefaultFallback", defaultFallback = "globalDefaultFallback",
+ fallbackClass = {FooUtil.class})
+ public String anotherFoo(int i) {
+ if (i == 5758) {
+ throw new IllegalArgumentException("oops");
+ }
+ return "Hello for " + i;
+ }
+
@SentinelResource(blockHandler = "globalBlockHandler", blockHandlerClass = FooUtil.class)
public int random() {
return ThreadLocalRandom.current().nextInt(0, 30000);
diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java
index 52bcb17ba4..2af079d30e 100644
--- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java
+++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java
@@ -23,9 +23,15 @@
public class FooUtil {
public static final int BLOCK_FLAG = 88888;
+ public static final String FALLBACK_DEFAULT_RESULT = "fallback";
public static int globalBlockHandler(BlockException ex) {
System.out.println("Oops: " + ex.getClass().getSimpleName());
return BLOCK_FLAG;
}
+
+ public static String globalDefaultFallback(Throwable t) {
+ System.out.println("Fallback caught: " + t.getClass().getSimpleName());
+ return FALLBACK_DEFAULT_RESULT;
+ }
}
From 4819675f8497e6a2bb3222f0fc97b3ed1eb2a2d4 Mon Sep 17 00:00:00 2001
From: kexianjun
Date: Wed, 24 Apr 2019 09:23:49 +0800
Subject: [PATCH 02/29] dashboard: fix typo in SystemController (#700)
---
.../csp/sentinel/dashboard/controller/SystemController.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java
index 815efcafec..8e99718bc3 100755
--- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java
+++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java
@@ -78,7 +78,7 @@ Result> queryMachineRules(HttpServletRequest request, Str
}
}
- private int countNotNullAndNotNegtive(Number... values) {
+ private int countNotNullAndNotNegative(Number... values) {
int notNullCount = 0;
for (int i = 0; i < values.length; i++) {
if (values[i] != null && values[i].doubleValue() >= 0) {
@@ -103,7 +103,7 @@ Result> add(HttpServletRequest request,
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
- int notNullCount = countNotNullAndNotNegtive(avgLoad, avgRt, maxThread, qps);
+ int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps);
if (notNullCount != 1) {
return Result.ofFail(-1, "only one of [avgLoad, avgRt, maxThread, qps] "
+ "value must be set >= 0, but " + notNullCount + " values get");
From 540428bae07251d63b34188037de83f50180626e Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Wed, 24 Apr 2019 09:40:35 +0800
Subject: [PATCH 03/29] Support getting and updating gateway rules and API
groups via transport command
Signed-off-by: Eric Zhao
---
.../pom.xml | 5 ++
.../gateway/common/api/ApiDefinition.java | 2 +
...tewayApiDefinitionGroupCommandHandler.java | 36 +++++++++++
.../command/GetGatewayRuleCommandHandler.java | 36 +++++++++++
...tewayApiDefinitionGroupCommandHandler.java | 61 +++++++++++++++++++
.../UpdateGatewayRuleCommandHandler.java | 61 +++++++++++++++++++
...libaba.csp.sentinel.command.CommandHandler | 4 ++
7 files changed, 205 insertions(+)
create mode 100644 sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java
create mode 100644 sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java
create mode 100644 sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
create mode 100644 sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
create mode 100755 sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
index acda0b1dba..219e0c017c 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml
@@ -26,6 +26,11 @@
com.alibaba.cspsentinel-parameter-flow-control
+
+ com.alibaba.csp
+ sentinel-transport-common
+ provided
+ junit
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java
index 6f3a9782ea..4540eebe86 100644
--- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java
@@ -19,6 +19,8 @@
import java.util.Set;
/**
+ * A group of HTTP API patterns.
+ *
* @author Eric Zhao
* @since 1.6.0
*/
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java
new file mode 100644
index 0000000000..b6403f086e
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.common.command;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
+import com.alibaba.csp.sentinel.command.CommandHandler;
+import com.alibaba.csp.sentinel.command.CommandRequest;
+import com.alibaba.csp.sentinel.command.CommandResponse;
+import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.fastjson.JSON;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+@CommandMapping(name = "gateway/getApiDefinitions", desc = "Fetch all customized gateway API groups")
+public class GetGatewayApiDefinitionGroupCommandHandler implements CommandHandler {
+
+ @Override
+ public CommandResponse handle(CommandRequest request) {
+ return CommandResponse.ofSuccess(JSON.toJSONString(GatewayApiDefinitionManager.getApiDefinitions()));
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java
new file mode 100644
index 0000000000..e7b8e9c578
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.common.command;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
+import com.alibaba.csp.sentinel.command.CommandHandler;
+import com.alibaba.csp.sentinel.command.CommandRequest;
+import com.alibaba.csp.sentinel.command.CommandResponse;
+import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.fastjson.JSON;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+@CommandMapping(name = "gateway/getRules", desc = "Fetch all gateway rules")
+public class GetGatewayRuleCommandHandler implements CommandHandler {
+
+ @Override
+ public CommandResponse handle(CommandRequest request) {
+ return CommandResponse.ofSuccess(JSON.toJSONString(GatewayRuleManager.getRules()));
+ }
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
new file mode 100644
index 0000000000..963561f82a
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.common.command;
+
+import java.net.URLDecoder;
+import java.util.HashSet;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
+import com.alibaba.csp.sentinel.command.CommandHandler;
+import com.alibaba.csp.sentinel.command.CommandRequest;
+import com.alibaba.csp.sentinel.command.CommandResponse;
+import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.fastjson.JSONArray;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+@CommandMapping(name = "gateway/updateApiDefinitions", desc = "")
+public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler {
+
+ @Override
+ public CommandResponse handle(CommandRequest request) {
+ String data = request.getParam("data");
+ if (StringUtil.isBlank(data)) {
+ return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
+ }
+ try {
+ data = URLDecoder.decode(data, "utf-8");
+ } catch (Exception e) {
+ RecordLog.info("Decode gateway API definition data error", e);
+ return CommandResponse.ofFailure(e, "decode gateway API definition data error");
+ }
+
+ RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {0}", data);
+
+ String result = SUCCESS_MSG;
+ List apiDefinitions = JSONArray.parseArray(data, ApiDefinition.class);
+ GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<>(apiDefinitions));
+ return CommandResponse.ofSuccess(result);
+ }
+
+ private static final String SUCCESS_MSG = "success";
+}
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
new file mode 100644
index 0000000000..46e14209b4
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.common.command;
+
+import java.net.URLDecoder;
+import java.util.HashSet;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
+import com.alibaba.csp.sentinel.command.CommandHandler;
+import com.alibaba.csp.sentinel.command.CommandRequest;
+import com.alibaba.csp.sentinel.command.CommandResponse;
+import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.fastjson.JSONArray;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+@CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
+public class UpdateGatewayRuleCommandHandler implements CommandHandler {
+
+ @Override
+ public CommandResponse handle(CommandRequest request) {
+ String data = request.getParam("data");
+ if (StringUtil.isBlank(data)) {
+ return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
+ }
+ try {
+ data = URLDecoder.decode(data, "utf-8");
+ } catch (Exception e) {
+ RecordLog.info("Decode gateway rule data error", e);
+ return CommandResponse.ofFailure(e, "decode gateway rule data error");
+ }
+
+ RecordLog.info(String.format("[API Server] Receiving rule change (type: gateway rule): %s", data));
+
+ String result = SUCCESS_MSG;
+ List flowRules = JSONArray.parseArray(data, GatewayFlowRule.class);
+ GatewayRuleManager.loadRules(new HashSet<>(flowRules));
+ return CommandResponse.ofSuccess(result);
+ }
+
+ private static final String SUCCESS_MSG = "success";
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler
new file mode 100755
index 0000000000..824408905b
--- /dev/null
+++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler
@@ -0,0 +1,4 @@
+com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayApiDefinitionGroupCommandHandler
+com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayRuleCommandHandler
+com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayApiDefinitionGroupCommandHandler
+com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayRuleCommandHandler
\ No newline at end of file
From 1baac7783db76e6087a46c57a6612b3e78bad20b Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Tue, 23 Apr 2019 18:46:58 +0800
Subject: [PATCH 04/29] Refactor and improve Sentinel Zuul Adapter
- Now the adapter is based-on sentinel-api-gateway-adapter-common and supports both proxyId and customized APIs
Signed-off-by: Eric Zhao
---
.../sentinel-zuul-adapter/README.md | 85 ++++++----
.../sentinel-zuul-adapter/pom.xml | 19 +++
.../zuul/RequestContextItemParser.java | 47 ++++++
.../api/ZuulApiDefinitionChangeObserver.java | 33 ++++
.../api/ZuulGatewayApiMatcherManager.java | 69 ++++++++
.../api/matcher/RequestContextApiMatcher.java | 72 ++++++++
.../api/route/PrefixRoutePathMatcher.java | 55 ++++++
.../zuul/api/route/RegexRoutePathMatcher.java | 49 ++++++
.../zuul/api/route/ZuulRouteMatchers.java | 55 ++++++
.../callback}/DefaultRequestOriginParser.java | 3 +-
.../zuul/callback}/RequestOriginParser.java | 2 +-
.../callback/ZuulGatewayCallbackManager.java | 38 +++++
.../zuul/constants/ZuulConstant.java | 13 +-
.../zuul/fallback/BlockResponse.java | 7 +-
.../DefaultBlockFallbackProvider.java | 4 +-
.../fallback/ZuulBlockFallbackManager.java | 7 +-
.../fallback/ZuulBlockFallbackProvider.java | 2 +-
.../zuul/filters/SentinelEntryUtils.java | 66 ++++++++
.../zuul/filters/SentinelZuulErrorFilter.java | 75 +++++++++
.../zuul/filters/SentinelZuulPostFilter.java | 66 ++++++++
.../zuul/filters/SentinelZuulPreFilter.java | 159 ++++++++++++++++++
.../zuul/fallback/DefaultUrlCleaner.java | 27 ---
.../adapter/zuul/fallback/UrlCleaner.java | 31 ----
.../zuul/filters/AbstractSentinelFilter.java | 46 -----
.../zuul/filters/SentinelErrorFilter.java | 81 ---------
.../zuul/filters/SentinelPostFilter.java | 60 -------
.../zuul/filters/SentinelPreFilter.java | 112 ------------
.../properties/SentinelZuulProperties.java | 80 ---------
.../adapter/zuul/util/FilterUtil.java | 157 -----------------
...way.common.api.ApiDefinitionChangeObserver | 1 +
.../ZuulBlockFallbackManagerTest.java | 6 +-
.../ZuulBlockFallbackProviderTest.java | 6 +-
.../filters/SentinelZuulErrorFilterTest.java} | 25 ++-
.../filters/SentinelZuulPostFilterTest.java} | 17 +-
.../filters/SentinelZuulPreFilterTest.java | 61 +++++++
.../zuul/filters/SentinelPreFilterTest.java | 106 ------------
36 files changed, 967 insertions(+), 775 deletions(-)
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{zuul/fallback => gateway/zuul/callback}/DefaultRequestOriginParser.java (79%)
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{zuul/fallback => gateway/zuul/callback}/RequestOriginParser.java (94%)
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/constants/ZuulConstant.java (80%)
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/BlockResponse.java (93%)
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/DefaultBlockFallbackProvider.java (85%)
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/ZuulBlockFallbackManager.java (88%)
rename sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/ZuulBlockFallbackProvider.java (95%)
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
delete mode 100755 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java
delete mode 100755 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java
delete mode 100755 sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver
rename sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/ZuulBlockFallbackManagerTest.java (95%)
rename sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/{ => gateway}/zuul/fallback/ZuulBlockFallbackProviderTest.java (94%)
rename sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/{zuul/filters/SentinelErrorFilterTest.java => gateway/zuul/filters/SentinelZuulErrorFilterTest.java} (51%)
rename sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/{zuul/filters/SentinelPostFilterTest.java => gateway/zuul/filters/SentinelZuulPostFilterTest.java} (55%)
create mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java
delete mode 100644 sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java
diff --git a/sentinel-adapter/sentinel-zuul-adapter/README.md b/sentinel-adapter/sentinel-zuul-adapter/README.md
index f86b440674..9814a948bc 100755
--- a/sentinel-adapter/sentinel-zuul-adapter/README.md
+++ b/sentinel-adapter/sentinel-zuul-adapter/README.md
@@ -1,6 +1,7 @@
# Sentinel Zuul Adapter
-Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow control for Zuul gateway service.
+Sentinel Zuul Adapter provides **route level** and **customized API level**
+flow control for Zuul API Gateway.
> *Note*: this adapter only support Zuul 1.x.
@@ -18,54 +19,68 @@ Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow c
2. Register filters
+For Spring Cloud Zuul users, we only need to inject the three filters in Spring configuration class like this:
+
+```java
+@Configuration
+public class ZuulConfig {
+
+ @Bean
+ public ZuulFilter sentinelZuulPreFilter() {
+ // We can provider the filter order here.
+ return new SentinelZuulPreFilter(10000);
+ }
+
+ @Bean
+ public ZuulFilter sentinelZuulPostFilter() {
+ return new SentinelZuulPostFilter(1000);
+ }
+
+ @Bean
+ public ZuulFilter sentinelZuulErrorFilter() {
+ return new SentinelZuulErrorFilter(-1);
+ }
+}
+```
+
+For original Zuul users:
+
```java
-// get registry
+// Get filter registry
final FilterRegistry r = FilterRegistry.instance();
-// this is property config. set filter enable
-SentinelZuulProperties properties = new SentinelZuulProperties();
-properties.setEnabled(true);
-// set url cleaner, here use default
-DefaultUrlCleaner defaultUrlCleaner = new DefaultUrlCleaner();
-// set origin parser. here use default
-DefaultRequestOriginParser defaultRequestOriginParser = new DefaultRequestOriginParser();
-
-// register filters. you must register all three filters.
-SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, defaultUrlCleaner, defaultRequestOriginParser);
-r.put("sentinelPreFilter", sentinelPreFilter);
-SentinelPostFilter postFilter = new SentinelPostFilter(properties);
-r.put("sentinelPostFilter", postFilter);
-SentinelErrorFilter errorFilter = new SentinelErrorFilter(properties);
-r.put("sentinelErrorFilter", errorFilter);
+
+// We need to register all three filters.
+SentinelZuulPreFilter sentinelPreFilter = new SentinelZuulPreFilter();
+r.put("sentinelZuulPreFilter", sentinelPreFilter);
+SentinelZuulPostFilter postFilter = new SentinelZuulPostFilter();
+r.put("sentinelZuulPostFilter", postFilter);
+SentinelZuulErrorFilter errorFilter = new SentinelZuulErrorFilter();
+r.put("sentinelZuulErrorFilter", errorFilter);
```
## How it works
-As Zuul run as per thread per connection block model, we add filters around `route Filter` to trace sentinel statistics.
+As Zuul run as per thread per connection block model, we add filters around route filter to trace Sentinel statistics.
-- `SentinelPreFilter`: Get an entry of resource, the first order is **ServiceId** (the key in RequestContext is `serviceId`, this can set in own custom filter), then **API Path**.
-- `SentinelPostFilter`: When success response, exit entry.
-- `SentinelPreFilter`: When an `Exception` caught, trace the exception and exit context.
+- `SentinelZuulPreFilter`: This pre-filter will regard all proxy ID (`proxy` in `RequestContext`) and all customized API as resources. When a `BlockException` caught, the filter will try to find a fallback to execute.
+- `SentinelZuulPostFilter`: When the response has no exception caught, the post filter will complete the entries.
+- `SentinelZuulPreFilter`: When an exception is caught, the filter will trace the exception and complete the entries.
-The order of filters can be changed in property.
+The order of filters can be changed via the constructor.
The invocation chain resembles this:
```bash
-EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
--EntranceNode: coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
---coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
----/coke/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
--EntranceNode: sentinel_default_context(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
--EntranceNode: book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
---book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
----/book/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
+-EntranceNode: sentinel_gateway_context$$route$$another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:8 1mb:1 1mt:9)
+--another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:1 1mt:5)
+--another_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:0 1mt:4)
+-EntranceNode: sentinel_gateway_context$$route$$my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:6 1mb:0 1mt:6)
+--my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2)
+--some_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2)
```
-- `book` and `coke` are serviceId.
-- `/book/coke` is api path, the real API path is `/coke`.
-
## Integration with Sentinel Dashboard
1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
@@ -76,7 +91,7 @@ EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
You can implement `SentinelFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown.
The default fallback provider is `DefaultBlockFallbackProvider`.
-By default fallback route is `ServiveId + URI PATH`, example `/book/coke`, first `book` is serviceId, `/coke` is URI PATH, so that both can be needed.
+By default fallback route is proxy ID (or customized API name).
Here is an example:
@@ -90,7 +105,7 @@ public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {
// you can define root as service level
@Override
public String getRoute() {
- return "/coke/coke";
+ return "my-route";
}
@Override
diff --git a/sentinel-adapter/sentinel-zuul-adapter/pom.xml b/sentinel-adapter/sentinel-zuul-adapter/pom.xml
index 41228d5274..68f67b8df6 100755
--- a/sentinel-adapter/sentinel-zuul-adapter/pom.xml
+++ b/sentinel-adapter/sentinel-zuul-adapter/pom.xml
@@ -21,6 +21,11 @@
com.alibaba.cspsentinel-core
+
+ com.alibaba.csp
+ sentinel-api-gateway-adapter-common
+
+
javax.servletjavax.servlet-api
@@ -39,6 +44,20 @@
+
+
+ org.springframework
+ spring-core
+ 4.3.20.RELEASE
+ provided
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-zuul
+ 1.4.6.RELEASE
+ test
+ junit
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java
new file mode 100644
index 0000000000..c1e081e8b1
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser;
+
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class RequestContextItemParser implements RequestItemParser {
+
+ @Override
+ public String getPath(RequestContext requestContext) {
+ return requestContext.getRequest().getServletPath();
+ }
+
+ @Override
+ public String getRemoteAddress(RequestContext requestContext) {
+ return requestContext.getRequest().getRemoteAddr();
+ }
+
+ @Override
+ public String getHeader(RequestContext requestContext, String headerKey) {
+ return requestContext.getRequest().getHeader(headerKey);
+ }
+
+ @Override
+ public String getUrlParam(RequestContext requestContext, String paramName) {
+ return requestContext.getRequest().getParameter(paramName);
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java
new file mode 100644
index 0000000000..a53c9a4e0b
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api;
+
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver {
+
+ @Override
+ public void onChange(Set apiDefinitions) {
+ ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions);
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java
new file mode 100644
index 0000000000..b9ba7bc595
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class ZuulGatewayApiMatcherManager {
+
+ private static final Map API_MATCHER_MAP = new ConcurrentHashMap<>();
+
+ public static Map getApiMatcherMap() {
+ return Collections.unmodifiableMap(API_MATCHER_MAP);
+ }
+
+ public static RequestContextApiMatcher getMatcher(final String apiName) {
+ if (apiName == null) {
+ return null;
+ }
+ return API_MATCHER_MAP.get(apiName);
+ }
+
+ public static Set getApiDefinitionSet() {
+ Set set = new HashSet<>();
+ for (RequestContextApiMatcher matcher : API_MATCHER_MAP.values()) {
+ set.add(matcher.getApiDefinition());
+ }
+ return set;
+ }
+
+ static synchronized void loadApiDefinitions(/*@Valid*/ Set definitions) {
+ if (definitions == null || definitions.isEmpty()) {
+ API_MATCHER_MAP.clear();
+ return;
+ }
+ for (ApiDefinition definition : definitions) {
+ addApiDefinition(definition);
+ }
+ }
+
+ static void addApiDefinition(ApiDefinition definition) {
+ API_MATCHER_MAP.put(definition.getApiName(), new RequestContextApiMatcher(definition));
+ }
+
+ private ZuulGatewayApiMatcherManager() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java
new file mode 100644
index 0000000000..90718db194
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher;
+
+import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
+import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route.ZuulRouteMatchers;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class RequestContextApiMatcher extends AbstractApiMatcher {
+
+ public RequestContextApiMatcher(ApiDefinition apiDefinition) {
+ super(apiDefinition);
+ }
+
+ @Override
+ protected void initializeMatchers() {
+ if (apiDefinition.getPredicateItems() != null) {
+ for (ApiPredicateItem item : apiDefinition.getPredicateItems()) {
+ Predicate predicate = fromApiPredicate(item);
+ if (predicate != null) {
+ matchers.add(predicate);
+ }
+ }
+ }
+ }
+
+ private Predicate fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) {
+ if (item instanceof ApiPathPredicateItem) {
+ return fromApiPathPredicate((ApiPathPredicateItem)item);
+ }
+ return null;
+ }
+
+ private Predicate fromApiPathPredicate(/*@Valid*/ ApiPathPredicateItem item) {
+ String pattern = item.getPattern();
+ if (StringUtil.isBlank(pattern)) {
+ return null;
+ }
+ switch (item.getMatchStrategy()) {
+ case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX:
+ return ZuulRouteMatchers.regexPath(pattern);
+ case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX:
+ return ZuulRouteMatchers.antPath(pattern);
+ default:
+ return ZuulRouteMatchers.exactPath(pattern);
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
new file mode 100644
index 0000000000..21e636e589
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+import com.netflix.zuul.context.RequestContext;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class PrefixRoutePathMatcher implements Predicate {
+
+ private final String pattern;
+
+ private final PathMatcher pathMatcher;
+ private final boolean canMatch;
+
+ public PrefixRoutePathMatcher(String pattern) {
+ AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
+ this.pattern = pattern;
+ this.pathMatcher = new AntPathMatcher();
+ this.canMatch = pathMatcher.isPattern(pattern);
+ }
+
+ @Override
+ public boolean test(RequestContext context) {
+ String path = context.getRequest().getServletPath();
+ if (canMatch) {
+ return pathMatcher.match(pattern, path);
+ }
+ return false;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
new file mode 100644
index 0000000000..daf1310ec3
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route;
+
+import java.util.regex.Pattern;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public class RegexRoutePathMatcher implements Predicate {
+
+ private final String pattern;
+ private final Pattern regex;
+
+ public RegexRoutePathMatcher(String pattern) {
+ AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
+ this.pattern = pattern;
+ this.regex = Pattern.compile(pattern);
+ }
+
+ @Override
+ public boolean test(RequestContext context) {
+ String path = context.getRequest().getServletPath();
+ return regex.matcher(path).matches();
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java
new file mode 100644
index 0000000000..970c30e1b2
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route;
+
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class ZuulRouteMatchers {
+
+ public static Predicate all() {
+ return new Predicate() {
+ @Override
+ public boolean test(RequestContext requestContext) {
+ return true;
+ }
+ };
+ }
+
+ public static Predicate antPath(String pathPattern) {
+ return new PrefixRoutePathMatcher(pathPattern);
+ }
+
+ public static Predicate exactPath(final String path) {
+ return new Predicate() {
+ @Override
+ public boolean test(RequestContext exchange) {
+ return exchange.getRequest().getServletPath().equals(path);
+ }
+ };
+ }
+
+ public static Predicate regexPath(String pathPattern) {
+ return new RegexRoutePathMatcher(pathPattern);
+ }
+
+ private ZuulRouteMatchers() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java
similarity index 79%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java
index 45dba4d705..e2d078436f 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java
@@ -1,4 +1,4 @@
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback;
import javax.servlet.http.HttpServletRequest;
@@ -6,6 +6,7 @@
* @author tiger
*/
public class DefaultRequestOriginParser implements RequestOriginParser {
+
@Override
public String parseOrigin(HttpServletRequest request) {
return "";
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java
similarity index 94%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java
index 20f6d744a2..caacd0a0ef 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback;
import javax.servlet.http.HttpServletRequest;
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java
new file mode 100644
index 0000000000..03bfa246cd
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback;
+
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+public final class ZuulGatewayCallbackManager {
+
+ private static volatile RequestOriginParser originParser = new DefaultRequestOriginParser();
+
+ public static RequestOriginParser getOriginParser() {
+ return originParser;
+ }
+
+ public static void setOriginParser(RequestOriginParser originParser) {
+ AssertUtil.notNull(originParser, "originParser cannot be null");
+ ZuulGatewayCallbackManager.originParser = originParser;
+ }
+
+ private ZuulGatewayCallbackManager() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java
similarity index 80%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java
index 244fbf4ae8..3042805321 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.constants;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.constants;
import com.netflix.zuul.ZuulFilter;
@@ -27,6 +27,10 @@ public class ZuulConstant {
* Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer.
*/
public static final String SERVICE_ID_KEY = "serviceId";
+ /**
+ * Zuul {@link com.netflix.zuul.context.RequestContext} key for proxying (route ID).
+ */
+ public static final String PROXY_ID_KEY = "proxy";
/**
* {@link ZuulFilter#filterType()} error type.
@@ -58,5 +62,12 @@ public class ZuulConstant {
*/
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context";
+ /**
+ * Zuul context key for keeping Sentinel entries.
+ *
+ * @since 1.6.0
+ */
+ public static final String ZUUL_CTX_SENTINEL_ENTRIES_KEY = "_sentinel_entries";
+
private ZuulConstant(){}
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java
similarity index 93%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java
index 6fd617fd10..435850fce6 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
/**
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException}
@@ -22,7 +22,12 @@
* @author tiger
*/
public class BlockResponse {
+
+ /**
+ * HTTP status code.
+ */
private int code;
+
private String message;
private String route;
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java
similarity index 85%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java
index 432db872b1..1bd9403475 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
-import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
@@ -33,7 +32,6 @@ public String getRoute() {
@Override
public BlockResponse fallbackResponse(String route, Throwable cause) {
- RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
if (cause instanceof BlockException) {
return new BlockResponse(429, "Sentinel block exception", route);
} else {
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java
similarity index 88%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java
index 2760acff6b..187017623e 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
import java.util.HashMap;
import java.util.Map;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+
/**
* This provide fall back class manager.
*
@@ -26,7 +28,7 @@
*/
public class ZuulBlockFallbackManager {
- private static Map fallbackProviderCache = new HashMap();
+ private static Map fallbackProviderCache = new HashMap<>();
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
@@ -34,6 +36,7 @@ public class ZuulBlockFallbackManager {
* Register special provider for different route.
*/
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
+ AssertUtil.notNull(provider, "fallback provider cannot be null");
String route = provider.getRoute();
if ("*".equals(route) || route == null) {
defaultFallbackProvider = provider;
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java
similarity index 95%
rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java
index e6b3acb0c8..766220eb9a 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
/**
* This interface is compatible for different spring cloud version.
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java
new file mode 100644
index 0000000000..8c93a1b7ec
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2019 Alibaba Group Holding Ltd.
+ *
+ * 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
+
+import java.util.Deque;
+
+import com.alibaba.csp.sentinel.AsyncEntry;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+
+import com.netflix.zuul.context.RequestContext;
+
+/**
+ * @author Eric Zhao
+ * @since 1.6.0
+ */
+final class SentinelEntryUtils {
+
+ @SuppressWarnings("unchecked")
+ static void tryExitFromCurrentContext() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
+ Deque asyncEntries = (Deque) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+ AsyncEntry entry;
+ while (!asyncEntries.isEmpty()) {
+ entry = asyncEntries.pop();
+ entry.exit();
+ }
+ ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+ }
+
+ ContextUtil.exit();
+ }
+
+ @SuppressWarnings("unchecked")
+ static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) {
+ Deque asyncEntries = (Deque) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+ AsyncEntry entry;
+ while (!asyncEntries.isEmpty()) {
+ entry = asyncEntries.pop();
+ Tracer.traceEntry(t, entry);
+ entry.exit();
+ }
+ ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
+ }
+ ContextUtil.exit();
+ }
+
+ private SentinelEntryUtils() {}
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java
new file mode 100644
index 0000000000..2e1d856bbe
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
+
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+
+/**
+ * This filter track routing exception and exit entry;
+ *
+ * @author tiger
+ * @author Eric Zhao
+ */
+public class SentinelZuulErrorFilter extends ZuulFilter {
+
+ private final int order;
+
+ public SentinelZuulErrorFilter() {
+ this(-1);
+ }
+
+ public SentinelZuulErrorFilter(int order) {
+ this.order = order;
+ }
+
+ @Override
+ public String filterType() {
+ return ZuulConstant.ERROR_TYPE;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ return ctx.getThrowable() != null;
+ }
+
+ @Override
+ public int filterOrder() {
+ return order;
+ }
+
+ @Override
+ public Object run() throws ZuulException {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ Throwable throwable = ctx.getThrowable();
+ if (throwable != null) {
+ if (!BlockException.isBlockException(throwable)) {
+ // Trace exception for each entry and exit entries in order.
+ // The entries can be retrieved from the request context.
+ SentinelEntryUtils.tryTraceExceptionThenExitFromCurrentContext(throwable);
+ RecordLog.info("[SentinelZuulErrorFilter] Trace error cause", throwable.getCause());
+ }
+ }
+ return null;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java
new file mode 100644
index 0000000000..03cdddbfee
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
+
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.exception.ZuulException;
+
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER;
+
+/**
+ * This filter will mark complete and exit {@link com.alibaba.csp.sentinel.Entry}.
+ *
+ * @author tiger
+ * @author Eric Zhao
+ */
+public class SentinelZuulPostFilter extends ZuulFilter {
+
+ private final int order;
+
+ public SentinelZuulPostFilter() {
+ this(SEND_RESPONSE_FILTER_ORDER);
+ }
+
+ public SentinelZuulPostFilter(int order) {
+ this.order = order;
+ }
+
+ @Override
+ public String filterType() {
+ return ZuulConstant.POST_TYPE;
+ }
+
+ @Override
+ public int filterOrder() {
+ return order;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ @Override
+ public Object run() throws ZuulException {
+ // Exit the entries in order.
+ // The entries can be retrieved from the request context.
+ SentinelEntryUtils.tryExitFromCurrentContext();
+ return null;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
new file mode 100644
index 0000000000..da6a8b8260
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.AsyncEntry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
+import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.RequestContextItemParser;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulGatewayApiMatcherManager;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.ZuulGatewayCallbackManager;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager;
+import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
+
+/**
+ * This pre-filter will regard all {@code proxyId} and all customized API as resources.
+ * When a BlockException caught, the filter will try to find a fallback to execute.
+ *
+ * @author tiger
+ * @author Eric Zhao
+ */
+public class SentinelZuulPreFilter extends ZuulFilter {
+
+ private final int order;
+
+ private final GatewayParamParser paramParser = new GatewayParamParser<>(
+ new RequestContextItemParser());
+
+ public SentinelZuulPreFilter() {
+ this(10000);
+ }
+
+ public SentinelZuulPreFilter(int order) {
+ this.order = order;
+ }
+
+ @Override
+ public String filterType() {
+ return ZuulConstant.PRE_TYPE;
+ }
+
+ /**
+ * This run before route filter so we can get more accurate RT time.
+ */
+ @Override
+ public int filterOrder() {
+ return order;
+ }
+
+ @Override
+ public boolean shouldFilter() {
+ return true;
+ }
+
+ private void doSentinelEntry(String resourceName, final int resType, RequestContext requestContext,
+ Deque asyncEntries) throws BlockException {
+ Object[] params = paramParser.parseParameterFor(resourceName, requestContext,
+ new Predicate() {
+ @Override
+ public boolean test(GatewayFlowRule r) {
+ return r.getResourceMode() == resType;
+ }
+ });
+ AsyncEntry entry = SphU.asyncEntry(resourceName, EntryType.IN, 1, params);
+ asyncEntries.push(entry);
+ }
+
+ @Override
+ public Object run() throws ZuulException {
+ RequestContext ctx = RequestContext.getCurrentContext();
+ String origin = parseOrigin(ctx.getRequest());
+ String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY);
+
+ Deque asyncEntries = new ArrayDeque<>();
+ String fallBackRoute = routeId;
+ try {
+ if (StringUtil.isNotBlank(routeId)) {
+ ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin);
+ doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, ctx, asyncEntries);
+ }
+
+ Set matchingApis = pickMatchingApiDefinitions(ctx);
+ if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) {
+ ContextUtil.enter(ZuulConstant.ZUUL_DEFAULT_CONTEXT, origin);
+ }
+ for (String apiName : matchingApis) {
+ fallBackRoute = apiName;
+ doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, asyncEntries);
+ }
+ } catch (BlockException ex) {
+ ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(
+ fallBackRoute);
+ BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex);
+ // Prevent routing from running
+ ctx.setRouteHost(null);
+ ctx.set(ZuulConstant.SERVICE_ID_KEY, null);
+
+ // Set fallback response.
+ ctx.setResponseBody(blockResponse.toString());
+ ctx.setResponseStatusCode(blockResponse.getCode());
+ } finally {
+ // We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly.
+ // So here the entries will be carried in the request context.
+ if (!asyncEntries.isEmpty()) {
+ ctx.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, asyncEntries);
+ }
+ }
+ return null;
+ }
+
+ private String parseOrigin(HttpServletRequest request) {
+ return ZuulGatewayCallbackManager.getOriginParser().parseOrigin(request);
+ }
+
+ private Set pickMatchingApiDefinitions(RequestContext requestContext) {
+ Set apis = new HashSet<>();
+ for (RequestContextApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) {
+ if (matcher.test(requestContext)) {
+ apis.add(matcher.getApiName());
+ }
+ }
+ return apis;
+ }
+}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java
deleted file mode 100755
index 097808204e..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
-
-/***
- * @author tiger
- */
-public class DefaultUrlCleaner implements UrlCleaner {
-
- @Override
- public String clean(String originUrl) {
- return originUrl;
- }
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java
deleted file mode 100755
index d6b9a33b46..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
-
-/***
- * @author tiger
- */
-public interface UrlCleaner {
-
- /***
- *
Process the url. Some path variables should be handled and unified.
- *
e.g. collect_item_relation--10200012121-.html will be converted to collect_item_relation.html
- *
- * @param originUrl original url
- * @return processed url
- */
- String clean(String originUrl);
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java
deleted file mode 100644
index 0becdf53d3..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
-
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
-import com.alibaba.csp.sentinel.util.AssertUtil;
-import com.netflix.zuul.ZuulFilter;
-
-/**
- * Abstract class for sentinel filters.
- *
- * @author tiger
- */
-public abstract class AbstractSentinelFilter extends ZuulFilter {
-
- private final SentinelZuulProperties sentinelZuulProperties;
-
- public SentinelZuulProperties getSentinelZuulProperties() {
- return sentinelZuulProperties;
- }
-
- public AbstractSentinelFilter(SentinelZuulProperties sentinelZuulProperties) {
- AssertUtil.notNull(sentinelZuulProperties,"SentinelZuulProperties can not be null");
- this.sentinelZuulProperties = sentinelZuulProperties;
- }
-
- @Override
- public boolean shouldFilter() {
- return sentinelZuulProperties.isEnabled();
- }
-
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java
deleted file mode 100644
index dc192dba5a..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
-
-import com.alibaba.csp.sentinel.Tracer;
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
-import com.alibaba.csp.sentinel.context.ContextUtil;
-import com.alibaba.csp.sentinel.log.RecordLog;
-import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.netflix.zuul.context.RequestContext;
-import com.netflix.zuul.exception.ZuulException;
-
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE;
-
-/**
- * This filter track routing exception and exit entry;
- *
- * @author tiger
- */
-public class SentinelErrorFilter extends AbstractSentinelFilter {
-
- public SentinelErrorFilter(SentinelZuulProperties sentinelZuulProperties) {
- super(sentinelZuulProperties);
- }
-
- @Override
- public String filterType() {
- return ERROR_TYPE;
- }
-
- @Override
- public boolean shouldFilter() {
- RequestContext ctx = RequestContext.getCurrentContext();
- return getSentinelZuulProperties().isEnabled() && ctx.getThrowable() != null;
- }
-
- @Override
- public int filterOrder() {
- return getSentinelZuulProperties().getOrder().getError();
- }
-
- /**
- * Trace not {@link BlockException} ex.
- * While loop will exit all entries,
- * Case serviceId and URL entry twice in {@link SentinelPreFilter}.
- * The ContextUtil.getContext().getCurEntry() will exit from inner to outer.
- */
- @Override
- public Object run() throws ZuulException {
- try {
- RequestContext ctx = RequestContext.getCurrentContext();
- Throwable throwable = ctx.getThrowable();
- if (throwable != null) {
- if (!BlockException.isBlockException(throwable)) {
- Tracer.trace(throwable.getCause());
- RecordLog.info("[Sentinel Error Filter] Trace cause", throwable.getCause());
- }
- }
- } finally {
- while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) {
- ContextUtil.getContext().getCurEntry().exit();
- }
- ContextUtil.exit();
- }
- return null;
- }
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java
deleted file mode 100644
index 9159489c1c..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
-
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
-import com.alibaba.csp.sentinel.context.ContextUtil;
-import com.alibaba.csp.sentinel.log.RecordLog;
-import com.netflix.zuul.exception.ZuulException;
-
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE;
-
-/**
- * This filter do success routing RT statistic and exit {@link com.alibaba.csp.sentinel.Entry}
- *
- * @author tiger
- */
-public class SentinelPostFilter extends AbstractSentinelFilter {
-
- public SentinelPostFilter(SentinelZuulProperties sentinelZuulProperties) {
- super(sentinelZuulProperties);
- }
-
- @Override
- public String filterType() {
- return POST_TYPE;
- }
-
- @Override
- public int filterOrder() {
- return getSentinelZuulProperties().getOrder().getPost();
- }
-
- /**
- * While loop will exit all entries,
- * Case serviceId and URL entry twice in {@link SentinelPreFilter}.
- * The ContextUtil.getContext().getCurEntry() will exit from inner to outer.
- */
- @Override
- public Object run() throws ZuulException {
- while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) {
- ContextUtil.getContext().getCurEntry().exit();
- }
- ContextUtil.exit();
- return null;
- }
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java
deleted file mode 100644
index 7ef2c787a2..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
-
-import com.alibaba.csp.sentinel.EntryType;
-import com.alibaba.csp.sentinel.SphU;
-import com.alibaba.csp.sentinel.adapter.zuul.fallback.*;
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
-import com.alibaba.csp.sentinel.adapter.zuul.util.FilterUtil;
-import com.alibaba.csp.sentinel.context.ContextUtil;
-import com.alibaba.csp.sentinel.log.RecordLog;
-import com.alibaba.csp.sentinel.slots.block.BlockException;
-import com.alibaba.csp.sentinel.util.AssertUtil;
-import com.alibaba.csp.sentinel.util.StringUtil;
-import com.netflix.zuul.context.RequestContext;
-import com.netflix.zuul.exception.ZuulException;
-
-import javax.servlet.http.HttpServletRequest;
-
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE;
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY;
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ZUUL_DEFAULT_CONTEXT;
-
-/**
- * This pre filter get an entry of resource,the first order is ServiceId, then API Path.
- * When get a BlockException run fallback logic.
- *
- * @author tiger
- */
-public class SentinelPreFilter extends AbstractSentinelFilter {
-
- private final UrlCleaner urlCleaner;
-
- private final RequestOriginParser requestOriginParser;
-
- public SentinelPreFilter(SentinelZuulProperties sentinelZuulProperties,
- UrlCleaner urlCleaner,
- RequestOriginParser requestOriginParser) {
- super(sentinelZuulProperties);
- AssertUtil.notNull(urlCleaner, "UrlCleaner can not be null");
- AssertUtil.notNull(requestOriginParser, "RequestOriginParser can not be null");
- this.urlCleaner = urlCleaner;
- this.requestOriginParser = requestOriginParser;
- }
-
- @Override
- public String filterType() {
- return PRE_TYPE;
- }
-
- /**
- * This run before route filter so we can get more accurate RT time.
- */
- @Override
- public int filterOrder() {
- return getSentinelZuulProperties().getOrder().getPre();
- }
-
- @Override
- public Object run() throws ZuulException {
- RequestContext ctx = RequestContext.getCurrentContext();
- String origin = parseOrigin(ctx.getRequest());
- String serviceTarget = (String) ctx.get(SERVICE_ID_KEY);
- // When serviceId blocked first get the service level fallback provider.
- String fallBackRoute = serviceTarget;
- try {
- if (StringUtil.isNotEmpty(serviceTarget)) {
- RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter ServiceId: %s", origin, serviceTarget));
- ContextUtil.enter(serviceTarget, origin);
- SphU.entry(serviceTarget, EntryType.IN);
- } else {
- RecordLog.info("[Sentinel Pre Filter] ServiceId is empty");
- ContextUtil.enter(ZUUL_DEFAULT_CONTEXT, origin);
- }
- String uriTarget = FilterUtil.filterTarget(ctx.getRequest());
- // Clean and unify the URL.
- // For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
- // the amount of context and resources will exceed the threshold.
- uriTarget = urlCleaner.clean(uriTarget);
- fallBackRoute = uriTarget;
- RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter Uri Path: %s", origin, uriTarget));
- SphU.entry(uriTarget, EntryType.IN);
- } catch (BlockException ex) {
- RecordLog.warn(String.format("[Sentinel Pre Filter] Block Exception when Origin: %s enter fall back route: %s", origin, fallBackRoute), ex);
- ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(fallBackRoute);
- BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex);
- // prevent routing from running
- ctx.setRouteHost(null);
- ctx.set(SERVICE_ID_KEY, null);
- ctx.setResponseBody(blockResponse.toString());
- }
- return null;
- }
-
- private String parseOrigin(HttpServletRequest request) {
- return requestOriginParser.parseOrigin(request);
- }
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java
deleted file mode 100644
index 8e7681ed14..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.properties;
-
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER;
-
-/**
- * Sentinel Spring Cloud Zuul AutoConfiguration property.
- *
- * @author tiger
- */
-public class SentinelZuulProperties {
-
- private boolean enabled = false;
-
- private Order order = new Order();
-
- public static class Order {
-
- private int post = SEND_RESPONSE_FILTER_ORDER - 10;
-
- private int pre = 10000;
-
- private int error = -1;
-
- public int getPost() {
- return post;
- }
-
- public void setPost(int post) {
- this.post = post;
- }
-
- public int getPre() {
- return pre;
- }
-
- public void setPre(int pre) {
- this.pre = pre;
- }
-
- public int getError() {
- return error;
- }
-
- public void setError(int error) {
- this.error = error;
- }
- }
-
- public boolean isEnabled() {
- return enabled;
- }
-
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- }
-
- public Order getOrder() {
- return order;
- }
-
- public void setOrder(Order order) {
- this.order = order;
- }
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java
deleted file mode 100755
index cc00cceea8..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.alibaba.csp.sentinel.adapter.zuul.util;
-
-import com.alibaba.csp.sentinel.util.StringUtil;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Util class for web servlet filter.
- * This is same as servlet adapter util class
- *
- * @author tiger
- */
-public final class FilterUtil {
-
- public static String filterTarget(HttpServletRequest request) {
- String pathInfo = getResourcePath(request);
- if (!pathInfo.startsWith("/")) {
- pathInfo = "/" + pathInfo;
- }
-
- if ("/".equals(pathInfo)) {
- return pathInfo;
- }
-
- // Note: pathInfo should be converted to camelCase style.
- int lastSlashIndex = pathInfo.lastIndexOf("/");
-
- if (lastSlashIndex >= 0) {
- pathInfo = pathInfo.substring(0, lastSlashIndex) + "/"
- + StringUtil.trim(pathInfo.substring(lastSlashIndex + 1));
- } else {
- pathInfo = "/" + StringUtil.trim(pathInfo);
- }
-
- return pathInfo;
- }
-
- private static String getResourcePath(HttpServletRequest request) {
- String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false);
- String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0);
-
- return servletPath + pathInfo;
- }
-
- private static String normalizeAbsolutePath(String path, boolean removeTrailingSlash) throws IllegalStateException {
- return normalizePath(path, true, false, removeTrailingSlash);
- }
-
- private static String normalizePath(String path, boolean forceAbsolute, boolean forceRelative,
- boolean removeTrailingSlash) throws IllegalStateException {
- char[] pathChars = StringUtil.trimToEmpty(path).toCharArray();
- int length = pathChars.length;
-
- // Check path and slash.
- boolean startsWithSlash = false;
- boolean endsWithSlash = false;
-
- if (length > 0) {
- char firstChar = pathChars[0];
- char lastChar = pathChars[length - 1];
-
- startsWithSlash = firstChar == '/' || firstChar == '\\';
- endsWithSlash = lastChar == '/' || lastChar == '\\';
- }
-
- StringBuilder buf = new StringBuilder(length);
- boolean isAbsolutePath = forceAbsolute || !forceRelative && startsWithSlash;
- int index = startsWithSlash ? 0 : -1;
- int level = 0;
-
- if (isAbsolutePath) {
- buf.append("/");
- }
-
- while (index < length) {
- index = indexOfSlash(pathChars, index + 1, false);
-
- if (index == length) {
- break;
- }
-
- int nextSlashIndex = indexOfSlash(pathChars, index, true);
-
- String element = new String(pathChars, index, nextSlashIndex - index);
- index = nextSlashIndex;
-
- // Ignore "."
- if (".".equals(element)) {
- continue;
- }
-
- // Backtrack ".."
- if ("..".equals(element)) {
- if (level == 0) {
- if (isAbsolutePath) {
- throw new IllegalStateException(path);
- } else {
- buf.append("../");
- }
- } else {
- buf.setLength(pathChars[--level]);
- }
-
- continue;
- }
-
- pathChars[level++] = (char)buf.length();
- buf.append(element).append('/');
- }
-
- // remove the last "/"
- if (buf.length() > 0) {
- if (!endsWithSlash || removeTrailingSlash) {
- buf.setLength(buf.length() - 1);
- }
- }
-
- return buf.toString();
- }
-
- private static int indexOfSlash(char[] chars, int beginIndex, boolean slash) {
- int i = beginIndex;
-
- for (; i < chars.length; i++) {
- char ch = chars[i];
-
- if (slash) {
- if (ch == '/' || ch == '\\') {
- break; // if a slash
- }
- } else {
- if (ch != '/' && ch != '\\') {
- break; // if not a slash
- }
- }
- }
-
- return i;
- }
-
- private FilterUtil() {}
-}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver b/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver
new file mode 100644
index 0000000000..a924cf5bc3
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver
@@ -0,0 +1 @@
+com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulApiDefinitionChangeObserver
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java
similarity index 95%
rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java
index 81425be222..f3c0b3e201 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+
import org.junit.Assert;
import org.junit.Test;
-
/**
* @author tiger
*/
@@ -56,7 +56,7 @@ public void clear() {
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
ZuulBlockFallbackManager.clear();
- Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE);
+ Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE);
}
}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java
similarity index 94%
rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java
index f701d4a9da..f7ec49bc66 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.fallback;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+
import org.junit.Assert;
import org.junit.Test;
@@ -48,7 +49,8 @@ public void testGetNotInCacheRoute() throws Exception {
@Test
public void testFlowControlFallbackResponse() throws Exception {
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
- BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new FlowException("flow exception"));
+ BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE,
+ new FlowException("flow exception"));
Assert.assertEquals(clientHttpResponse.getCode(), 429);
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java
similarity index 51%
rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java
index 8f2bca89e7..4c9096d548 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java
@@ -14,42 +14,37 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
import com.netflix.zuul.context.RequestContext;
import org.junit.Assert;
import org.junit.Test;
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE;
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.ERROR_TYPE;
/**
* @author tiger
*/
-public class SentinelErrorFilterTest {
+public class SentinelZuulErrorFilterTest {
+
@Test
public void testFilterType() throws Exception {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
- Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE);
+ SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
+ Assert.assertEquals(sentinelZuulErrorFilter.filterType(), ERROR_TYPE);
}
@Test
public void testShouldFilter() {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- Assert.assertFalse(properties.isEnabled());
- properties.setEnabled(true);
- SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
+ SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setThrowable(new RuntimeException());
- Assert.assertTrue(sentinelErrorFilter.shouldFilter());
+ Assert.assertTrue(sentinelZuulErrorFilter.shouldFilter());
}
@Test
public void testRun() throws Exception {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
- Object result = sentinelErrorFilter.run();
+ SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
+ Object result = sentinelZuulErrorFilter.run();
Assert.assertNull(result);
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java
similarity index 55%
rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java
rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java
index 6ccd6cae20..8d8046190d 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java
@@ -14,31 +14,28 @@
* limitations under the License.
*/
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
import org.junit.Assert;
import org.junit.Test;
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE;
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.POST_TYPE;
/**
* @author tiger
*/
-public class SentinelPostFilterTest {
+public class SentinelZuulPostFilterTest {
@Test
public void testFilterType() throws Exception {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
- Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE);
+ SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter();
+ Assert.assertEquals(sentinelZuulPostFilter.filterType(), POST_TYPE);
}
@Test
public void testRun() throws Exception {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
- Object result = sentinelPostFilter.run();
+ SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter();
+ Object result = sentinelZuulPostFilter.run();
Assert.assertNull(result);
}
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java
new file mode 100644
index 0000000000..289a9b92c8
--- /dev/null
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters;
+
+import com.netflix.zuul.context.RequestContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.PRE_TYPE;
+import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SERVICE_ID_KEY;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author tiger
+ */
+public class SentinelZuulPreFilterTest {
+
+ private String SERVICE_ID = "servicea";
+
+ private String URI = "/servicea/test";
+
+ @Mock
+ private HttpServletRequest httpServletRequest;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(httpServletRequest.getContextPath()).thenReturn("");
+ when(httpServletRequest.getPathInfo()).thenReturn(URI);
+ RequestContext requestContext = new RequestContext();
+ requestContext.set(SERVICE_ID_KEY, SERVICE_ID);
+ requestContext.setRequest(httpServletRequest);
+ RequestContext.testSetCurrentContext(requestContext);
+ }
+
+ @Test
+ public void testFilterType() throws Exception {
+ SentinelZuulPreFilter sentinelZuulPreFilter = new SentinelZuulPreFilter();
+ Assert.assertEquals(sentinelZuulPreFilter.filterType(), PRE_TYPE);
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java
deleted file mode 100644
index ab71e5cfd7..0000000000
--- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.alibaba.csp.sentinel.adapter.zuul.filters;
-
-import com.alibaba.csp.sentinel.adapter.zuul.fallback.DefaultRequestOriginParser;
-import com.alibaba.csp.sentinel.adapter.zuul.fallback.RequestOriginParser;
-import com.alibaba.csp.sentinel.adapter.zuul.fallback.UrlCleaner;
-import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties;
-import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
-import com.netflix.zuul.context.RequestContext;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import javax.servlet.http.HttpServletRequest;
-
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE;
-import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY;
-import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.when;
-
-/**
- * @author tiger
- */
-public class SentinelPreFilterTest {
-
- private String SERVICE_ID = "servicea";
-
- private String URI = "/servicea/test";
-
- @Mock
- private HttpServletRequest httpServletRequest;
-
- @Mock
- private UrlCleaner urlCleaner;
-
- private final RequestOriginParser requestOriginParser = new DefaultRequestOriginParser();
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(httpServletRequest.getContextPath()).thenReturn("");
- when(httpServletRequest.getPathInfo()).thenReturn(URI);
- RequestContext requestContext = new RequestContext();
- requestContext.set(SERVICE_ID_KEY, SERVICE_ID);
- requestContext.setRequest(httpServletRequest);
- RequestContext.testSetCurrentContext(requestContext);
- }
-
- @Test
- public void testFilterType() throws Exception {
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser);
- Assert.assertEquals(sentinelPreFilter.filterType(), PRE_TYPE);
- }
-
- @Test
- public void testRun() throws Exception {
- RequestContext ctx = RequestContext.getCurrentContext();
- SentinelZuulProperties properties = new SentinelZuulProperties();
- SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser);
- given(urlCleaner.clean(URI)).willReturn(URI);
- sentinelPreFilter.run();
- Assert.assertNull(ctx.getRouteHost());
- Assert.assertEquals(ctx.get(SERVICE_ID_KEY), SERVICE_ID);
- }
-
- @Test
- public void testServiceFallBackRun() throws Exception {
- RequestContext ctx = RequestContext.getCurrentContext();
- SentinelZuulProperties properties = new SentinelZuulProperties();
- properties.setEnabled(true);
- SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser);
-
- given(urlCleaner.clean(URI)).willAnswer(
- new Answer
+
+
+ org.apache.curator
+ curator-recipes
+ ${curator.version}
+ test
+
+
junitjunit
diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java
new file mode 100644
index 0000000000..b104da1d12
--- /dev/null
+++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.dashboard.rule.zookeeper;
+
+import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
+import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
+import com.alibaba.csp.sentinel.datasource.Converter;
+import org.apache.curator.framework.CuratorFramework;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component("flowRuleZookeeperPublisher")
+public class FlowRuleZookeeperProvider implements DynamicRuleProvider> {
+
+ @Autowired
+ private CuratorFramework zkClient;
+ @Autowired
+ private Converter> converter;
+
+ @Override
+ public List getRules(String appName) throws Exception {
+ String zkPath = ZookeeperConfigUtil.getPath(appName);
+ byte[] bytes = zkClient.getData().forPath(zkPath);
+ if (null == bytes || bytes.length == 0) {
+ return new ArrayList<>();
+ }
+ String s = new String(bytes);
+
+ return converter.convert(s);
+ }
+}
\ No newline at end of file
diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java
new file mode 100644
index 0000000000..de803dc955
--- /dev/null
+++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.dashboard.rule.zookeeper;
+
+import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
+import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.csp.sentinel.util.AssertUtil;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.Stat;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+@Component("flowRuleZookeeperPublisher")
+public class FlowRuleZookeeperPublisher implements DynamicRulePublisher> {
+ @Autowired
+ private CuratorFramework zkClient;
+ @Autowired
+ private Converter, String> converter;
+
+ @Override
+ public void publish(String app, List rules) throws Exception {
+ AssertUtil.notEmpty(app, "app name cannot be empty");
+
+ String path = ZookeeperConfigUtil.getPath(app);
+ Stat stat = zkClient.checkExists().forPath(path);
+ if (stat == null) {
+ zkClient.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, null);
+ }
+ byte[] data = CollectionUtils.isEmpty(rules) ? "".getBytes() : converter.convert(rules).getBytes();
+ zkClient.setData().forPath(path, data);
+ }
+}
\ No newline at end of file
diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java
new file mode 100644
index 0000000000..601ff2f947
--- /dev/null
+++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.dashboard.rule.zookeeper;
+
+import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
+import com.alibaba.csp.sentinel.datasource.Converter;
+import com.alibaba.fastjson.JSON;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+@Configuration
+public class ZookeeperConfig {
+
+ @Bean
+ public Converter, String> flowRuleEntityEncoder() {
+ return JSON::toJSONString;
+ }
+
+ @Bean
+ public Converter> flowRuleEntityDecoder() {
+ return s -> JSON.parseArray(s, FlowRuleEntity.class);
+ }
+
+ @Bean
+ public CuratorFramework zkClient() {
+ CuratorFramework zkClient =
+ CuratorFrameworkFactory.newClient("127.0.0.1:2181",
+ new ExponentialBackoffRetry(ZookeeperConfigUtil.SLEEP_TIME, ZookeeperConfigUtil.RETRY_TIMES));
+ zkClient.start();
+
+ return zkClient;
+ }
+}
\ No newline at end of file
diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java
new file mode 100644
index 0000000000..30605f2051
--- /dev/null
+++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2018 Alibaba Group Holding Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.csp.sentinel.dashboard.rule.zookeeper;
+
+
+import org.apache.commons.lang.StringUtils;
+
+public class ZookeeperConfigUtil {
+ public static final String RULE_ROOT_PATH = "/sentinel_rule_config";
+
+ public static final int RETRY_TIMES = 3;
+ public static final int SLEEP_TIME = 1000;
+
+ public static String getPath(String appName) {
+ StringBuilder stringBuilder = new StringBuilder(RULE_ROOT_PATH);
+
+ if (StringUtils.isBlank(appName)) {
+ return stringBuilder.toString();
+ }
+ if (appName.startsWith("/")) {
+ stringBuilder.append(appName);
+ } else {
+ stringBuilder.append("/")
+ .append(appName);
+ }
+ return stringBuilder.toString();
+ }
+}
\ No newline at end of file
From 55838d9fb0142561ad1436546f7b31610015c319 Mon Sep 17 00:00:00 2001
From: haofangyuan
Date: Sun, 5 May 2019 15:08:07 +0800
Subject: [PATCH 10/29] dashboard: Fix empty rule pushing bug of
FlowRuleZookeeperPublisher sample (#732)
---
.../dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java
index de803dc955..7d3f72bd07 100644
--- a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java
+++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java
@@ -44,7 +44,7 @@ public void publish(String app, List rules) throws Exception {
if (stat == null) {
zkClient.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, null);
}
- byte[] data = CollectionUtils.isEmpty(rules) ? "".getBytes() : converter.convert(rules).getBytes();
+ byte[] data = CollectionUtils.isEmpty(rules) ? "[]".getBytes() : converter.convert(rules).getBytes();
zkClient.setData().forPath(path, data);
}
}
\ No newline at end of file
From a2683383d1dc07f099553598a99b34efa7f764de Mon Sep 17 00:00:00 2001
From: Carpenter Lee
Date: Mon, 6 May 2019 10:55:42 +0800
Subject: [PATCH 11/29] [Feature]Add an extension to Sentinel internal
statistics (#730)
Add Sentinel MetricExtension, which provides extension to Sentinel internal statistics.
This extension provides callbacks when a request passes rule checking,
blocked by flow control, successfully end or exception occurred.
Accompanied by these events, response time and current thread number
will be recorded too.
Signed-off-by: Carpenter Lee
---
.../java/com/alibaba/csp/sentinel/Tracer.java | 13 ++-
.../metric/extension/MetricCallbackInit.java | 22 +++++
.../metric/extension/MetricExtension.java | 86 +++++++++++++++++++
.../extension/MetricExtensionProvider.java | 54 ++++++++++++
.../callback/MetricEntryCallback.java | 34 ++++++++
.../callback/MetricExitCallback.java | 28 ++++++
.../com.alibaba.csp.sentinel.init.InitFunc | 1 +
.../callback/FakeMetricExtension.java | 51 +++++++++++
.../callback/MetricEntryCallbackTest.java | 48 +++++++++++
.../callback/MetricExitCallbackTest.java | 43 ++++++++++
10 files changed, 376 insertions(+), 4 deletions(-)
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
create mode 100644 sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java
create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java
create mode 100644 sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
index 8a5cdea7eb..c9a4e573b8 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java
@@ -17,9 +17,11 @@
import com.alibaba.csp.sentinel.context.Context;
import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
import com.alibaba.csp.sentinel.node.ClusterNode;
import com.alibaba.csp.sentinel.node.DefaultNode;
import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
/**
* This class is used to record other exceptions except block exception.
@@ -55,7 +57,7 @@ public static void trace(Throwable e, int count) {
}
DefaultNode curNode = (DefaultNode)context.getCurNode();
- traceExceptionToNode(e, count, curNode);
+ traceExceptionToNode(e, count, context.getCurEntry(), curNode);
}
/**
@@ -74,7 +76,7 @@ public static void traceContext(Throwable e, int count, Context context) {
}
DefaultNode curNode = (DefaultNode)context.getCurNode();
- traceExceptionToNode(e, count, curNode);
+ traceExceptionToNode(e, count, context.getCurEntry(), curNode);
}
/**
@@ -103,13 +105,16 @@ public static void traceEntry(Throwable e, int count, Entry entry) {
}
DefaultNode curNode = (DefaultNode)entry.getCurNode();
- traceExceptionToNode(e, count, curNode);
+ traceExceptionToNode(e, count, entry, curNode);
}
- private static void traceExceptionToNode(Throwable t, int count, DefaultNode curNode) {
+ private static void traceExceptionToNode(Throwable t, int count, Entry entry, DefaultNode curNode) {
if (curNode == null) {
return;
}
+ for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
+ m.addException(entry.getResourceWrapper().getName(), count, t);
+ }
// clusterNode can be null when Constants.ON is false.
ClusterNode clusterNode = curNode.getClusterNode();
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
new file mode 100644
index 0000000000..0ffef9e12f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java
@@ -0,0 +1,22 @@
+package com.alibaba.csp.sentinel.metric.extension;
+
+import com.alibaba.csp.sentinel.init.InitFunc;
+import com.alibaba.csp.sentinel.metric.extension.callback.MetricEntryCallback;
+import com.alibaba.csp.sentinel.metric.extension.callback.MetricExitCallback;
+import com.alibaba.csp.sentinel.slots.statistic.StatisticSlotCallbackRegistry;
+
+/**
+ * Register callbacks for metric extension.
+ *
+ * @author Carpenter Lee
+ * @since 1.6.1
+ */
+public class MetricCallbackInit implements InitFunc {
+ @Override
+ public void init() throws Exception {
+ StatisticSlotCallbackRegistry.addEntryCallback(MetricEntryCallback.class.getCanonicalName(),
+ new MetricEntryCallback());
+ StatisticSlotCallbackRegistry.addExitCallback(MetricExitCallback.class.getCanonicalName(),
+ new MetricExitCallback());
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
new file mode 100644
index 0000000000..8f21328b8f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java
@@ -0,0 +1,86 @@
+package com.alibaba.csp.sentinel.metric.extension;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * This interface provides extension to Sentinel internal statistics.
+ *
+ * Please note that all method in this class will invoke in the same thread of biz logic.
+ * It's necessary to not do time-consuming operation in any of the interface's method,
+ * otherwise biz logic will be blocked.
+ *
+ *
+ * @author Carpenter Lee
+ * @since 1.6.1
+ */
+public interface MetricExtension {
+
+ /**
+ * Add current pass count of the resource name.
+ *
+ * @param n count to add
+ * @param resource resource name
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void addPass(String resource, int n, Object... args);
+
+ /**
+ * Add current block count of the resource name.
+ *
+ * @param n count to add
+ * @param resource resource name
+ * @param origin the original invoker.
+ * @param blockException block exception related.
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void addBlock(String resource, int n, String origin, BlockException blockException, Object... args);
+
+ /**
+ * Add current completed count of the resource name.
+ *
+ * @param n count to add
+ * @param resource resource name
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void addSuccess(String resource, int n, Object... args);
+
+ /**
+ * Add current exception count of the resource name.
+ *
+ * @param n count to add
+ * @param resource resource name
+ * @param throwable exception related.
+ */
+ void addException(String resource, int n, Throwable throwable);
+
+ /**
+ * Add response time of the resource name.
+ *
+ * @param rt response time in millisecond
+ * @param resource resource name
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void addRt(String resource, long rt, Object... args);
+
+ /**
+ * Increase current thread count of the resource name.
+ *
+ * @param resource resource name
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void increaseThreadNum(String resource, Object... args);
+
+ /**
+ * Decrease current thread count of the resource name.
+ *
+ * @param resource resource name
+ * @param args additional arguments of the resource, eg. if the resource is a method name,
+ * the args will be the parameters of the method.
+ */
+ void decreaseThreadNum(String resource, Object... args);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
new file mode 100644
index 0000000000..9980a6e137
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java
@@ -0,0 +1,54 @@
+package com.alibaba.csp.sentinel.metric.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.SpiLoader;
+
+/**
+ * Get all {@link MetricExtension}s via SPI.
+ *
+ * @author Carpenter Lee
+ * @since 1.6.1
+ */
+public class MetricExtensionProvider {
+ private static List metricExtensions = new ArrayList<>();
+
+ static {
+ resolveInstance();
+ }
+
+ private static void resolveInstance() {
+ List extensions = SpiLoader.loadInstanceList(MetricExtension.class);
+
+ if (extensions == null) {
+ RecordLog.warn("[MetricExtensionProvider] WARN: No existing MetricExtension found");
+ } else {
+ metricExtensions.addAll(extensions);
+ RecordLog.info("[MetricExtensionProvider] MetricExtension resolved, size=" + extensions.size());
+ }
+ }
+
+ /**
+ * Get all metric extensions. DO NOT MODIFY the returned list, use {@link #addMetricExtension(MetricExtension)}.
+ *
+ * @return all metric extensions.
+ */
+ public static List getMetricExtensions() {
+ return metricExtensions;
+ }
+
+ /**
+ * Add metric extension.
+ *
+ * Not that this method is NOT thread safe.
+ *
+ *
+ * @param metricExtension the metric extension to add.
+ */
+ public static void addMetricExtension(MetricExtension metricExtension) {
+ metricExtensions.add(metricExtension);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
new file mode 100644
index 0000000000..e5409970ee
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java
@@ -0,0 +1,34 @@
+package com.alibaba.csp.sentinel.metric.extension.callback;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * Metric extension entry callback.
+ *
+ * @author Carpenter Lee
+ * @since 1.6.1
+ */
+public class MetricEntryCallback implements ProcessorSlotEntryCallback {
+ @Override
+ public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param,
+ int count, Object... args) throws Exception {
+ for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
+ m.increaseThreadNum(resourceWrapper.getName(), args);
+ m.addPass(resourceWrapper.getName(), count, args);
+ }
+ }
+
+ @Override
+ public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper,
+ DefaultNode param, int count, Object... args) {
+ for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
+ m.addBlock(resourceWrapper.getName(), count, context.getOrigin(), ex, args);
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
new file mode 100644
index 0000000000..334a8f3a1b
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java
@@ -0,0 +1,28 @@
+package com.alibaba.csp.sentinel.metric.extension.callback;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.util.TimeUtil;
+
+/**
+ * Metric extension exit callback.
+ *
+ * @author Carpenter Lee
+ * @since 1.6.1
+ */
+public class MetricExitCallback implements ProcessorSlotExitCallback {
+ @Override
+ public void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) {
+ if (context.getCurEntry().getError() == null) {
+ long realRt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
+ m.addRt(resourceWrapper.getName(), realRt, args);
+ m.addSuccess(resourceWrapper.getName(), count, args);
+ m.decreaseThreadNum(resourceWrapper.getName(), args);
+ }
+ }
+ }
+}
diff --git a/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
new file mode 100644
index 0000000000..68a6ac77f7
--- /dev/null
+++ b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
@@ -0,0 +1 @@
+com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit
\ No newline at end of file
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java
new file mode 100644
index 0000000000..f9ac290830
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java
@@ -0,0 +1,51 @@
+package com.alibaba.csp.sentinel.metric.extension.callback;
+
+import com.alibaba.csp.sentinel.metric.extension.MetricExtension;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author Carpenter Lee
+ */
+class FakeMetricExtension implements MetricExtension {
+ long pass = 0;
+ long block = 0;
+ long success = 0;
+ long exception = 0;
+ long rt = 0;
+ long thread = 0;
+
+ @Override
+ public void addPass(String resource, int n, Object... args) {
+ pass += n;
+ }
+
+ @Override
+ public void addBlock(String resource, int n, String origin, BlockException ex, Object... args) {
+ block += n;
+ }
+
+ @Override
+ public void addSuccess(String resource, int n, Object... args) {
+ success += n;
+ }
+
+ @Override
+ public void addException(String resource, int n, Throwable t) {
+ exception += n;
+ }
+
+ @Override
+ public void addRt(String resource, long rt, Object... args) {
+ this.rt += rt;
+ }
+
+ @Override
+ public void increaseThreadNum(String resource, Object... args) {
+ thread++;
+ }
+
+ @Override
+ public void decreaseThreadNum(String resource, Object... args) {
+ thread--;
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java
new file mode 100644
index 0000000000..f1972c8e9e
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java
@@ -0,0 +1,48 @@
+package com.alibaba.csp.sentinel.metric.extension.callback;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Carpenter Lee
+ */
+public class MetricEntryCallbackTest {
+
+ @Test
+ public void onPass() throws Exception {
+ FakeMetricExtension extension = new FakeMetricExtension();
+ MetricExtensionProvider.addMetricExtension(extension);
+
+ MetricEntryCallback entryCallback = new MetricEntryCallback();
+ StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT);
+ int count = 2;
+ Object[] args = {"args1", "args2"};
+ entryCallback.onPass(null, resourceWrapper, null, count, args);
+ Assert.assertEquals(extension.pass, count);
+ Assert.assertEquals(extension.thread, 1);
+ }
+
+ @Test
+ public void onBlocked() throws Exception {
+ FakeMetricExtension extension = new FakeMetricExtension();
+ MetricExtensionProvider.addMetricExtension(extension);
+
+ MetricEntryCallback entryCallback = new MetricEntryCallback();
+ StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT);
+ Context context = mock(Context.class);
+ when(context.getOrigin()).thenReturn("origin1");
+ int count = 2;
+ Object[] args = {"args1", "args2"};
+ entryCallback.onBlocked(new FlowException("xx"), context, resourceWrapper, null, count, args);
+ Assert.assertEquals(extension.block, count);
+ }
+}
\ No newline at end of file
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java
new file mode 100644
index 0000000000..8bf2b89520
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java
@@ -0,0 +1,43 @@
+package com.alibaba.csp.sentinel.metric.extension.callback;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.util.TimeUtil;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Carpenter Lee
+ */
+public class MetricExitCallbackTest {
+
+ @Test
+ public void onExit() {
+ FakeMetricExtension extension = new FakeMetricExtension();
+ MetricExtensionProvider.addMetricExtension(extension);
+
+ MetricExitCallback exitCallback = new MetricExitCallback();
+ StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT);
+ int count = 2;
+ Object[] args = {"args1", "args2"};
+ extension.rt = 20;
+ extension.success = 6;
+ extension.thread = 10;
+ Context context = mock(Context.class);
+ Entry entry = mock(Entry.class);
+ when(entry.getError()).thenReturn(null);
+ when(entry.getCreateTime()).thenReturn(TimeUtil.currentTimeMillis() - 100);
+ when(context.getCurEntry()).thenReturn(entry);
+ exitCallback.onExit(context, resourceWrapper, count, args);
+ Assert.assertEquals(120, extension.rt, 10);
+ Assert.assertEquals(extension.success, 6 + count);
+ Assert.assertEquals(extension.thread, 10 - 1);
+ }
+}
\ No newline at end of file
From 6e4560845c2f1e63763bfd27ff11d49e5e59d101 Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Mon, 6 May 2019 13:46:03 +0800
Subject: [PATCH 12/29] Update SENTINEL_VERSION constant to 1.6.1
Signed-off-by: Eric Zhao
---
.../src/main/java/com/alibaba/csp/sentinel/Constants.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
index 923e04ae26..f4ff433e6d 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
@@ -30,7 +30,7 @@
*/
public final class Constants {
- public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.6.0");
+ public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.6.1");
public final static int MAX_CONTEXT_NAME_SIZE = 2000;
public final static int MAX_SLOT_CHAIN_SIZE = 6000;
From 274ecb51c3820ca63b68a3b865b7f8b9c405dba7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B0=B9=E5=90=89=E6=AC=A2?=
Date: Thu, 9 May 2019 10:07:27 +0800
Subject: [PATCH 13/29] Fix the bug that Chinese characters are malformed in
response body of ZuulBlockFallbackProvider (#737)
---
.../adapter/gateway/zuul/filters/SentinelZuulPreFilter.java | 2 ++
.../com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
index da6a8b8260..39f85ff0dc 100644
--- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
+++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java
@@ -133,6 +133,8 @@ public Object run() throws ZuulException {
// Set fallback response.
ctx.setResponseBody(blockResponse.toString());
ctx.setResponseStatusCode(blockResponse.getCode());
+ // Set Response ContentType
+ ctx.getResponse().setContentType("application/json; charset=utf-8");
} finally {
// We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly.
// So here the entries will be carried in the request context.
diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java
index 84d03e1e73..d1c83779ac 100644
--- a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java
+++ b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java
@@ -43,4 +43,4 @@ public ZuulFilter sentinelZuulPostFilter() {
public ZuulFilter sentinelZuulErrorFilter() {
return new SentinelZuulErrorFilter(-1);
}
-}
+}
\ No newline at end of file
From 18d8b4c8a7ba6063dc8e24c486b721ad8acb0ab6 Mon Sep 17 00:00:00 2001
From: Carpenter Lee
Date: Fri, 10 May 2019 14:20:00 +0800
Subject: [PATCH 14/29] Add CPU usage and system load to metric (#749)
Signed-off-by: Carpenter Lee
---
.../com/alibaba/csp/sentinel/Constants.java | 10 ++++
.../slots/system/SystemRuleManager.java | 1 +
.../slots/system/SystemStatusListener.java | 6 --
.../handler/SendMetricCommandHandler.java | 56 +++++++++++++++++--
4 files changed, 62 insertions(+), 11 deletions(-)
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
index f4ff433e6d..f7ba9288ae 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
@@ -43,6 +43,16 @@ public final class Constants {
*/
public final static String TOTAL_IN_RESOURCE_NAME = "__total_inbound_traffic__";
+ /**
+ * A virtual resource identifier for cpu usage statistics (since 1.6.1).
+ */
+ public final static String CPU_USAGE_RESOURCE_NAME = "__cpu_usage__";
+
+ /**
+ * A virtual resource identifier for system load statistics (since 1.6.1).
+ */
+ public final static String SYSTEM_LOAD_RESOURCE_NAME = "__system_load__";
+
/**
* Global ROOT statistic node that represents the universal parent node.
*/
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java
index 99b93a931d..13be34dea9 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java
@@ -222,6 +222,7 @@ protected void restoreSetting() {
qps = Double.MAX_VALUE;
highestSystemLoadIsSet = false;
+ highestCpuUsageIsSet = false;
maxRtIsSet = false;
maxThreadIsSet = false;
qpsIsSet = false;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java
index 8cf45a37c6..e02f0c858b 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java
@@ -33,8 +33,6 @@ public class SystemStatusListener implements Runnable {
volatile String reason = StringUtil.EMPTY;
- static final int processor = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
-
public double getSystemAverageLoad() {
return currentLoad;
}
@@ -46,10 +44,6 @@ public double getCpuUsage() {
@Override
public void run() {
try {
- if (!SystemRuleManager.getCheckSystemStatus()) {
- return;
- }
-
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
currentLoad = osBean.getSystemLoadAverage();
/**
diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java
index e841004c42..b3692e14d9 100755
--- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java
+++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java
@@ -15,18 +15,22 @@
*/
package com.alibaba.csp.sentinel.command.handler;
+import java.util.ArrayList;
import java.util.List;
+import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.command.CommandHandler;
import com.alibaba.csp.sentinel.command.CommandRequest;
import com.alibaba.csp.sentinel.command.CommandResponse;
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
-import com.alibaba.csp.sentinel.util.PidUtil;
import com.alibaba.csp.sentinel.config.SentinelConfig;
-import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.node.metric.MetricNode;
import com.alibaba.csp.sentinel.node.metric.MetricSearcher;
import com.alibaba.csp.sentinel.node.metric.MetricWriter;
+import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+import com.alibaba.csp.sentinel.util.PidUtil;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.TimeUtil;
/**
* Retrieve and aggregate {@link MetricNode} metrics.
@@ -34,7 +38,8 @@
* @author leyou
* @author Eric Zhao
*/
-@CommandMapping(name = "metric", desc = "get and aggregate metrics, accept param: startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}")
+@CommandMapping(name = "metric", desc = "get and aggregate metrics, accept param: "
+ + "startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}")
public class SendMetricCommandHandler implements CommandHandler {
private MetricSearcher searcher;
@@ -83,9 +88,11 @@ public CommandResponse handle(CommandRequest request) {
} catch (Exception ex) {
return CommandResponse.ofFailure(new RuntimeException("Error when retrieving metrics", ex));
}
-
if (list == null) {
- return CommandResponse.ofSuccess("No metrics");
+ list = new ArrayList<>();
+ }
+ if (StringUtil.isBlank(identity)) {
+ addCpuUsageAndLoad(list);
}
StringBuilder sb = new StringBuilder();
for (MetricNode node : list) {
@@ -93,4 +100,43 @@ public CommandResponse handle(CommandRequest request) {
}
return CommandResponse.ofSuccess(sb.toString());
}
+
+ /**
+ * add current cpu usage and load to the metric list.
+ *
+ * @param list metric list, should not be null
+ */
+ private void addCpuUsageAndLoad(List list) {
+ long time = TimeUtil.currentTimeMillis() / 1000 * 1000;
+ double load = SystemRuleManager.getCurrentSystemAvgLoad();
+ double usage = SystemRuleManager.getCurrentCpuUsage();
+ if (load > 0) {
+ MetricNode loadNode = toNode(load, time, Constants.SYSTEM_LOAD_RESOURCE_NAME);
+ list.add(loadNode);
+ }
+ if (usage > 0) {
+ MetricNode usageNode = toNode(usage, time, Constants.CPU_USAGE_RESOURCE_NAME);
+ list.add(usageNode);
+ }
+ }
+
+ /**
+ * transfer the value to a MetricNode, the value will multiply 10000 then truncate
+ * to long value, and as the {@link MetricNode#passQps}.
+ *
+ * This is an eclectic scheme before we have a standard metric format.
+ *