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 <sczyh16@gmail.com>
This commit is contained in:
parent
540428bae0
commit
1baac7783d
|
|
@ -1,6 +1,7 @@
|
||||||
# Sentinel Zuul Adapter
|
# 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.
|
> *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
|
2. Register filters
|
||||||
|
|
||||||
```java
|
For Spring Cloud Zuul users, we only need to inject the three filters in Spring configuration class like this:
|
||||||
// get 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.
|
```java
|
||||||
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, defaultUrlCleaner, defaultRequestOriginParser);
|
@Configuration
|
||||||
r.put("sentinelPreFilter", sentinelPreFilter);
|
public class ZuulConfig {
|
||||||
SentinelPostFilter postFilter = new SentinelPostFilter(properties);
|
|
||||||
r.put("sentinelPostFilter", postFilter);
|
@Bean
|
||||||
SentinelErrorFilter errorFilter = new SentinelErrorFilter(properties);
|
public ZuulFilter sentinelZuulPreFilter() {
|
||||||
r.put("sentinelErrorFilter", errorFilter);
|
// 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 filter registry
|
||||||
|
final FilterRegistry r = FilterRegistry.instance();
|
||||||
|
|
||||||
|
// 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
|
## 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**.
|
- `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.
|
||||||
- `SentinelPostFilter`: When success response, exit entry.
|
- `SentinelZuulPostFilter`: When the response has no exception caught, the post filter will complete the entries.
|
||||||
- `SentinelPreFilter`: When an `Exception` caught, trace the exception and exit context.
|
- `SentinelZuulPreFilter`: When an exception is caught, the filter will trace the exception and complete the entries.
|
||||||
|
|
||||||
<img width="792" src="https://user-images.githubusercontent.com/9305625/47277113-6b5da780-d5ef-11e8-8a0a-93a6b09b0887.png">
|
<img width="792" src="https://user-images.githubusercontent.com/9305625/47277113-6b5da780-d5ef-11e8-8a0a-93a6b09b0887.png">
|
||||||
|
|
||||||
The order of filters can be changed in property.
|
The order of filters can be changed via the constructor.
|
||||||
|
|
||||||
The invocation chain resembles this:
|
The invocation chain resembles this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
EntranceNode: machine-root(t:3 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)
|
||||||
-EntranceNode: coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
|
--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)
|
||||||
--coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
|
--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)
|
||||||
---/coke/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
|
-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)
|
||||||
-EntranceNode: sentinel_default_context(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
|
--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)
|
||||||
-EntranceNode: book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0)
|
--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(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 API path is `/coke`.
|
|
||||||
|
|
||||||
## Integration with Sentinel Dashboard
|
## Integration with Sentinel Dashboard
|
||||||
|
|
||||||
1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/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.
|
You can implement `SentinelFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown.
|
||||||
The default fallback provider is `DefaultBlockFallbackProvider`.
|
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:
|
Here is an example:
|
||||||
|
|
||||||
|
|
@ -90,7 +105,7 @@ public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||||
// you can define root as service level
|
// you can define root as service level
|
||||||
@Override
|
@Override
|
||||||
public String getRoute() {
|
public String getRoute() {
|
||||||
return "/coke/coke";
|
return "my-route";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@
|
||||||
<groupId>com.alibaba.csp</groupId>
|
<groupId>com.alibaba.csp</groupId>
|
||||||
<artifactId>sentinel-core</artifactId>
|
<artifactId>sentinel-core</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
|
@ -39,6 +44,20 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- we need to use AntPathMatcher in spring-core -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>4.3.20.RELEASE</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||||
|
<version>1.4.6.RELEASE</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
|
|
||||||
|
|
@ -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<RequestContext> {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<ApiDefinition> apiDefinitions) {
|
||||||
|
ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<String, RequestContextApiMatcher> API_MATCHER_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static Map<String, RequestContextApiMatcher> 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<ApiDefinition> getApiDefinitionSet() {
|
||||||
|
Set<ApiDefinition> set = new HashSet<>();
|
||||||
|
for (RequestContextApiMatcher matcher : API_MATCHER_MAP.values()) {
|
||||||
|
set.add(matcher.getApiDefinition());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized void loadApiDefinitions(/*@Valid*/ Set<ApiDefinition> 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() {}
|
||||||
|
}
|
||||||
|
|
@ -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<RequestContext> {
|
||||||
|
|
||||||
|
public RequestContextApiMatcher(ApiDefinition apiDefinition) {
|
||||||
|
super(apiDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeMatchers() {
|
||||||
|
if (apiDefinition.getPredicateItems() != null) {
|
||||||
|
for (ApiPredicateItem item : apiDefinition.getPredicateItems()) {
|
||||||
|
Predicate<RequestContext> predicate = fromApiPredicate(item);
|
||||||
|
if (predicate != null) {
|
||||||
|
matchers.add(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<RequestContext> fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) {
|
||||||
|
if (item instanceof ApiPathPredicateItem) {
|
||||||
|
return fromApiPathPredicate((ApiPathPredicateItem)item);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<RequestContext> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<RequestContext> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<RequestContext> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<RequestContext> all() {
|
||||||
|
return new Predicate<RequestContext>() {
|
||||||
|
@Override
|
||||||
|
public boolean test(RequestContext requestContext) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<RequestContext> antPath(String pathPattern) {
|
||||||
|
return new PrefixRoutePathMatcher(pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<RequestContext> exactPath(final String path) {
|
||||||
|
return new Predicate<RequestContext>() {
|
||||||
|
@Override
|
||||||
|
public boolean test(RequestContext exchange) {
|
||||||
|
return exchange.getRequest().getServletPath().equals(path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<RequestContext> regexPath(String pathPattern) {
|
||||||
|
return new RegexRoutePathMatcher(pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZuulRouteMatchers() {}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
@ -6,6 +6,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
* @author tiger
|
* @author tiger
|
||||||
*/
|
*/
|
||||||
public class DefaultRequestOriginParser implements RequestOriginParser {
|
public class DefaultRequestOriginParser implements RequestOriginParser {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String parseOrigin(HttpServletRequest request) {
|
public String parseOrigin(HttpServletRequest request) {
|
||||||
return "";
|
return "";
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* 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;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
|
@ -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() {}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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;
|
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.
|
* Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer.
|
||||||
*/
|
*/
|
||||||
public static final String SERVICE_ID_KEY = "serviceId";
|
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.
|
* {@link ZuulFilter#filterType()} error type.
|
||||||
|
|
@ -58,5 +62,12 @@ public class ZuulConstant {
|
||||||
*/
|
*/
|
||||||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context";
|
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(){}
|
private ZuulConstant(){}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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}
|
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException}
|
||||||
|
|
@ -22,7 +22,12 @@ package com.alibaba.csp.sentinel.adapter.zuul.fallback;
|
||||||
* @author tiger
|
* @author tiger
|
||||||
*/
|
*/
|
||||||
public class BlockResponse {
|
public class BlockResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP status code.
|
||||||
|
*/
|
||||||
private int code;
|
private int code;
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
private String route;
|
private String route;
|
||||||
|
|
||||||
|
|
@ -14,9 +14,8 @@
|
||||||
* limitations under the License.
|
* 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;
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,7 +32,6 @@ public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
||||||
RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
|
|
||||||
if (cause instanceof BlockException) {
|
if (cause instanceof BlockException) {
|
||||||
return new BlockResponse(429, "Sentinel block exception", route);
|
return new BlockResponse(429, "Sentinel block exception", route);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -14,11 +14,13 @@
|
||||||
* limitations under the License.
|
* 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.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This provide fall back class manager.
|
* This provide fall back class manager.
|
||||||
*
|
*
|
||||||
|
|
@ -26,7 +28,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class ZuulBlockFallbackManager {
|
public class ZuulBlockFallbackManager {
|
||||||
|
|
||||||
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<String, ZuulBlockFallbackProvider>();
|
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<>();
|
||||||
|
|
||||||
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
|
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
|
||||||
|
|
||||||
|
|
@ -34,6 +36,7 @@ public class ZuulBlockFallbackManager {
|
||||||
* Register special provider for different route.
|
* Register special provider for different route.
|
||||||
*/
|
*/
|
||||||
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
|
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
|
||||||
|
AssertUtil.notNull(provider, "fallback provider cannot be null");
|
||||||
String route = provider.getRoute();
|
String route = provider.getRoute();
|
||||||
if ("*".equals(route) || route == null) {
|
if ("*".equals(route) || route == null) {
|
||||||
defaultFallbackProvider = provider;
|
defaultFallbackProvider = provider;
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* 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.
|
* This interface is compatible for different spring cloud version.
|
||||||
|
|
@ -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<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) 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<AsyncEntry> asyncEntries = (Deque<AsyncEntry>) 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() {}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<RequestContext> 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<AsyncEntry> asyncEntries) throws BlockException {
|
||||||
|
Object[] params = paramParser.parseParameterFor(resourceName, requestContext,
|
||||||
|
new Predicate<GatewayFlowRule>() {
|
||||||
|
@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<AsyncEntry> 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<String> 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<String> pickMatchingApiDefinitions(RequestContext requestContext) {
|
||||||
|
Set<String> apis = new HashSet<>();
|
||||||
|
for (RequestContextApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) {
|
||||||
|
if (matcher.test(requestContext)) {
|
||||||
|
apis.add(matcher.getApiName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 {
|
|
||||||
|
|
||||||
/***
|
|
||||||
* <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);
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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() {}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulApiDefinitionChangeObserver
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
* limitations under the License.
|
* 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 com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author tiger
|
* @author tiger
|
||||||
*/
|
*/
|
||||||
|
|
@ -56,7 +56,7 @@ public class ZuulBlockFallbackManagerTest {
|
||||||
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
||||||
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
||||||
ZuulBlockFallbackManager.clear();
|
ZuulBlockFallbackManager.clear();
|
||||||
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE);
|
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -14,9 +14,10 @@
|
||||||
* limitations under the License.
|
* 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 com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
@ -48,7 +49,8 @@ public class ZuulBlockFallbackProviderTest {
|
||||||
@Test
|
@Test
|
||||||
public void testFlowControlFallbackResponse() throws Exception {
|
public void testFlowControlFallbackResponse() throws Exception {
|
||||||
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
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);
|
Assert.assertEquals(clientHttpResponse.getCode(), 429);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,42 +14,37 @@
|
||||||
* limitations under the License.
|
* 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 com.netflix.zuul.context.RequestContext;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
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
|
* @author tiger
|
||||||
*/
|
*/
|
||||||
public class SentinelErrorFilterTest {
|
public class SentinelZuulErrorFilterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterType() throws Exception {
|
public void testFilterType() throws Exception {
|
||||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
|
||||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
Assert.assertEquals(sentinelZuulErrorFilter.filterType(), ERROR_TYPE);
|
||||||
Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShouldFilter() {
|
public void testShouldFilter() {
|
||||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
|
||||||
Assert.assertFalse(properties.isEnabled());
|
|
||||||
properties.setEnabled(true);
|
|
||||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
|
||||||
RequestContext ctx = RequestContext.getCurrentContext();
|
RequestContext ctx = RequestContext.getCurrentContext();
|
||||||
ctx.setThrowable(new RuntimeException());
|
ctx.setThrowable(new RuntimeException());
|
||||||
Assert.assertTrue(sentinelErrorFilter.shouldFilter());
|
Assert.assertTrue(sentinelZuulErrorFilter.shouldFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRun() throws Exception {
|
public void testRun() throws Exception {
|
||||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter();
|
||||||
SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties);
|
Object result = sentinelZuulErrorFilter.run();
|
||||||
Object result = sentinelErrorFilter.run();
|
|
||||||
Assert.assertNull(result);
|
Assert.assertNull(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,31 +14,28 @@
|
||||||
* limitations under the License.
|
* 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.Assert;
|
||||||
import org.junit.Test;
|
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
|
* @author tiger
|
||||||
*/
|
*/
|
||||||
public class SentinelPostFilterTest {
|
public class SentinelZuulPostFilterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFilterType() throws Exception {
|
public void testFilterType() throws Exception {
|
||||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter();
|
||||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
|
Assert.assertEquals(sentinelZuulPostFilter.filterType(), POST_TYPE);
|
||||||
Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRun() throws Exception {
|
public void testRun() throws Exception {
|
||||||
SentinelZuulProperties properties = new SentinelZuulProperties();
|
SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter();
|
||||||
SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties);
|
Object result = sentinelZuulPostFilter.run();
|
||||||
Object result = sentinelPostFilter.run();
|
|
||||||
Assert.assertNull(result);
|
Assert.assertNull(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<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