Browse Source

Improve code and demo for sentinel-spring-webmvc-adapter module

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 5 years ago
parent
commit
20ad3c8f79
18 changed files with 260 additions and 212 deletions
  1. +47
    -56
      sentinel-adapter/sentinel-spring-webmvc-adapter/README.md
  2. +3
    -2
      sentinel-adapter/sentinel-spring-webmvc-adapter/pom.xml
  3. +26
    -17
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/AbstractSentinelInterceptor.java
  4. +10
    -21
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java
  5. +11
    -17
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebTotalInterceptor.java
  6. +6
    -6
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/BlockExceptionHandler.java
  7. +4
    -1
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/DefaultBlockExceptionHandler.java
  8. +4
    -4
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/UrlCleaner.java
  9. +4
    -2
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java
  10. +25
    -4
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java
  11. +14
    -9
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcTotalConfig.java
  12. +2
    -2
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java
  13. +35
    -0
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptorTest.java
  14. +4
    -4
      sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java
  15. +37
    -37
      sentinel-demo/sentinel-demo-spring-webmvc/pom.xml
  16. +3
    -3
      sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/WebMvcDemoApplication.java
  17. +17
    -21
      sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/config/InterceptorConfig.java
  18. +8
    -6
      sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/config/SentinelSpringMvcBlockHandlerConfig.java

+ 47
- 56
sentinel-adapter/sentinel-spring-webmvc-adapter/README.md View File

@@ -1,6 +1,8 @@
# Sentinel Spring MVC Interceptor
# Sentinel Spring MVC Adapter

Sentinel provides Spring MVC Interceptor integration to enable flow control for web requests, And support url like '/foo/{id}'
## Introduction

Sentinel provides integration for Spring Web to enable flow control for web requests.

Add the following dependency in `pom.xml` (if you are using Maven):

@@ -12,7 +14,7 @@ Add the following dependency in `pom.xml` (if you are using Maven):
</dependency>
```

Configure interceptor
Then we could add a configuration bean to configure the interceptor:

```java
@Configuration
@@ -20,96 +22,85 @@ public class InterceptorConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
//Add sentinel interceptor
addSpringMvcInterceptor(registry);
//If you want to sentinel the total flow, you can add total interceptor
addSpringMvcTotalInterceptor(registry);
}

private void addSpringMvcInterceptor(InterceptorRegistry registry) {
//Configure
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
//Custom configuration if necessary
// Enable the HTTP method prefix.
config.setHttpMethodSpecify(true);
config.setOriginParser(request -> request.getHeader("S-user"));
//Add sentinel interceptor
registry.addInterceptor(new SentinelInterceptor(config)).addPathPatterns("/**");
}

private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
//Configure
SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
//Custom configuration if necessary
config.setRequestAttributeName("my_sentinel_spring_mvc_total_entity_container");
config.setTotalResourceName("my-spring-mvc-total-url-request");
//Add sentinel interceptor
registry.addInterceptor(new SentinelTotalInterceptor(config)).addPathPatterns("/**");
// Add to the interceptor list.
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
}
}
```

Configure 'BlockException' handler, there are three options:
1. Global exception handling in spring MVC. <Recommend>
Then Sentinel will extract URL patterns defined in Web Controller as the web resource (e.g. `/foo/{id}`).

## Configuration

### Block handling

Sentinel Spring Web adapter provides a `BlockExceptionHandler` interface to handle the blocked requests.
We could set the handler via `SentinelWebMvcTotalConfig#setBlockExceptionHandler()` method.

By default the interceptor will throw out the `BlockException`.
We need to set a global exception handler function in Spring to handle it. An example:

