Add adapter support for Zuul 1.x (#188)
- implement `SentinelPreFilter`, `SentinelPostFilter` and `SentinelErrorFilter` - support fallback
This commit is contained in:
parent
e42551a877
commit
3a1eb56338
|
|
@ -18,6 +18,7 @@
|
|||
<module>sentinel-web-servlet</module>
|
||||
<module>sentinel-dubbo-adapter</module>
|
||||
<module>sentinel-grpc-adapter</module>
|
||||
<module>sentinel-zuul-adapter</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
@ -32,7 +33,11 @@
|
|||
<artifactId>sentinel-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-web-servlet</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,148 @@
|
|||
# Sentinel Zuul Adapter
|
||||
|
||||
Zuul does not provide rateLimit function, If use default `SentinelRibbonFilter` route filter. it wrapped by Hystrix Command. so only provide Service level
|
||||
circuit protect.
|
||||
|
||||
Sentinel can provide `ServiceId` level and `API Path` level flow control for zuul gateway service.
|
||||
|
||||
*Note*: this project is for zuul 1.
|
||||
|
||||
## How to use
|
||||
|
||||
1. Add maven dependency
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-zuul-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
|
||||
```
|
||||
|
||||
2. Register filters
|
||||
|
||||
```java
|
||||
// get registry
|
||||
final FilterRegistry r = FilterRegistry.instance();
|
||||
// this is property config. set filter ennable
|
||||
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);
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
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 get an `Exception`, trace the exception and exit context.
|
||||
|
||||
|
||||
the order of Filter can be changed in property:
|
||||
|
||||
|
||||
|
||||
Filters create structure like:
|
||||
|
||||
|
||||
```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)
|
||||
|
||||
```
|
||||
|
||||
`book` and `coke` are serviceId.
|
||||
|
||||
`---/book/coke` is api path, the real uri is `/coke`.
|
||||
|
||||
|
||||
## Integration with Sentinel DashBord
|
||||
|
||||
1. Start [Sentinel DashBord](https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0).
|
||||
|
||||
2. Sentinel has full rule config features. see [Dynamic-Rule-Configuration](https://github.com/alibaba/Sentinel/wiki/Dynamic-Rule-Configuration)
|
||||
|
||||
## Fallbacks
|
||||
|
||||
Implements `SentinelFallbackProvider` to define your own Fallback Provider when Sentinel Block Exception throwing. 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.
|
||||
|
||||
Here is an example:
|
||||
|
||||
```java
|
||||
|
||||
// custom provider
|
||||
public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class);
|
||||
|
||||
// you can define root as service level
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "/coke/coke";
|
||||
}
|
||||
|
||||
@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 {
|
||||
return new BlockResponse(500, "System Error", route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register fallback
|
||||
ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
|
||||
|
||||
```
|
||||
|
||||
Default block response
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"code":429,
|
||||
"message":"Sentinel block exception",
|
||||
"route":"/"
|
||||
}
|
||||
```
|
||||
|
||||
## Origin parser
|
||||
|
||||
自定义解析URL
|
||||
|
||||
```java
|
||||
|
||||
public class DefaultRequestOriginParser implements RequestOriginParser {
|
||||
@Override
|
||||
public String parseOrigin(HttpServletRequest request) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<?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-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.4.2-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>sentinel-zuul-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<zuul.version>1.3.1</zuul.version>
|
||||
<servlet.api.version>3.1.0</servlet.api.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.zuul</groupId>
|
||||
<artifactId>zuul-core</artifactId>
|
||||
<version>${zuul.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet.api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.constants;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class ZuulConstant {
|
||||
|
||||
/**
|
||||
* Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer.
|
||||
*/
|
||||
public static final String SERVICE_ID_KEY = "serviceId";
|
||||
|
||||
/**
|
||||
* {@link ZuulFilter#filterType()} error type.
|
||||
*/
|
||||
public static final String ERROR_TYPE = "error";
|
||||
|
||||
/**
|
||||
* {@link ZuulFilter#filterType()} post type.
|
||||
*/
|
||||
public static final String POST_TYPE = "post";
|
||||
|
||||
/**
|
||||
* {@link ZuulFilter#filterType()} pre type.
|
||||
*/
|
||||
public static final String PRE_TYPE = "pre";
|
||||
|
||||
/**
|
||||
* {@link ZuulFilter#filterType()} route type.
|
||||
*/
|
||||
public static final String ROUTE_TYPE = "route";
|
||||
|
||||
/**
|
||||
* Filter Order for SEND_RESPONSE_FILTER_ORDER
|
||||
*/
|
||||
public static final int SEND_RESPONSE_FILTER_ORDER = 1000;
|
||||
|
||||
/**
|
||||
* Zuul use Sentinel as default context when serviceId is empty.
|
||||
*/
|
||||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context";
|
||||
|
||||
private ZuulConstant(){}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException}
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class BlockResponse {
|
||||
private int code;
|
||||
private String message;
|
||||
private String route;
|
||||
|
||||
public BlockResponse(int code, String message, String route) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.route = route;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
public void setRoute(String route) {
|
||||
this.route = route;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" +
|
||||
"\"code\":" + code +
|
||||
", \"message\":" + "\"" + message + "\"" +
|
||||
", \"route\":" + "\"" + route + "\"" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
/**
|
||||
* Default Fallback provider for sentinel {@link BlockException}, {@literal *} meant for all routes.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return "*";
|
||||
}
|
||||
|
||||
@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 {
|
||||
return new BlockResponse(500, "System Error", route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.alibaba.csp.sentinel.adapter.zuul.fallback;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class DefaultRequestOriginParser implements RequestOriginParser {
|
||||
@Override
|
||||
public String parseOrigin(HttpServletRequest request) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* The origin parser parses request origin (e.g. IP, user, appName) from HTTP request.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public interface RequestOriginParser {
|
||||
|
||||
/**
|
||||
* Parse the origin from given HTTP request.
|
||||
*
|
||||
* @param request HTTP request
|
||||
* @return parsed origin
|
||||
*/
|
||||
String parseOrigin(HttpServletRequest request);
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
/***
|
||||
* <p>Process the url. Some path variables should be handled and unified.</p>
|
||||
* <p>e.g. collect_item_relation--10200012121-.html will be converted to collect_item_relation.html</p>
|
||||
*
|
||||
* @param originUrl original url
|
||||
* @return processed url
|
||||
*/
|
||||
String clean(String originUrl);
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This provide fall back class manager.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class ZuulBlockFallbackManager {
|
||||
|
||||
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<String, ZuulBlockFallbackProvider>();
|
||||
|
||||
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
|
||||
|
||||
/**
|
||||
* Register special provider for different route.
|
||||
*/
|
||||
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
|
||||
String route = provider.getRoute();
|
||||
if ("*".equals(route) || route == null) {
|
||||
defaultFallbackProvider = provider;
|
||||
} else {
|
||||
fallbackProviderCache.put(route, provider);
|
||||
}
|
||||
}
|
||||
|
||||
public static ZuulBlockFallbackProvider getFallbackProvider(String route) {
|
||||
ZuulBlockFallbackProvider provider = fallbackProviderCache.get(route);
|
||||
if (provider == null) {
|
||||
provider = defaultFallbackProvider;
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
public synchronized static void clear(){
|
||||
fallbackProviderCache.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* This interface is compatible for different spring cloud version.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public interface ZuulBlockFallbackProvider {
|
||||
|
||||
/**
|
||||
* The route this fallback will be used for.
|
||||
* @return The route the fallback will be used for.
|
||||
*/
|
||||
String getRoute();
|
||||
|
||||
/**
|
||||
* Provides a fallback response based on the cause of the failed execution.
|
||||
*
|
||||
* @param route The route the fallback is for
|
||||
* @param cause cause of the main method failure, may be <code>null</code>
|
||||
* @return the fallback response
|
||||
*/
|
||||
BlockResponse fallbackResponse(String route, Throwable cause);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class ZuulBlockFallbackManagerTest {
|
||||
|
||||
private String ROUTE = "/test";
|
||||
|
||||
private String DEFAULT_ROUTE = "*";
|
||||
|
||||
class MyNullResponseFallBackProvider implements ZuulBlockFallbackProvider {
|
||||
@Override
|
||||
public String getRoute() {
|
||||
return ROUTE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterProvider() throws Exception {
|
||||
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
|
||||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
||||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
||||
Assert.assertNull(myNullResponseFallBackProvider.fallbackResponse(ROUTE, new FlowException("flow ex")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clear() {
|
||||
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
|
||||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
||||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
||||
ZuulBlockFallbackManager.clear();
|
||||
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.zuul.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class ZuulBlockFallbackProviderTest {
|
||||
|
||||
private String ALL_ROUTE = "*";
|
||||
|
||||
@Test
|
||||
public void testGetNullRoute() throws Exception {
|
||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(null);
|
||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDefaultRoute() throws Exception {
|
||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotInCacheRoute() throws Exception {
|
||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider("/not/in");
|
||||
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlowControlFallbackResponse() throws Exception {
|
||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new FlowException("flow exception"));
|
||||
Assert.assertEquals(clientHttpResponse.getCode(), 429);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuntimeExceptionFallbackResponse() throws Exception {
|
||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new RuntimeException());
|
||||
Assert.assertEquals(clientHttpResponse.getCode(), 500);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.netflix.zuul.context.RequestContext;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE;
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class SentinelErrorFilterTest {
|
||||
@Test
|
||||
public void testFilterType() throws Exception {
|
||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
||||
Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFilter() {
|
||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
||||
Assert.assertFalse(properties.isEnabled());
|
||||
properties.setEnabled(true);
|
||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
ctx.setThrowable(new RuntimeException());
|
||||
Assert.assertTrue(sentinelErrorFilter.shouldFilter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun() throws Exception {
|
||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
||||
Object result = sentinelErrorFilter.run();
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE;
|
||||
|
||||
/**
|
||||
* @author tiger
|
||||
*/
|
||||
public class SentinelPostFilterTest {
|
||||
|
||||
@Test
|
||||
public void testFilterType() throws Exception {
|
||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
|
||||
Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun() throws Exception {
|
||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
|
||||
Object result = sentinelPostFilter.run();
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
throw new FlowException("flow ex");
|
||||
}
|
||||
}
|
||||
);
|
||||
sentinelPreFilter.run();
|
||||
Assert.assertNull(ctx.getRouteHost());
|
||||
Assert.assertNull(ctx.get(SERVICE_ID_KEY));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue