Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -3,11 +3,11 @@ | |||
This adapter provides **route level** and **customized API level** | |||
flow control for Zuul 2.x API Gateway. | |||
> *Note*: this adapter only support Zuul 2.x. | |||
> *Note*: this adapter only supports Zuul 2.x. | |||
## How to use | |||
> You can refer to demo `sentinel-demo-zuul2-gateway` | |||
> You can refer to demo [`sentinel-demo-zuul2-gateway`](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-zuul2-gateway). | |||
1. Add Maven dependency to your `pom.xml`: | |||
@@ -29,9 +29,9 @@ filterMultibinder.addBinding().toInstance(new SentinelZuulEndpoint()); | |||
## How it works | |||
As Zuul 2.x is based on netty, a event-drive model, so we use `AsyncEntry` to do flow control. | |||
As Zuul 2.x is based on Netty, an event-driven asynchronous model, so we use `AsyncEntry`. | |||
- `SentinelZuulInboundFilter`: This inbound filter will regard all proxy ID (`proxy` in `SessionContext`) and all customized API as resources. When a `BlockException` caught, the filter will set endpoint to find a fallback to execute. | |||
- `SentinelZuulInboundFilter`: This inbound filter will regard all routes (`routeVIP` in `SessionContext` by default) and all customized API as resources. When a `BlockException` caught, the filter will set endpoint to find a fallback to execute. | |||
- `SentinelZuulOutboundFilter`: When the response has no exception caught, the post filter will trace the exception and complete the entries. | |||
- `SentinelZuulEndpoint`: When an exception is caught, the filter will find a fallback to execute. | |||
@@ -40,6 +40,8 @@ As Zuul 2.x is based on netty, a event-drive model, so we use `AsyncEntry` to do | |||
1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard). | |||
2. You can configure the rules in Sentinel dashboard or via dynamic rule configuration. | |||
> You may need to add `-Dcsp.sentinel.app.type=1` property to mark this application as API gateway. | |||
## Fallbacks | |||
You can implement `ZuulBlockFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown. | |||
@@ -86,16 +88,3 @@ Default block response: | |||
"route":"/" | |||
} | |||
``` | |||
## Request origin parser | |||
You can register customized request origin parser like this: | |||
```java | |||
public class MyRequestOriginParser implements RequestOriginParser { | |||
@Override | |||
public String parseOrigin(HttpRequestMessage request) { | |||
return request.getInboundRequest().getOriginalHost() + ":" + request.getInboundRequest().getOriginalPort(); | |||
} | |||
} | |||
``` |
@@ -41,6 +41,7 @@ | |||
</exclusions> | |||
</dependency> | |||
<!-- The Spring library is introduced for AntMatcher --> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-core</artifactId> | |||
@@ -1,8 +1,27 @@ | |||
/* | |||
* 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.zuul2; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; | |||
import com.netflix.zuul.message.http.HttpRequestMessage; | |||
/** | |||
* @author wavesZh | |||
* @since 1.7.2 | |||
*/ | |||
public class HttpRequestMessageItemParser implements RequestItemParser<HttpRequestMessage> { | |||
@Override | |||
@@ -22,7 +22,7 @@ import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeOb | |||
/** | |||
* @author Eric Zhao | |||
* @since 1.6.0 | |||
* @since 1.7.2 | |||
*/ | |||
public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver { | |||
@@ -26,6 +26,7 @@ import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMes | |||
/** | |||
* @author wavesZh | |||
* @since 1.7.2 | |||
*/ | |||
public final class ZuulGatewayApiMatcherManager { | |||
@@ -1,30 +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.gateway.zuul2.callback; | |||
import com.netflix.zuul.message.http.HttpRequestMessage; | |||
/** | |||
* @author wavesZh | |||
*/ | |||
public class DefaultRequestOriginParser implements RequestOriginParser { | |||
@Override | |||
public String parseOrigin(HttpRequestMessage request) { | |||
return ""; | |||
} | |||
} |
@@ -1,35 +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.gateway.zuul2.callback; | |||
import com.netflix.zuul.message.http.HttpRequestMessage; | |||
/** | |||
* 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(HttpRequestMessage request); | |||
} |
@@ -1,38 +0,0 @@ | |||
/* | |||
* 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.zuul2.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() {} | |||
} |
@@ -16,15 +16,14 @@ | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants; | |||
/** | |||
* @author wavesZh | |||
*/ | |||
public class ZuulConstant { | |||
public class SentinelZuul2Constants { | |||
/** | |||
* Zuul use Sentinel as default context when serviceId is empty. | |||
* The default entrance (context) name when the routeId is empty. | |||
*/ | |||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context"; | |||
public static final String ZUUL_DEFAULT_CONTEXT = "zuul2_default_context"; | |||
/** | |||
* Zuul context key for keeping Sentinel entries. | |||
*/ | |||
@@ -36,5 +35,5 @@ public class ZuulConstant { | |||
*/ | |||
public static final String ZUUL_CTX_SENTINEL_BLOCKED_FLAG = "_sentinel_blocked_flag"; | |||
private ZuulConstant(){} | |||
private SentinelZuul2Constants() {} | |||
} |
@@ -19,7 +19,7 @@ package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
/** | |||
* Default Fallback provider for sentinel {@link BlockException}, {@literal *} meant for all routes. | |||
* Default fallback provider for Sentinel {@link BlockException}, {@literal *} meant for all routes. | |||
* | |||
* @author tiger | |||
*/ | |||
@@ -33,7 +33,7 @@ public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider { | |||
@Override | |||
public BlockResponse fallbackResponse(String route, Throwable cause) { | |||
if (cause instanceof BlockException) { | |||
return new BlockResponse(429, "Sentinel block exception", route); | |||
return new BlockResponse(429, "SentinelBlockException", route); | |||
} else { | |||
return new BlockResponse(500, "System Error", route); | |||
} | |||
@@ -16,10 +16,11 @@ | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.endpoint; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.BlockResponse; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.ZuulBlockFallbackManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback.ZuulBlockFallbackProvider; | |||
import com.netflix.zuul.context.SessionContext; | |||
import com.netflix.zuul.filters.http.HttpSyncEndpoint; | |||
import com.netflix.zuul.message.http.HttpRequestMessage; | |||
@@ -32,13 +33,14 @@ import com.netflix.zuul.message.http.HttpResponseMessageImpl; | |||
* @author wavesZh | |||
*/ | |||
public class SentinelZuulEndpoint extends HttpSyncEndpoint { | |||
@Override | |||
public HttpResponseMessage apply(HttpRequestMessage request) { | |||
SessionContext context = request.getContext(); | |||
Throwable throwable = context.getError(); | |||
String fallBackRoute = (String) context.get(ZuulConstant.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE); | |||
ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider( | |||
fallBackRoute); | |||
String fallBackRoute = (String) context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE); | |||
ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager | |||
.getFallbackProvider(fallBackRoute); | |||
BlockResponse response = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, throwable); | |||
HttpResponseMessage resp = new HttpResponseMessageImpl(context, request, response.getCode()); | |||
resp.setBodyAsText(response.toString()); | |||
@@ -13,7 +13,6 @@ | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.inbound; | |||
import java.util.ArrayDeque; | |||
@@ -21,20 +20,22 @@ import java.util.Deque; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import java.util.concurrent.Executor; | |||
import java.util.function.Function; | |||
import com.alibaba.csp.sentinel.AsyncEntry; | |||
import com.alibaba.csp.sentinel.EntryType; | |||
import com.alibaba.csp.sentinel.ResourceTypeConstants; | |||
import com.alibaba.csp.sentinel.SphU; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.HttpRequestMessageItemParser; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulGatewayApiMatcherManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.callback.ZuulGatewayCallbackManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.EntryHolder; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.endpoint.SentinelZuulEndpoint; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
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.SessionContext; | |||
import com.netflix.zuul.filters.http.HttpInboundFilter; | |||
@@ -45,7 +46,7 @@ import rx.schedulers.Schedulers; | |||
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; | |||
/** | |||
* Zuul2 inboundFilter for Sentinel. | |||
* The Zuul inbound filter wrapped with Sentinel route and customized API group entries. | |||
* | |||
* @author wavesZh | |||
*/ | |||
@@ -57,31 +58,53 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { | |||
private final String blockedEndpointName; | |||
/** | |||
* if executor is null, flow control action will do on I/O thread | |||
* If the executor is null, flow control action will be performed on I/O thread | |||
*/ | |||
private final Executor executor; | |||
/** | |||
* true if blocked but the rest of inbound filters will be skipped; | |||
* false even if blocked, user can invoke other inbound filters by yourself. | |||
* If true, the rest of inbound filters will be skipped when the request is blocked. | |||
*/ | |||
private final boolean fastError; | |||
private final Function<HttpRequestMessage, String> routeExtractor; | |||
private final GatewayParamParser<HttpRequestMessage> paramParser = new GatewayParamParser<>( | |||
new HttpRequestMessageItemParser()); | |||
/** | |||
* Constructor of the inbound filter, which extracts the route from the context route VIP attribute by default. | |||
* | |||
* @param order the order of the filter | |||
*/ | |||
public SentinelZuulInboundFilter(int order) { | |||
this(order, null); | |||
this(order, m -> m.getContext().getRouteVIP()); | |||
} | |||
public SentinelZuulInboundFilter(int order, Executor executor) { | |||
this(order, DEFAULT_BLOCK_ENDPOINT_NAME, executor, true); | |||
} | |||
public SentinelZuulInboundFilter(int order, Function<HttpRequestMessage, String> routeExtractor) { | |||
this(order, null, routeExtractor); | |||
} | |||
public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError) { | |||
public SentinelZuulInboundFilter(int order, Executor executor, Function<HttpRequestMessage, String> routeExtractor) { | |||
this(order, DEFAULT_BLOCK_ENDPOINT_NAME, executor, true, routeExtractor); | |||
} | |||
/** | |||
* Constructor of the inbound filter. | |||
* | |||
* @param order the order of the filter | |||
* @param blockedEndpointName the endpoint to go when the request is blocked | |||
* @param executor the executor where Sentinel do flow checking. If null, it will be executed in current thread. | |||
* @param fastError whether the rest of the filters will be skipped if the request is blocked | |||
* @param routeExtractor the route ID extractor | |||
*/ | |||
public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError, | |||
Function<HttpRequestMessage, String> routeExtractor) { | |||
AssertUtil.notEmpty(blockedEndpointName, "blockedEndpointName cannot be empty"); | |||
AssertUtil.notNull(routeExtractor, "routeExtractor cannot be null"); | |||
this.order = order; | |||
this.blockedEndpointName = blockedEndpointName; | |||
this.executor = executor; | |||
this.fastError = fastError; | |||
this.routeExtractor = routeExtractor; | |||
} | |||
@Override | |||
@@ -100,18 +123,17 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { | |||
private Observable<HttpRequestMessage> apply(HttpRequestMessage request) { | |||
SessionContext context = request.getContext(); | |||
String origin = parseOrigin(request); | |||
Deque<EntryHolder> holders = new ArrayDeque<>(); | |||
String routeId = context.getRouteVIP(); | |||
String routeId = routeExtractor.apply(request); | |||
String fallBackRoute = routeId; | |||
try { | |||
if (StringUtil.isNotBlank(routeId)) { | |||
ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin); | |||
ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId); | |||
doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, request, holders); | |||
} | |||
Set<String> matchingApis = pickMatchingApiDefinitions(request); | |||
if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) { | |||
ContextUtil.enter(ZuulConstant.ZUUL_DEFAULT_CONTEXT, origin); | |||
ContextUtil.enter(SentinelZuul2Constants.ZUUL_DEFAULT_CONTEXT); | |||
} | |||
for (String apiName : matchingApis) { | |||
fallBackRoute = apiName; | |||
@@ -119,8 +141,8 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { | |||
} | |||
return Observable.just(request); | |||
} catch (BlockException t) { | |||
context.put(ZuulConstant.ZUUL_CTX_SENTINEL_BLOCKED_FLAG, Boolean.TRUE); | |||
context.put(ZuulConstant.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE, fallBackRoute); | |||
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_BLOCKED_FLAG, Boolean.TRUE); | |||
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE, fallBackRoute); | |||
if (fastError) { | |||
context.setShouldSendErrorResponse(true); | |||
context.setErrorEndpoint(blockedEndpointName); | |||
@@ -130,7 +152,7 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { | |||
return Observable.error(t); | |||
} finally { | |||
if (!holders.isEmpty()) { | |||
context.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders); | |||
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders); | |||
} | |||
// clear context to avoid another request use incorrect context | |||
ContextUtil.exit(); | |||
@@ -139,14 +161,10 @@ public class SentinelZuulInboundFilter extends HttpInboundFilter { | |||
private void doSentinelEntry(String resourceName, final int resType, HttpRequestMessage input, Deque<EntryHolder> holders) throws BlockException { | |||
Object[] params = paramParser.parseParameterFor(resourceName, input, r -> r.getResourceMode() == resType); | |||
AsyncEntry entry = SphU.asyncEntry(resourceName, EntryType.IN, 1, params); | |||
AsyncEntry entry = SphU.asyncEntry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY, EntryType.IN, params); | |||
holders.push(new EntryHolder(entry, params)); | |||
} | |||
private String parseOrigin(HttpRequestMessage request) { | |||
return ZuulGatewayCallbackManager.getOriginParser().parseOrigin(request); | |||
} | |||
private Set<String> pickMatchingApiDefinitions(HttpRequestMessage message) { | |||
Set<String> apis = new HashSet<>(); | |||
for (HttpRequestMessageApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) { | |||
@@ -17,26 +17,19 @@ | |||
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.outbound; | |||
import java.util.Deque; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import com.alibaba.csp.sentinel.AsyncEntry; | |||
import com.alibaba.csp.sentinel.Tracer; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.ZuulConstant; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.SentinelZuul2Constants; | |||
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.EntryHolder; | |||
import com.alibaba.csp.sentinel.context.ContextUtil; | |||
import com.alibaba.csp.sentinel.slots.block.BlockException; | |||
import com.netflix.zuul.context.SessionContext; | |||
import com.netflix.zuul.filters.FilterError; | |||
import com.netflix.zuul.filters.http.HttpOutboundFilter; | |||
import com.netflix.zuul.message.http.HttpResponseMessage; | |||
import org.apache.commons.collections.CollectionUtils; | |||
import rx.Observable; | |||
/** | |||
* Zuul2 outboundFilter for Sentinel. | |||
* <p> | |||
* The filter will complete the entries and trace the exception that happen in previous filters. | |||
* The Zuul outbound filter which will complete the Sentinel entries and | |||
* trace the exception that happened in previous filters. | |||
* | |||
* @author wavesZh | |||
*/ | |||
@@ -61,23 +54,18 @@ public class SentinelZuulOutboundFilter extends HttpOutboundFilter { | |||
public HttpResponseMessage apply(HttpResponseMessage response) { | |||
SessionContext context = response.getContext(); | |||
if (context.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY) == null) { | |||
if (context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY) == null) { | |||
return response; | |||
} | |||
List<FilterError> errors = context.getFilterErrors().stream() | |||
.filter(e -> BlockException.isBlockException(e.getException())) | |||
.collect(Collectors.toList()); | |||
boolean notBlocked = true; | |||
if (CollectionUtils.isEmpty(errors)) { | |||
notBlocked = false; | |||
} | |||
Deque<EntryHolder> holders = (Deque<EntryHolder>) context.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
boolean previousBlocked = context.getFilterErrors().stream() | |||
.anyMatch(e -> BlockException.isBlockException(e.getException())); | |||
Deque<EntryHolder> holders = (Deque<EntryHolder>) context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY); | |||
while (!holders.isEmpty()) { | |||
EntryHolder holder = holders.pop(); | |||
if (notBlocked) { | |||
if (!previousBlocked) { | |||
Tracer.traceEntry(context.getError(), holder.getEntry()); | |||
holder.getEntry().exit(1, holder.getParams()); | |||
} | |||
holder.getEntry().exit(1, holder.getParams()); | |||
} | |||
return response; | |||
} | |||
@@ -1,96 +0,0 @@ | |||
package com.alibaba.csp.sentinel.demo.zuul2.gateway; | |||
import java.io.IOException; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
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.GatewayApiDefinitionManager; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem; | |||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; | |||
import com.google.inject.Injector; | |||
import com.google.inject.Scopes; | |||
import com.netflix.appinfo.EurekaInstanceConfig; | |||
import com.netflix.appinfo.providers.MyDataCenterInstanceConfigProvider; | |||
import com.netflix.config.ConfigurationManager; | |||
import com.netflix.governator.InjectorBuilder; | |||
import com.netflix.zuul.netty.server.BaseServerStartup; | |||
import com.netflix.zuul.netty.server.Server; | |||
public class Bootstrap { | |||
public static void main(String[] args) { | |||
new Bootstrap().start(); | |||
} | |||
public void start() { | |||
Server server; | |||
try { | |||
new GatewayRuleConfig().doInit(); | |||
ConfigurationManager.loadCascadedPropertiesFromResources("application"); | |||
Injector injector = InjectorBuilder.fromModule(new ZuulModule()).createInjector(); | |||
injector.getInstance(FiltersRegisteringService.class); | |||
BaseServerStartup serverStartup = injector.getInstance(BaseServerStartup.class); | |||
server = serverStartup.server(); | |||
server.start(true); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
private void initGatewayRules() { | |||
Set<GatewayFlowRule> rules = new HashSet<>(); | |||
rules.add(new GatewayFlowRule("another_customized_api") | |||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) | |||
.setCount(1) | |||
.setIntervalSec(1) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) | |||
.setFieldName("pa") | |||
) | |||
); | |||
rules.add(new GatewayFlowRule("some_customized_api") | |||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) | |||
.setCount(5) | |||
.setIntervalSec(1) | |||
.setParamItem(new GatewayParamFlowItem() | |||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) | |||
.setFieldName("pn") | |||
) | |||
); | |||
GatewayRuleManager.loadRules(rules); | |||
} | |||
private void initCustomizedApis() { | |||
Set<ApiDefinition> definitions = new HashSet<>(); | |||
ApiDefinition api1 = new ApiDefinition("some_customized_api") | |||
.setPredicateItems(new HashSet<ApiPredicateItem>() {{ | |||
add(new ApiPathPredicateItem().setPattern("/ahas")); | |||
add(new ApiPathPredicateItem().setPattern("/aliyun/**") | |||
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); | |||
}}); | |||
ApiDefinition api2 = new ApiDefinition("another_customized_api") | |||
.setPredicateItems(new HashSet<ApiPredicateItem>() {{ | |||
add(new ApiPathPredicateItem().setPattern("/**") | |||
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); | |||
}}); | |||
definitions.add(api1); | |||
definitions.add(api2); | |||
GatewayApiDefinitionManager.loadApiDefinitions(definitions); | |||
} | |||
public static class ZuulModule extends ZuulSampleModule { | |||
@Override | |||
protected void configure() { | |||
//DataCenterInfo | |||
bind(EurekaInstanceConfig.class) | |||
.toProvider(MyDataCenterInstanceConfigProvider.class) | |||
.in(Scopes.SINGLETON); | |||
super.configure(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
/* | |||
* 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.demo.zuul2.gateway; | |||
import java.io.IOException; | |||
import com.google.inject.Injector; | |||
import com.google.inject.Scopes; | |||
import com.netflix.appinfo.EurekaInstanceConfig; | |||
import com.netflix.appinfo.providers.MyDataCenterInstanceConfigProvider; | |||
import com.netflix.config.ConfigurationManager; | |||
import com.netflix.governator.InjectorBuilder; | |||
import com.netflix.zuul.netty.server.BaseServerStartup; | |||
import com.netflix.zuul.netty.server.Server; | |||
/** | |||
* <p>The Zuul 2.x demo with Sentinel gateway flow control.</p> | |||
* <p>Run with {@code -Dcsp.sentinel.api.type=1} to mark the demo as API gateway.</p> | |||
* | |||
* @author wavesZh | |||
*/ | |||
public class ZuulBootstrap { | |||
public static void main(String[] args) { | |||
new ZuulBootstrap().start(); | |||
} | |||
public void start() { | |||
Server server; | |||
try { | |||
// Load sample rules. You may also manage rules in Sentinel dashboard. | |||
new GatewayRuleConfig().doInit(); | |||
ConfigurationManager.loadCascadedPropertiesFromResources("application"); | |||
Injector injector = InjectorBuilder.fromModule(new ZuulModule()).createInjector(); | |||
injector.getInstance(FiltersRegisteringService.class); | |||
BaseServerStartup serverStartup = injector.getInstance(BaseServerStartup.class); | |||
server = serverStartup.server(); | |||
server.start(true); | |||
} catch (IOException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
public static class ZuulModule extends ZuulSampleModule { | |||
@Override | |||
protected void configure() { | |||
//DataCenterInfo | |||
bind(EurekaInstanceConfig.class) | |||
.toProvider(MyDataCenterInstanceConfigProvider.class) | |||
.in(Scopes.SINGLETON); | |||
super.configure(); | |||
} | |||
} | |||
} |