```java
@ControllerAdvice
@Order(0)
public class SentinelSpringMvcBlockHandlerConfig {
public class SentinelBlockExceptionHandlerConfig {
private Logger logger = LoggerFactory.getLogger(this.getClass());

@ExceptionHandler(BlockException.class)
@ResponseBody
public String sentinelBlockHandler(BlockException e) {
AbstractRule rule = e.getRule();
logger.info("Blocked by sentinel, {}", rule.toString());
logger.info("Blocked by Sentinel: {}", rule.toString());
return "Blocked by Sentinel";
}
}

```
2. Use `DefaultBlockExceptionHandler`
```java
//SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
config.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
```
3. `implements BlockExceptionHandler`

We've provided a `DefaultBlockExceptionHandler`. When a request is blocked, the handler will return a default page
indicating the request is rejected (`Blocked by Sentinel (flow limiting)`).
The HTTP status code of the default block page is **429 (Too Many Requests)**.

We could also implement our implementation of the `BlockExceptionHandler` interface and
set to the config object. An example:

```java
//SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
config.setBlockExceptionHandler((request, response, e) -> {
String resourceName = e.getRule().getResource();
//Depending on your situation, you can choose to process or throw
// Depending on your situation, you can choose to process or throw
if ("/hello".equals(resourceName)) {
//Do something ......
//Write string or error page;
response.getWriter().write("Blocked by sentinel");
// Do something ......
response.getWriter().write("Blocked by Sentinel");
} else {
//Handle it in global exception handling
// Handle it in global exception handling
throw e;
}
});
```

Configuration
- Common configuration in `SentinelWebMvcConfig` and `SentinelWebMvcTotalConfig`
### Customized configuration

- Common configuration in `SentinelWebMvcConfig` and `SentinelWebMvcTotalConfig`:

| name | description | type | default value |
|------|------------|------|-------|
| blockExceptionHandler| The handler when blocked by sentinel, there are three options:<br/>1. The default value is null, you can hanlde `BlockException` in spring MVC;<br/>2.Use `DefaultBlockExceptionHandler`;<br/>3. `implements BlockExceptionHandler` | `BlockExceptionHandler` | `null` |
| originParser | `RequestOriginParser` interface is useful for extracting request origin (e.g. IP or appName from HTTP Header) from HTTP request | `RequestOriginParser` | `null` |
| `blockExceptionHandler`| The handler that handles the block request | `BlockExceptionHandler` | null (throw out the BlockException) |
| `originParser` | Extracting request origin (e.g. IP or appName from HTTP Header) from HTTP request | `RequestOriginParser` | - |

- `SentinelWebMvcConfig` configuration
- `SentinelWebMvcConfig` configuration:

| name | description | type | default value |
|------|------------|------|-------|
| urlCleaner | The `UrlCleaner` interface is designed for clean and unify the URL resource. For REST APIs, you can to clean the URL resource (e.g. `/api/user/getById` and `/api/user/getByName` -> `/api/user/getBy*`), avoid the amount of context and will exceed the threshold | `UrlCleaner` | `null` |
| requestAttributeName | Attribute name in request used by sentinel, please check record log, if it is already used, please set | `String` | sentinel_spring_mvc_entry_container |
| httpMethodSpecify | Specify http method, for example: GET:/hello | `boolean` | `false` |
| urlCleaner | The `UrlCleaner` interface is designed for clean and unify the URL resource. | `UrlCleaner` | - |
| requestAttributeName | Attribute key in request used by Sentinel (internal) | `String` | `$$sentinel_spring_web_entry_attr` |
| httpMethodSpecify | Specify whether the URL resource name should contain the HTTP method prefix (e.g. `POST:`). | `boolean` | `false` |


`SentinelWebMvcTotalConfig` configuration
- `SentinelWebMvcTotalConfig` configuration:

| name | description | type | default value |
|------|------------|------|-------|
| totalResourceName | The resource name in `SentinelTotalInterceptor` | `String` | spring-mvc-total-url-request |
| requestAttributeName | Attribute name in request used by sentinel, please check record log, if it is already used, please set | `String` | sentinel_spring_mvc_total_entry_container |

| totalResourceName | The resource name in `SentinelTotalInterceptor` | `String` | `spring-mvc-total-url-request` |
| requestAttributeName | Attribute key in request used by Sentinel (internal) | `String` | `$$sentinel_spring_web_total_entry_attr` |

