Polish code and demo of Sentinel Zuul 2.x adapter
Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
5b9865db1c
commit
0536fb6846
|
|
@ -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,24 +54,19 @@ 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());
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue