From 0536fb6846798ed053be9ebca53c12d54ebc5d36 Mon Sep 17 00:00:00 2001 From: Eric Zhao Date: Sat, 14 Mar 2020 19:06:23 +0800 Subject: [PATCH] Polish code and demo of Sentinel Zuul 2.x adapter Signed-off-by: Eric Zhao --- .../sentinel-zuul2-adapter/README.md | 23 ++--- .../sentinel-zuul2-adapter/pom.xml | 1 + .../zuul2/HttpRequestMessageItemParser.java | 19 ++++ .../api/ZuulApiDefinitionChangeObserver.java | 2 +- .../api/ZuulGatewayApiMatcherManager.java | 1 + .../callback/DefaultRequestOriginParser.java | 30 ------ .../zuul2/callback/RequestOriginParser.java | 35 ------- .../callback/ZuulGatewayCallbackManager.java | 38 -------- ...stant.java => SentinelZuul2Constants.java} | 9 +- .../DefaultBlockFallbackProvider.java | 4 +- .../endpoint/SentinelZuulEndpoint.java | 10 +- .../inbound/SentinelZuulInboundFilter.java | 66 ++++++++----- .../outbound/SentinelZuulOutboundFilter.java | 30 ++---- .../demo/zuul2/gateway/Bootstrap.java | 96 ------------------- .../demo/zuul2/gateway/ZuulBootstrap.java | 68 +++++++++++++ 15 files changed, 159 insertions(+), 273 deletions(-) delete mode 100644 sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/DefaultRequestOriginParser.java delete mode 100644 sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/RequestOriginParser.java delete mode 100644 sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/ZuulGatewayCallbackManager.java rename sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/{ZuulConstant.java => SentinelZuul2Constants.java} (83%) delete mode 100644 sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/Bootstrap.java create mode 100644 sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/ZuulBootstrap.java diff --git a/sentinel-adapter/sentinel-zuul2-adapter/README.md b/sentinel-adapter/sentinel-zuul2-adapter/README.md index 943fcaf0..e0387ee5 100755 --- a/sentinel-adapter/sentinel-zuul2-adapter/README.md +++ b/sentinel-adapter/sentinel-zuul2-adapter/README.md @@ -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(); - } -} -``` \ No newline at end of file diff --git a/sentinel-adapter/sentinel-zuul2-adapter/pom.xml b/sentinel-adapter/sentinel-zuul2-adapter/pom.xml index 132f4b38..1fd9b29c 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/pom.xml +++ b/sentinel-adapter/sentinel-zuul2-adapter/pom.xml @@ -41,6 +41,7 @@ + org.springframework spring-core diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java index 990cfb95..8c3f3cff 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/HttpRequestMessageItemParser.java @@ -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 { @Override diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java index 7689647a..0cbaa4e4 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulApiDefinitionChangeObserver.java @@ -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 { diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java index 3afc74be..7c71724c 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/api/ZuulGatewayApiMatcherManager.java @@ -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 { diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/DefaultRequestOriginParser.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/DefaultRequestOriginParser.java deleted file mode 100644 index ed2271df..00000000 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/DefaultRequestOriginParser.java +++ /dev/null @@ -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 ""; - } -} diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/RequestOriginParser.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/RequestOriginParser.java deleted file mode 100644 index 2794b288..00000000 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/RequestOriginParser.java +++ /dev/null @@ -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); -} diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/ZuulGatewayCallbackManager.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/ZuulGatewayCallbackManager.java deleted file mode 100644 index c70abed4..00000000 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/callback/ZuulGatewayCallbackManager.java +++ /dev/null @@ -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() {} -} diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/ZuulConstant.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java similarity index 83% rename from sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/ZuulConstant.java rename to sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java index a3902efd..0e01d490 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/ZuulConstant.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/constants/SentinelZuul2Constants.java @@ -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() {} } diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java index 031e841a..222630d5 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/fallback/DefaultBlockFallbackProvider.java @@ -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); } diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java index 167dd296..67131274 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/endpoint/SentinelZuulEndpoint.java @@ -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()); diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java index 5d2601e8..ff174538 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/inbound/SentinelZuulInboundFilter.java @@ -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 routeExtractor; private final GatewayParamParser 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 routeExtractor) { + this(order, null, routeExtractor); + } - public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError) { + public SentinelZuulInboundFilter(int order, Executor executor, Function 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 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 apply(HttpRequestMessage request) { SessionContext context = request.getContext(); - String origin = parseOrigin(request); Deque 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 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 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 pickMatchingApiDefinitions(HttpRequestMessage message) { Set apis = new HashSet<>(); for (HttpRequestMessageApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) { diff --git a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java index 75f6a179..9946fc25 100644 --- a/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java +++ b/sentinel-adapter/sentinel-zuul2-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul2/filters/outbound/SentinelZuulOutboundFilter.java @@ -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. - *

- * 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 errors = context.getFilterErrors().stream() - .filter(e -> BlockException.isBlockException(e.getException())) - .collect(Collectors.toList()); - boolean notBlocked = true; - if (CollectionUtils.isEmpty(errors)) { - notBlocked = false; - } - Deque holders = (Deque) context.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); + boolean previousBlocked = context.getFilterErrors().stream() + .anyMatch(e -> BlockException.isBlockException(e.getException())); + Deque holders = (Deque) 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; } diff --git a/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/Bootstrap.java b/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/Bootstrap.java deleted file mode 100644 index 335c3505..00000000 --- a/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/Bootstrap.java +++ /dev/null @@ -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 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 definitions = new HashSet<>(); - ApiDefinition api1 = new ApiDefinition("some_customized_api") - .setPredicateItems(new HashSet() {{ - 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() {{ - 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(); - } - } -} diff --git a/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/ZuulBootstrap.java b/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/ZuulBootstrap.java new file mode 100644 index 00000000..cc39b766 --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul2-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul2/gateway/ZuulBootstrap.java @@ -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; + +/** + *

The Zuul 2.x demo with Sentinel gateway flow control.

+ *

Run with {@code -Dcsp.sentinel.api.type=1} to mark the demo as API gateway.

+ * + * @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(); + } + } +}