+ 3
- 2
sentinel-adapter/sentinel-spring-webmvc-adapter/pom.xml View File

@@ -3,13 +3,14 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-adapter</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.7.0-SNAPSHOT</version>
<artifactId>sentinel-adapter</artifactId>
<version>1.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>sentinel-spring-webmvc-adapter</artifactId>
<packaging>jar</packaging>

<properties>
<spring.version>5.1.8.RELEASE</spring.version>


+ 26
- 17
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/AbstractSentinelInterceptor.java View File

@@ -20,38 +20,47 @@ import javax.servlet.http.HttpServletResponse;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.ResourceTypeConstants;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.BaseWebMvcConfig;
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 org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
* @author kaizi2009
* @since 1.7.1
*/
public abstract class AbstractSentinelInterceptor implements HandlerInterceptor {

public static final String SPRING_MVC_CONTEXT_NAME = "spring_mvc_context";
public static final String SENTINEL_SPRING_WEB_CONTEXT_NAME = "sentinel_spring_web_context";
private static final String EMPTY_ORIGIN = "";
protected static final String COLON = ":";
private BaseWebMvcConfig baseWebMvcConfig;

private final BaseWebMvcConfig baseWebMvcConfig;

public AbstractSentinelInterceptor(BaseWebMvcConfig config) {
AssertUtil.notNull(config, "BaseWebMvcConfig should not be null");
AssertUtil.assertNotBlank(config.getRequestAttributeName(), "requestAttributeName should not be blank");
this.baseWebMvcConfig = config;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {

throws Exception {
try {
String resourceName = getResourceName(request);

if (StringUtil.isNotEmpty(resourceName)) {
// Parse the request origin using registered origin parser.
String origin = parseOrigin(request);
ContextUtil.enter(SPRING_MVC_CONTEXT_NAME, origin);
Entry entry = SphU.entry(resourceName, EntryType.IN);
ContextUtil.enter(SENTINEL_SPRING_WEB_CONTEXT_NAME, origin);
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN);

setEntryInRequest(request, baseWebMvcConfig.getRequestAttributeName(), entry);
}
@@ -63,9 +72,10 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
}

/**
* Get sentinel resource name.
* @param request
* @return
* Return the resource name of the target web resource.
*
* @param request web request
* @return the resource name of the target web resource.
*/
protected abstract String getResourceName(HttpServletRequest request);

@@ -88,7 +98,8 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
protected void setEntryInRequest(HttpServletRequest request, String name, Entry entry) {
Object attrVal = request.getAttribute(name);
if (attrVal != null) {
RecordLog.warn(String.format("Already exist attribute name '%s' in request, please set `requestAttributeName`", name));
RecordLog.warn("[{}] The attribute key '{0}' already exists in request, please set `requestAttributeName`",
getClass().getSimpleName(), name);
} else {
request.setAttribute(name, entry);
}
@@ -96,7 +107,7 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor

protected Entry getEntryInRequest(HttpServletRequest request, String attrKey) {
Object entryObject = request.getAttribute(attrKey);
return entryObject == null ? null : (Entry) entryObject;
return entryObject == null ? null : (Entry)entryObject;
}

protected void removeEntryInRequest(HttpServletRequest request) {
@@ -112,11 +123,12 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
}
}

protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)
throws Exception {
if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
} else {
//Throw BlockException, handle it in spring mvc
// Throw BlockException directly. Users need to handle it in Spring global exception handler.
throw e;
}
}
@@ -132,7 +144,4 @@ public abstract class AbstractSentinelInterceptor implements HandlerInterceptor
return origin;
}

protected void setBaseWebMvcConfig(BaseWebMvcConfig config) {
this.baseWebMvcConfig = config;
}
}

sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelInterceptor.java → sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptor.java View File

@@ -17,7 +17,6 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;
import com.alibaba.csp.sentinel.log.RecordLog;

import javax.servlet.http.HttpServletRequest;

@@ -25,42 +24,32 @@ import com.alibaba.csp.sentinel.util.StringUtil;
import org.springframework.web.servlet.HandlerMapping;

/**
* Spring mvc interceptor that integrates with sentinel.
* Spring Web MVC interceptor that integrates with Sentinel.
*
* @author kaizi2009
* @since 1.7.1
*/
public class SentinelInterceptor extends AbstractSentinelInterceptor {
private SentinelWebMvcConfig config;
public class SentinelWebInterceptor extends AbstractSentinelInterceptor {

public SentinelInterceptor(SentinelWebMvcConfig config) {
super();
setConfig(config);
super.setBaseWebMvcConfig(config);
}
private final SentinelWebMvcConfig config;

public SentinelInterceptor() {
public SentinelWebInterceptor() {
this(new SentinelWebMvcConfig());
}

public SentinelInterceptor setConfig(SentinelWebMvcConfig config) {
public SentinelWebInterceptor(SentinelWebMvcConfig config) {
super(config);
if (config == null) {
// Use the default config by default.
this.config = new SentinelWebMvcConfig();
RecordLog.info("Config is null, use default config");
} else {
this.config = config;
}
RecordLog.info(String.format("SentinelInterceptor config: requestAttributeName=%s, originParser=%s, httpMethodSpecify=%s, blockExceptionHandler=%s, urlCleaner=%s", config.getRequestAttributeName(), config.getOriginParser(), config.isHttpMethodSpecify(), config.getBlockExceptionHandler(), config.getUrlCleaner()));
return this;
}

/**
* Get target in HttpServletRequest
*
* @param request
* @return
*/
@Override
protected String getResourceName(HttpServletRequest request) {
// Resolve the Spring Web URL pattern from the request attribute.
Object resourceNameObject = request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
if (resourceNameObject == null || !(resourceNameObject instanceof String)) {
return null;
@@ -72,7 +61,7 @@ public class SentinelInterceptor extends AbstractSentinelInterceptor {
}
// Add method specification if necessary
if (StringUtil.isNotEmpty(resourceName) && config.isHttpMethodSpecify()) {
resourceName = request.getMethod().toUpperCase() + COLON + resourceName;
resourceName = request.getMethod().toUpperCase() + ":" + resourceName;
}
return resourceName;
}

sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelTotalInterceptor.java → sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebTotalInterceptor.java View File

@@ -16,37 +16,31 @@
package com.alibaba.csp.sentinel.adapter.spring.webmvc;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;
import com.alibaba.csp.sentinel.log.RecordLog;

import javax.servlet.http.HttpServletRequest;

/**
* Spring mvc interceptor for all requests.
* The web interceptor for all requests, which will unify all URL as
* a single resource name (configured in {@link SentinelWebMvcTotalConfig}).
*
* @author kaizi2009
* @since 1.7.1
*/
public class SentinelTotalInterceptor extends AbstractSentinelInterceptor {
private SentinelWebMvcTotalConfig config;
public class SentinelWebTotalInterceptor extends AbstractSentinelInterceptor {

public SentinelTotalInterceptor(SentinelWebMvcTotalConfig config) {
super();
setConfig(config);
setBaseWebMvcConfig(config);
}

public SentinelTotalInterceptor() {
this(new SentinelWebMvcTotalConfig());
}
private final SentinelWebMvcTotalConfig config;

public SentinelTotalInterceptor setConfig(SentinelWebMvcTotalConfig config) {
public SentinelWebTotalInterceptor(SentinelWebMvcTotalConfig config) {
super(config);
if (config == null) {
this.config = new SentinelWebMvcTotalConfig();
RecordLog.info("Config is null, use default config");
} else {
this.config = config;
}
RecordLog.info(String.format("SentinelInterceptor config: requestAttributeName=%s, originParser=%s, blockExceptionHandler=%s, totalResourceName=%s", config.getRequestAttributeName(), config.getOriginParser(), config.getBlockExceptionHandler(), config.getTotalResourceName()));
return this;
}

public SentinelWebTotalInterceptor() {
this(new SentinelWebMvcTotalConfig());
}

@Override

+ 6
- 6
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/BlockExceptionHandler.java View File

@@ -21,19 +21,19 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Handle BlockException
* Handler for the blocked request.
*
* @author kaizi2009
*/
public interface BlockExceptionHandler {

/**
* Handle BlockException
* Handle the request when blocked.
*
* @param request
* @param response
* @param e Depending on your situation, you can choose to process or throw BlockException
* @throws Exception
* @param request Servlet request
* @param response Servlet response
* @param e the block exception
* @throws Exception users may throw out the BlockException or other error occurs
*/
void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception;



+ 4
- 1
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/DefaultBlockExceptionHandler.java View File

@@ -23,7 +23,7 @@ import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
* Default `BlockException` handler
* Default handler for the blocked request.
*
* @author kaizi2009
*/
@@ -31,6 +31,9 @@ public class DefaultBlockExceptionHandler implements BlockExceptionHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// Return 429 (Too Many Requests) by default.
response.setStatus(429);

StringBuffer url = request.getRequestURL();

if ("GET".equals(request.getMethod()) && StringUtil.isNotBlank(request.getQueryString())) {


+ 4
- 4
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/callback/UrlCleaner.java View File

@@ -16,17 +16,17 @@
package com.alibaba.csp.sentinel.adapter.spring.webmvc.callback;

/**
* Clean sentinel target
* Unify the resource target.
*
* @author kaizi2009
*/
public interface UrlCleaner {

/**
* Clean sentinel target
* Unify the resource target.
*
* @param originUrl
* @return
* @param originUrl the original URL
* @return the unified resource name
*/
String clean(String originUrl);
}

+ 4
- 2
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/BaseWebMvcConfig.java View File

@@ -17,13 +17,16 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.util.AssertUtil;

/**
* Common config
* Common base configuration for Spring Web MVC adapter.
*
* @author kaizi2009
* @since 1.7.1
*/
public abstract class BaseWebMvcConfig {

protected String requestAttributeName;
protected BlockExceptionHandler blockExceptionHandler;
protected RequestOriginParser originParser;
@@ -51,5 +54,4 @@ public abstract class BaseWebMvcConfig {
public void setOriginParser(RequestOriginParser originParser) {
this.originParser = originParser;
}

}

+ 25
- 4
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcConfig.java View File

@@ -19,12 +19,20 @@ import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.UrlCleaner;

/**
* @author kaizi2009
* @since 1.7.1
*/
public class SentinelWebMvcConfig extends BaseWebMvcConfig {

public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "sentinel_spring_mvc_entry_container";
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "$$sentinel_spring_web_entry_attr";

/**
* Specify the URL cleaner that unifies the URL resources.
*/
private UrlCleaner urlCleaner;
protected boolean httpMethodSpecify;
/**
* Specify whether the URL resource name should contain the HTTP method prefix (e.g. {@code POST:}).
*/
private boolean httpMethodSpecify;

public SentinelWebMvcConfig() {
super();
@@ -35,15 +43,28 @@ public class SentinelWebMvcConfig extends BaseWebMvcConfig {
return urlCleaner;
}

public void setUrlCleaner(UrlCleaner urlCleaner) {
public SentinelWebMvcConfig setUrlCleaner(UrlCleaner urlCleaner) {
this.urlCleaner = urlCleaner;
return this;
}

public boolean isHttpMethodSpecify() {
return httpMethodSpecify;
}

public void setHttpMethodSpecify(boolean httpMethodSpecify) {
public SentinelWebMvcConfig setHttpMethodSpecify(boolean httpMethodSpecify) {
this.httpMethodSpecify = httpMethodSpecify;
return this;
}

@Override
public String toString() {
return "SentinelWebMvcConfig{" +
"urlCleaner=" + urlCleaner +
", httpMethodSpecify=" + httpMethodSpecify +
", requestAttributeName='" + requestAttributeName + '\'' +
", blockExceptionHandler=" + blockExceptionHandler +
", originParser=" + originParser +
'}';
}
}

+ 14
- 9
sentinel-adapter/sentinel-spring-webmvc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/SentinelWebMvcTotalConfig.java View File

@@ -15,13 +15,14 @@
*/
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;


/**
* @author kaizi2009
* @since 1.7.1
*/
public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {

public static final String DEFAULT_TOTAL_RESOURCE_NAME = "spring-mvc-total-url-request";
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "sentinel_spring_mvc_total_entry_container";
public static final String DEFAULT_REQUEST_ATTRIBUTE_NAME = "$$sentinel_spring_web_total_entry_attr";

private String totalResourceName = DEFAULT_TOTAL_RESOURCE_NAME;

@@ -34,14 +35,18 @@ public class SentinelWebMvcTotalConfig extends BaseWebMvcConfig {
return totalResourceName;
}

/**
* Config total resource name
*
* @param totalResourceName
* @return
*/
public void setTotalResourceName(String totalResourceName) {
public SentinelWebMvcTotalConfig setTotalResourceName(String totalResourceName) {
this.totalResourceName = totalResourceName;
return this;
}

@Override
public String toString() {
return "SentinelWebMvcTotalConfig{" +
"totalResourceName='" + totalResourceName + '\'' +
", requestAttributeName='" + requestAttributeName + '\'' +
", blockExceptionHandler=" + blockExceptionHandler +
", originParser=" + originParser +
'}';
}
}

sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/TestInterceptor.java → sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelSpringMvcIntegrationTest.java View File

@@ -1,4 +1,3 @@
package com.alibaba.csp.sentinel.adapter.spring.webmvc;
/*
* Copyright 1999-2019 Alibaba Group Holding Ltd.
*
@@ -14,6 +13,7 @@ package com.alibaba.csp.sentinel.adapter.spring.webmvc;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.adapter.spring.webmvc;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -46,7 +46,7 @@ import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
@AutoConfigureMockMvc
public class TestInterceptor {
public class SentinelSpringMvcIntegrationTest {

private static final String HELLO_STR = "Hello!";
@Autowired

+ 35
- 0
sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/SentinelWebInterceptorTest.java View File

@@ -0,0 +1,35 @@
/*
* 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.spring.webmvc;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;

import org.junit.Test;

import static org.junit.Assert.*;

/**
* @author Eric Zhao
*/
public class SentinelWebInterceptorTest {

@Test(expected = IllegalArgumentException.class)
public void testPassIllegalConfig() {
SentinelWebMvcConfig config = new SentinelWebMvcConfig();
config.setRequestAttributeName(null);
SentinelWebInterceptor interceptor = new SentinelWebInterceptor(config);
}
}

+ 4
- 4
sentinel-adapter/sentinel-spring-webmvc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/spring/webmvc/config/InterceptorConfig.java View File

@@ -15,8 +15,8 @@
*/
package com.alibaba.csp.sentinel.adapter.spring.webmvc.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelTotalInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.slots.block.BlockException;
@@ -74,7 +74,7 @@ public class InterceptorConfig implements WebMvcConfigurer {
});

//Add sentinel interceptor
registry.addInterceptor(new SentinelInterceptor(config)).addPathPatterns("/**");
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
}

private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
@@ -86,6 +86,6 @@ public class InterceptorConfig implements WebMvcConfigurer {
config.setTotalResourceName("my_spring_mvc_total_url_request");

//Add sentinel interceptor
registry.addInterceptor(new SentinelTotalInterceptor(config)).addPathPatterns("/**");
registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
}
}

+ 37
- 37
sentinel-demo/sentinel-demo-spring-webmvc/pom.xml View File

@@ -1,44 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-demo</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.7.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sentinel-demo</artifactId>
<groupId>com.alibaba.csp</groupId>
<version>1.7.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>sentinel-demo-spring-webmvc</artifactId>
<artifactId>sentinel-demo-spring-webmvc</artifactId>

<properties>
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
</properties>
<properties>
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>

</project>

+ 3
- 3
sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/WebMvcDemoApplication.java View File

@@ -19,10 +19,10 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* <p>Add the JVM parameter to connect to the dashboard:</p>
* {@code -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dproject.name=sentinel-demo-spring-webmvc}
*
* @author kaizi2009
* <code>
* -Dcsp.sentinel.dashboard.server=127.0.0.1:8080 -Dproject.name=sentinel-demo-spring-webmvc
* </code>
*/
@SpringBootApplication
public class WebMvcDemoApplication {


+ 17
- 21
sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/config/InterceptorConfig.java View File

@@ -15,10 +15,12 @@
*/
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;

import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelTotalInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.DefaultBlockExceptionHandler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -33,34 +35,28 @@ public class InterceptorConfig implements WebMvcConfigurer {

@Override
public void addInterceptors(InterceptorRegistry registry) {
//Add sentinel interceptor
// Add Sentinel interceptor
addSpringMvcInterceptor(registry);

//If you want to sentinel the total flow, you can add total interceptor
addSpringMvcTotalInterceptor(registry);
}

private void addSpringMvcInterceptor(InterceptorRegistry registry) {
//Config
SentinelWebMvcConfig config = new SentinelWebMvcConfig();

config.setBlockExceptionHandler((request, response, e) -> {
//Depending on your situation, you can choose to process or throw
boolean needThrow = true;
if (needThrow) {
throw e;
} else {
//Write string or json string;
response.getWriter().write("Blocked by sentinel");
}
});
// Depending on your situation, you can choose to process the BlockException via
// the BlockExceptionHandler or throw it directly, then handle it
// in Spring web global exception handler.

//Custom configuration if necessary
// config.setBlockExceptionHandler((request, response, e) -> { throw e; });

// Use the default handler.
config.setBlockExceptionHandler(new DefaultBlockExceptionHandler());

// Custom configuration if necessary
config.setHttpMethodSpecify(true);
config.setOriginParser(request -> request.getHeader("S-user"));

//Add sentinel interceptor
registry.addInterceptor(new SentinelInterceptor(config)).addPathPatterns("/**");
// Add sentinel interceptor
registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
}

private void addSpringMvcTotalInterceptor(InterceptorRegistry registry) {
@@ -72,6 +68,6 @@ public class InterceptorConfig implements WebMvcConfigurer {
config.setTotalResourceName("my-spring-mvc-total-url-request");

//Add sentinel interceptor
registry.addInterceptor(new SentinelTotalInterceptor(config)).addPathPatterns("/**");
registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**");
}
}

+ 8
- 6
sentinel-demo/sentinel-demo-spring-webmvc/src/main/java/com/alibaba/csp/sentinel/demo/spring/webmvc/config/SentinelSpringMvcBlockHandlerConfig.java View File

@@ -16,7 +16,6 @@
package com.alibaba.csp.sentinel.demo.spring.webmvc.config;

import com.alibaba.csp.sentinel.demo.spring.webmvc.vo.ResultWrapper;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,20 +25,23 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
* Config blocked handler
* Spring configuration for global exception handler.
* This will be activated when the {@code BlockExceptionHandler}
* throws {@link BlockException directly}.
*
* @author kaizi2009
*/
@ControllerAdvice
@Order(0)
public class SentinelSpringMvcBlockHandlerConfig {

private Logger logger = LoggerFactory.getLogger(this.getClass());

@ExceptionHandler(BlockException.class)
@ResponseBody
public ResultWrapper sentinelBlockHandler(BlockException e) {
AbstractRule rule = e.getRule();
//Log
logger.info("Blocked by sentinel, {}", rule.toString());
//Return object
logger.warn("Blocked by Sentinel: {}", e.getRule());
// Return the customized result.
return ResultWrapper.blocked();
}
}

Loading…
Cancel
Save