Add gateway adapter for Zuul 2.x (#1138)
- also add demo for Zuul 2.x adapter
This commit is contained in:
parent
05e3caf618
commit
5b9865db1c
|
|
@ -26,6 +26,7 @@
|
||||||
<module>sentinel-api-gateway-adapter-common</module>
|
<module>sentinel-api-gateway-adapter-common</module>
|
||||||
<module>sentinel-spring-cloud-gateway-adapter</module>
|
<module>sentinel-spring-cloud-gateway-adapter</module>
|
||||||
<module>sentinel-spring-webmvc-adapter</module>
|
<module>sentinel-spring-webmvc-adapter</module>
|
||||||
|
<module>sentinel-zuul2-adapter</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Sentinel Zuul 2.x Adapter
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
> You can refer to demo `sentinel-demo-zuul2-gateway`
|
||||||
|
|
||||||
|
1. Add Maven dependency to your `pom.xml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-zuul2-adapter</artifactId>
|
||||||
|
<version>x.y.z</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Register filters
|
||||||
|
|
||||||
|
```java
|
||||||
|
filterMultibinder.addBinding().toInstance(new SentinelZuulInboundFilter(500));
|
||||||
|
filterMultibinder.addBinding().toInstance(new SentinelZuulOutboundFilter(500));
|
||||||
|
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.
|
||||||
|
|
||||||
|
- `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.
|
||||||
|
- `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.
|
||||||
|
|
||||||
|
## Integration with Sentinel Dashboard
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Fallbacks
|
||||||
|
|
||||||
|
You can implement `ZuulBlockFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown.
|
||||||
|
The default fallback provider is `DefaultBlockFallbackProvider`.
|
||||||
|
|
||||||
|
By default fallback route is proxy ID (or customized API name).
|
||||||
|
|
||||||
|
Here is an example:
|
||||||
|
|
||||||
|
```java
|
||||||
|
|
||||||
|
// custom provider
|
||||||
|
public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||||
|
|
||||||
|
private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class);
|
||||||
|
|
||||||
|
// you can define root as service level
|
||||||
|
@Override
|
||||||
|
public String getRoute() {
|
||||||
|
return "my-route";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
||||||
|
RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
|
||||||
|
if (cause instanceof BlockException) {
|
||||||
|
return new BlockResponse(429, "Sentinel block exception", route);
|
||||||
|
} else {
|
||||||
|
return new BlockResponse(500, "System Error", route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register fallback
|
||||||
|
ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());
|
||||||
|
```
|
||||||
|
|
||||||
|
Default block response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code":429,
|
||||||
|
"message":"Sentinel block exception",
|
||||||
|
"route":"/"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>sentinel-adapter</artifactId>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<version>1.7.2-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>sentinel-zuul2-adapter</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.source.version>1.8</java.source.version>
|
||||||
|
<java.target.version>1.8</java.target.version>
|
||||||
|
<zuul.version>2.1.5</zuul.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.zuul</groupId>
|
||||||
|
<artifactId>zuul-core</artifactId>
|
||||||
|
<version>${zuul.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-core</artifactId>
|
||||||
|
<version>5.1.9.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class HttpRequestMessageItemParser implements RequestItemParser<HttpRequestMessage> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath(HttpRequestMessage request) {
|
||||||
|
return request.getInboundRequest().getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRemoteAddress(HttpRequestMessage request) {
|
||||||
|
return request.getOriginalHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(HttpRequestMessage request, String key) {
|
||||||
|
return String.valueOf(request.getInboundRequest().getHeaders().get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUrlParam(HttpRequestMessage request, String paramName) {
|
||||||
|
return String.valueOf(request.getInboundRequest().getQueryParams().get(paramName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCookieValue(HttpRequestMessage request, String cookieName) {
|
||||||
|
return String.valueOf(request.getInboundRequest().parseCookies().get(cookieName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.api;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Eric Zhao
|
||||||
|
* @since 1.6.0
|
||||||
|
*/
|
||||||
|
public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(Set<ApiDefinition> apiDefinitions) {
|
||||||
|
ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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.adapter.gateway.zuul2.api;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.matcher.HttpRequestMessageApiMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public final class ZuulGatewayApiMatcherManager {
|
||||||
|
|
||||||
|
private static final Map<String, HttpRequestMessageApiMatcher> API_MATCHER_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static Map<String, HttpRequestMessageApiMatcher> getApiMatcherMap() {
|
||||||
|
return Collections.unmodifiableMap(API_MATCHER_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpRequestMessageApiMatcher getMatcher(final String apiName) {
|
||||||
|
if (apiName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return API_MATCHER_MAP.get(apiName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<ApiDefinition> getApiDefinitionSet() {
|
||||||
|
Set<ApiDefinition> set = new HashSet<>();
|
||||||
|
for (HttpRequestMessageApiMatcher matcher : API_MATCHER_MAP.values()) {
|
||||||
|
set.add(matcher.getApiDefinition());
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
static synchronized void loadApiDefinitions(/*@Valid*/ Set<ApiDefinition> definitions) {
|
||||||
|
if (definitions == null || definitions.isEmpty()) {
|
||||||
|
API_MATCHER_MAP.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (ApiDefinition definition : definitions) {
|
||||||
|
addApiDefinition(definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addApiDefinition(ApiDefinition definition) {
|
||||||
|
API_MATCHER_MAP.put(definition.getApiName(), new HttpRequestMessageApiMatcher(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZuulGatewayApiMatcherManager() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.api.matcher;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.route.ZuulRouteMatchers;
|
||||||
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class HttpRequestMessageApiMatcher extends AbstractApiMatcher<HttpRequestMessage> {
|
||||||
|
|
||||||
|
public HttpRequestMessageApiMatcher(ApiDefinition apiDefinition) {
|
||||||
|
super(apiDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeMatchers() {
|
||||||
|
if (apiDefinition.getPredicateItems() != null) {
|
||||||
|
for (ApiPredicateItem item : apiDefinition.getPredicateItems()) {
|
||||||
|
Predicate<HttpRequestMessage> predicate = fromApiPredicate(item);
|
||||||
|
if (predicate != null) {
|
||||||
|
matchers.add(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<HttpRequestMessage> fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) {
|
||||||
|
if (item instanceof ApiPathPredicateItem) {
|
||||||
|
return fromApiPathPredicate((ApiPathPredicateItem)item);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Predicate<HttpRequestMessage> fromApiPathPredicate(/*@Valid*/ ApiPathPredicateItem item) {
|
||||||
|
String pattern = item.getPattern();
|
||||||
|
if (StringUtil.isBlank(pattern)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (item.getMatchStrategy()) {
|
||||||
|
case SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX:
|
||||||
|
return ZuulRouteMatchers.regexPath(pattern);
|
||||||
|
case SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX:
|
||||||
|
return ZuulRouteMatchers.antPath(pattern);
|
||||||
|
default:
|
||||||
|
return ZuulRouteMatchers.exactPath(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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.api.route;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class PrefixRoutePathMatcher implements Predicate<HttpRequestMessage> {
|
||||||
|
|
||||||
|
private final String pattern;
|
||||||
|
|
||||||
|
private final PathMatcher pathMatcher;
|
||||||
|
private final boolean canMatch;
|
||||||
|
|
||||||
|
public PrefixRoutePathMatcher(String pattern) {
|
||||||
|
AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.pathMatcher = new AntPathMatcher();
|
||||||
|
this.canMatch = pathMatcher.isPattern(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(HttpRequestMessage context) {
|
||||||
|
String path = context.getPath();
|
||||||
|
if (canMatch) {
|
||||||
|
return pathMatcher.match(pattern, path);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPattern() {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.route;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class RegexRoutePathMatcher implements Predicate<HttpRequestMessage> {
|
||||||
|
|
||||||
|
private final String pattern;
|
||||||
|
private final Pattern regex;
|
||||||
|
|
||||||
|
public RegexRoutePathMatcher(String pattern) {
|
||||||
|
AssertUtil.assertNotBlank(pattern, "pattern cannot be blank");
|
||||||
|
this.pattern = pattern;
|
||||||
|
this.regex = Pattern.compile(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test(HttpRequestMessage input) {
|
||||||
|
String path = input.getInboundRequest().getPath();
|
||||||
|
return regex.matcher(path).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPattern() {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.api.route;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public final class ZuulRouteMatchers {
|
||||||
|
|
||||||
|
public static Predicate<HttpRequestMessage> all() {
|
||||||
|
return requestContext -> true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<HttpRequestMessage> antPath(String pathPattern) {
|
||||||
|
return new PrefixRoutePathMatcher(pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<HttpRequestMessage> exactPath(final String path) {
|
||||||
|
return exchange -> exchange.getPath().equals(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Predicate<HttpRequestMessage> regexPath(String pathPattern) {
|
||||||
|
return new RegexRoutePathMatcher(pathPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ZuulRouteMatchers() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.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() {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class ZuulConstant {
|
||||||
|
/**
|
||||||
|
* Zuul use Sentinel as default context when serviceId is empty.
|
||||||
|
*/
|
||||||
|
public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context";
|
||||||
|
/**
|
||||||
|
* Zuul context key for keeping Sentinel entries.
|
||||||
|
*/
|
||||||
|
public static final String ZUUL_CTX_SENTINEL_ENTRIES_KEY = "_sentinel_entries";
|
||||||
|
|
||||||
|
public static final String ZUUL_CTX_SENTINEL_FALLBACK_ROUTE = "_sentinel_fallback_route";
|
||||||
|
/**
|
||||||
|
* Indicate if request is blocked .
|
||||||
|
*/
|
||||||
|
public static final String ZUUL_CTX_SENTINEL_BLOCKED_FLAG = "_sentinel_blocked_flag";
|
||||||
|
|
||||||
|
private ZuulConstant(){}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.fallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException}
|
||||||
|
*
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public class BlockResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP status code.
|
||||||
|
*/
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
private String route;
|
||||||
|
|
||||||
|
public BlockResponse(int code, String message, String route) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.route = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoute() {
|
||||||
|
return route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoute(String route) {
|
||||||
|
this.route = route;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{" +
|
||||||
|
"\"code\":" + code +
|
||||||
|
", \"message\":" + "\"" + message + "\"" +
|
||||||
|
", \"route\":" + "\"" + route + "\"" +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Fallback provider for sentinel {@link BlockException}, {@literal *} meant for all routes.
|
||||||
|
*
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public class DefaultBlockFallbackProvider implements ZuulBlockFallbackProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRoute() {
|
||||||
|
return "*";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
||||||
|
if (cause instanceof BlockException) {
|
||||||
|
return new BlockResponse(429, "Sentinel block exception", route);
|
||||||
|
} else {
|
||||||
|
return new BlockResponse(500, "System Error", route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provide fall back class manager.
|
||||||
|
*
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public class ZuulBlockFallbackManager {
|
||||||
|
|
||||||
|
private static Map<String, ZuulBlockFallbackProvider> fallbackProviderCache = new HashMap<>();
|
||||||
|
|
||||||
|
private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register special provider for different route.
|
||||||
|
*/
|
||||||
|
public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) {
|
||||||
|
AssertUtil.notNull(provider, "fallback provider cannot be null");
|
||||||
|
String route = provider.getRoute();
|
||||||
|
if ("*".equals(route) || route == null) {
|
||||||
|
defaultFallbackProvider = provider;
|
||||||
|
} else {
|
||||||
|
fallbackProviderCache.put(route, provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ZuulBlockFallbackProvider getFallbackProvider(String route) {
|
||||||
|
ZuulBlockFallbackProvider provider = fallbackProviderCache.get(route);
|
||||||
|
if (provider == null) {
|
||||||
|
provider = defaultFallbackProvider;
|
||||||
|
}
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void clear(){
|
||||||
|
fallbackProviderCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface is compatible for different spring cloud version.
|
||||||
|
*
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public interface ZuulBlockFallbackProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The route this fallback will be used for.
|
||||||
|
* @return The route the fallback will be used for.
|
||||||
|
*/
|
||||||
|
String getRoute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a fallback response based on the cause of the failed execution.
|
||||||
|
*
|
||||||
|
* @param route The route the fallback is for
|
||||||
|
* @param cause cause of the main method failure, may be <code>null</code>
|
||||||
|
* @return the fallback response
|
||||||
|
*/
|
||||||
|
BlockResponse fallbackResponse(String route, Throwable cause);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* 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.filters;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class EntryHolder {
|
||||||
|
|
||||||
|
final private Entry entry;
|
||||||
|
|
||||||
|
final private Object[] params;
|
||||||
|
|
||||||
|
public EntryHolder(Entry entry, Object[] params) {
|
||||||
|
this.entry = entry;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry getEntry() {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getParams() {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.filters.endpoint;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.constants.ZuulConstant;
|
||||||
|
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;
|
||||||
|
import com.netflix.zuul.message.http.HttpResponseMessage;
|
||||||
|
import com.netflix.zuul.message.http.HttpResponseMessageImpl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Endpoint for handling exception.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
BlockResponse response = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, throwable);
|
||||||
|
HttpResponseMessage resp = new HttpResponseMessageImpl(context, request, response.getCode());
|
||||||
|
resp.setBodyAsText(response.toString());
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* 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.filters.inbound;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.AsyncEntry;
|
||||||
|
import com.alibaba.csp.sentinel.EntryType;
|
||||||
|
import com.alibaba.csp.sentinel.SphU;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.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.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.StringUtil;
|
||||||
|
import com.netflix.zuul.context.SessionContext;
|
||||||
|
import com.netflix.zuul.filters.http.HttpInboundFilter;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
|
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zuul2 inboundFilter for Sentinel.
|
||||||
|
*
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class SentinelZuulInboundFilter extends HttpInboundFilter {
|
||||||
|
|
||||||
|
private static final String DEFAULT_BLOCK_ENDPOINT_NAME = SentinelZuulEndpoint.class.getCanonicalName();
|
||||||
|
|
||||||
|
private final int order;
|
||||||
|
|
||||||
|
private final String blockedEndpointName;
|
||||||
|
/**
|
||||||
|
* if executor is null, flow control action will do 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.
|
||||||
|
*/
|
||||||
|
private final boolean fastError;
|
||||||
|
|
||||||
|
private final GatewayParamParser<HttpRequestMessage> paramParser = new GatewayParamParser<>(
|
||||||
|
new HttpRequestMessageItemParser());
|
||||||
|
|
||||||
|
public SentinelZuulInboundFilter(int order) {
|
||||||
|
this(order, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SentinelZuulInboundFilter(int order, Executor executor) {
|
||||||
|
this(order, DEFAULT_BLOCK_ENDPOINT_NAME, executor, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SentinelZuulInboundFilter(int order, String blockedEndpointName, Executor executor, boolean fastError) {
|
||||||
|
this.order = order;
|
||||||
|
this.blockedEndpointName = blockedEndpointName;
|
||||||
|
this.executor = executor;
|
||||||
|
this.fastError = fastError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int filterOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Observable<HttpRequestMessage> applyAsync(HttpRequestMessage request) {
|
||||||
|
if (executor != null) {
|
||||||
|
return Observable.just(request).subscribeOn(Schedulers.from(executor)).flatMap(this::apply);
|
||||||
|
} else {
|
||||||
|
return Observable.just(request).flatMap(this::apply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Observable<HttpRequestMessage> apply(HttpRequestMessage request) {
|
||||||
|
SessionContext context = request.getContext();
|
||||||
|
String origin = parseOrigin(request);
|
||||||
|
Deque<EntryHolder> holders = new ArrayDeque<>();
|
||||||
|
String routeId = context.getRouteVIP();
|
||||||
|
String fallBackRoute = routeId;
|
||||||
|
try {
|
||||||
|
if (StringUtil.isNotBlank(routeId)) {
|
||||||
|
ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
for (String apiName : matchingApis) {
|
||||||
|
fallBackRoute = apiName;
|
||||||
|
doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, request, holders);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if (fastError) {
|
||||||
|
context.setShouldSendErrorResponse(true);
|
||||||
|
context.setErrorEndpoint(blockedEndpointName);
|
||||||
|
} else {
|
||||||
|
context.setEndpoint(blockedEndpointName);
|
||||||
|
}
|
||||||
|
return Observable.error(t);
|
||||||
|
} finally {
|
||||||
|
if (!holders.isEmpty()) {
|
||||||
|
context.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders);
|
||||||
|
}
|
||||||
|
// clear context to avoid another request use incorrect context
|
||||||
|
ContextUtil.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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()) {
|
||||||
|
if (matcher.test(message)) {
|
||||||
|
apis.add(matcher.getApiName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apis;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldFilter(HttpRequestMessage msg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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.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.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.
|
||||||
|
*
|
||||||
|
* @author wavesZh
|
||||||
|
*/
|
||||||
|
public class SentinelZuulOutboundFilter extends HttpOutboundFilter {
|
||||||
|
|
||||||
|
private final int order;
|
||||||
|
|
||||||
|
public SentinelZuulOutboundFilter(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int filterOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Observable<HttpResponseMessage> applyAsync(HttpResponseMessage input) {
|
||||||
|
return Observable.just(apply(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpResponseMessage apply(HttpResponseMessage response) {
|
||||||
|
SessionContext context = response.getContext();
|
||||||
|
|
||||||
|
if (context.get(ZuulConstant.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);
|
||||||
|
while (!holders.isEmpty()) {
|
||||||
|
EntryHolder holder = holders.pop();
|
||||||
|
if (notBlocked) {
|
||||||
|
Tracer.traceEntry(context.getError(), holder.getEntry());
|
||||||
|
}
|
||||||
|
holder.getEntry().exit(1, holder.getParams());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldFilter(HttpResponseMessage msg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
com.alibaba.csp.sentinel.adapter.gateway.zuul2.api.ZuulApiDefinitionChangeObserver
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public class ZuulBlockFallbackManagerTest {
|
||||||
|
|
||||||
|
private String ROUTE = "/test";
|
||||||
|
|
||||||
|
private String DEFAULT_ROUTE = "*";
|
||||||
|
|
||||||
|
class MyNullResponseFallBackProvider implements ZuulBlockFallbackProvider {
|
||||||
|
@Override
|
||||||
|
public String getRoute() {
|
||||||
|
return ROUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockResponse fallbackResponse(String route, Throwable cause) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegisterProvider() throws Exception {
|
||||||
|
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
|
||||||
|
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
||||||
|
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
||||||
|
Assert.assertNull(myNullResponseFallBackProvider.fallbackResponse(ROUTE, new FlowException("flow ex")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void clear() {
|
||||||
|
MyNullResponseFallBackProvider myNullResponseFallBackProvider = new MyNullResponseFallBackProvider();
|
||||||
|
ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider);
|
||||||
|
Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE);
|
||||||
|
ZuulBlockFallbackManager.clear();
|
||||||
|
Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.alibaba.csp.sentinel.adapter.gateway.zuul2.fallback;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author tiger
|
||||||
|
*/
|
||||||
|
public class ZuulBlockFallbackProviderTest {
|
||||||
|
|
||||||
|
private String ALL_ROUTE = "*";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetNullRoute() throws Exception {
|
||||||
|
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(null);
|
||||||
|
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDefaultRoute() throws Exception {
|
||||||
|
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||||
|
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetNotInCacheRoute() throws Exception {
|
||||||
|
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider("/not/in");
|
||||||
|
Assert.assertEquals(fallbackProvider.getRoute(), ALL_ROUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFlowControlFallbackResponse() throws Exception {
|
||||||
|
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||||
|
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE,
|
||||||
|
new FlowException("flow exception"));
|
||||||
|
Assert.assertEquals(clientHttpResponse.getCode(), 429);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRuntimeExceptionFallbackResponse() throws Exception {
|
||||||
|
ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE);
|
||||||
|
BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new RuntimeException());
|
||||||
|
Assert.assertEquals(clientHttpResponse.getCode(), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
<module>sentinel-demo-zuul-gateway</module>
|
<module>sentinel-demo-zuul-gateway</module>
|
||||||
<module>sentinel-demo-etcd-datasource</module>
|
<module>sentinel-demo-etcd-datasource</module>
|
||||||
<module>sentinel-demo-spring-webmvc</module>
|
<module>sentinel-demo-spring-webmvc</module>
|
||||||
|
<module>sentinel-demo-zuul2-gateway</module>
|
||||||
<module>sentinel-demo-log-logback</module>
|
<module>sentinel-demo-log-logback</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>sentinel-demo</artifactId>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<version>1.7.2-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>sentinel-demo-zuul2-gateway</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-zuul2-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.csp</groupId>
|
||||||
|
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.zuul</groupId>
|
||||||
|
<artifactId>zuul-core</artifactId>
|
||||||
|
<version>2.1.5</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.28</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
<version>1.7.28</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>4.5.8</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
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,34 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.netflix.zuul.filters.FilterRegistry;
|
||||||
|
import com.netflix.zuul.filters.ZuulFilter;
|
||||||
|
|
||||||
|
public class FiltersRegisteringService {
|
||||||
|
|
||||||
|
private final List<ZuulFilter> filters;
|
||||||
|
private final FilterRegistry filterRegistry;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FiltersRegisteringService(FilterRegistry filterRegistry, Set<ZuulFilter> filters) {
|
||||||
|
this.filters = new ArrayList<>(filters);
|
||||||
|
this.filterRegistry = filterRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ZuulFilter> getFilters() {
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initialize() {
|
||||||
|
for (ZuulFilter filter: filters) {
|
||||||
|
this.filterRegistry.put(filter.filterName(), filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway;
|
||||||
|
|
||||||
|
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.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class GatewayRuleConfig {
|
||||||
|
|
||||||
|
public void doInit() {
|
||||||
|
// Prepare some gateway rules and API definitions (only for demo).
|
||||||
|
// It's recommended to leverage dynamic data source or the Sentinel dashboard to push the rules.
|
||||||
|
initCustomizedApis();
|
||||||
|
initGatewayRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCustomizedApis() {
|
||||||
|
Set<ApiDefinition> definitions = new HashSet<>();
|
||||||
|
ApiDefinition api1 = new ApiDefinition("some_customized_api")
|
||||||
|
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
|
||||||
|
add(new ApiPathPredicateItem().setPattern("/images"));
|
||||||
|
add(new ApiPathPredicateItem().setPattern("/comments")
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGatewayRules() {
|
||||||
|
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||||
|
rules.add(new GatewayFlowRule("images")
|
||||||
|
.setCount(10)
|
||||||
|
.setIntervalSec(1)
|
||||||
|
);
|
||||||
|
rules.add(new GatewayFlowRule("images")
|
||||||
|
.setCount(2)
|
||||||
|
.setIntervalSec(2)
|
||||||
|
.setBurst(2)
|
||||||
|
.setParamItem(new GatewayParamFlowItem()
|
||||||
|
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
rules.add(new GatewayFlowRule("comments")
|
||||||
|
.setCount(3)
|
||||||
|
.setIntervalSec(1)
|
||||||
|
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||||
|
.setMaxQueueingTimeoutMs(6000)
|
||||||
|
.setParamItem(new GatewayParamFlowItem()
|
||||||
|
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||||
|
.setFieldName("X-Sentinel-Flag")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
rules.add(new GatewayFlowRule("comments")
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import com.netflix.appinfo.ApplicationInfoManager;
|
||||||
|
import com.netflix.config.DynamicIntProperty;
|
||||||
|
import com.netflix.discovery.EurekaClient;
|
||||||
|
import com.netflix.netty.common.accesslog.AccessLogPublisher;
|
||||||
|
import com.netflix.netty.common.channel.config.ChannelConfig;
|
||||||
|
import com.netflix.netty.common.channel.config.CommonChannelConfigKeys;
|
||||||
|
import com.netflix.netty.common.metrics.EventLoopGroupMetrics;
|
||||||
|
import com.netflix.netty.common.proxyprotocol.StripUntrustedProxyHeadersHandler;
|
||||||
|
import com.netflix.netty.common.ssl.ServerSslConfig;
|
||||||
|
import com.netflix.netty.common.status.ServerStatusManager;
|
||||||
|
import com.netflix.spectator.api.Registry;
|
||||||
|
import com.netflix.zuul.FilterLoader;
|
||||||
|
import com.netflix.zuul.FilterUsageNotifier;
|
||||||
|
import com.netflix.zuul.RequestCompleteHandler;
|
||||||
|
import com.netflix.zuul.context.SessionContextDecorator;
|
||||||
|
import com.netflix.zuul.netty.server.BaseServerStartup;
|
||||||
|
import com.netflix.zuul.netty.server.DirectMemoryMonitor;
|
||||||
|
import com.netflix.zuul.netty.server.ZuulServerChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.group.ChannelGroup;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class SampleServerStartup extends BaseServerStartup {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public SampleServerStartup(ServerStatusManager serverStatusManager, FilterLoader filterLoader, SessionContextDecorator sessionCtxDecorator, FilterUsageNotifier usageNotifier, RequestCompleteHandler reqCompleteHandler, Registry registry, DirectMemoryMonitor directMemoryMonitor, EventLoopGroupMetrics eventLoopGroupMetrics, EurekaClient discoveryClient, ApplicationInfoManager applicationInfoManager, AccessLogPublisher accessLogPublisher) {
|
||||||
|
super(serverStatusManager, filterLoader, sessionCtxDecorator, usageNotifier, reqCompleteHandler, registry, directMemoryMonitor, eventLoopGroupMetrics, discoveryClient, applicationInfoManager, accessLogPublisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<Integer, ChannelInitializer> choosePortsAndChannels(ChannelGroup clientChannels) {
|
||||||
|
Map<Integer, ChannelInitializer> portsToChannels = new HashMap<>();
|
||||||
|
int port = new DynamicIntProperty("zuul.server.port.main", 8085).get();
|
||||||
|
|
||||||
|
String mainPortName = "main";
|
||||||
|
ChannelConfig channelConfig = BaseServerStartup.defaultChannelConfig(mainPortName);
|
||||||
|
ServerSslConfig sslConfig;
|
||||||
|
/* These settings may need to be tweaked depending if you're running behind an ELB HTTP listener, TCP listener,
|
||||||
|
* or directly on the internet.
|
||||||
|
*/
|
||||||
|
ChannelConfig channelDependencies = defaultChannelDependencies(mainPortName);
|
||||||
|
|
||||||
|
channelConfig.set(CommonChannelConfigKeys.allowProxyHeadersWhen, StripUntrustedProxyHeadersHandler.AllowWhen.ALWAYS);
|
||||||
|
channelConfig.set(CommonChannelConfigKeys.preferProxyProtocolForClientIp, false);
|
||||||
|
channelConfig.set(CommonChannelConfigKeys.isSSlFromIntermediary, false);
|
||||||
|
channelConfig.set(CommonChannelConfigKeys.withProxyProtocol, false);
|
||||||
|
|
||||||
|
portsToChannels.put(port, new ZuulServerChannelInitializer(port, channelConfig, channelDependencies, clientChannels));
|
||||||
|
logPortConfigured(port, null);
|
||||||
|
|
||||||
|
return portsToChannels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway;
|
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.endpoint.SentinelZuulEndpoint;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.inbound.SentinelZuulInboundFilter;
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.outbound.SentinelZuulOutboundFilter;
|
||||||
|
import com.alibaba.csp.sentinel.demo.zuul2.gateway.filters.Route;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.google.inject.multibindings.Multibinder;
|
||||||
|
import com.netflix.zuul.BasicFilterUsageNotifier;
|
||||||
|
import com.netflix.zuul.DynamicCodeCompiler;
|
||||||
|
import com.netflix.zuul.FilterFactory;
|
||||||
|
import com.netflix.zuul.FilterUsageNotifier;
|
||||||
|
import com.netflix.zuul.filters.ZuulFilter;
|
||||||
|
import com.netflix.zuul.groovy.GroovyCompiler;
|
||||||
|
import com.netflix.zuul.guice.GuiceFilterFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public class ZuulClasspathFiltersModule extends AbstractModule {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
bind(DynamicCodeCompiler.class).to(GroovyCompiler.class);
|
||||||
|
bind(FilterFactory.class).to(GuiceFilterFactory.class);
|
||||||
|
|
||||||
|
bind(FilterUsageNotifier.class).to(BasicFilterUsageNotifier.class);
|
||||||
|
|
||||||
|
Multibinder<ZuulFilter> filterMultibinder = Multibinder.newSetBinder(binder(), ZuulFilter.class);
|
||||||
|
filterMultibinder.addBinding().toInstance(new SentinelZuulInboundFilter(500));
|
||||||
|
filterMultibinder.addBinding().toInstance(new SentinelZuulOutboundFilter(500));
|
||||||
|
filterMultibinder.addBinding().toInstance(new SentinelZuulEndpoint());
|
||||||
|
filterMultibinder.addBinding().toInstance(new Route());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway;
|
||||||
|
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
|
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
|
||||||
|
import com.netflix.discovery.DiscoveryClient;
|
||||||
|
import com.netflix.netty.common.accesslog.AccessLogPublisher;
|
||||||
|
import com.netflix.netty.common.status.ServerStatusManager;
|
||||||
|
import com.netflix.spectator.api.DefaultRegistry;
|
||||||
|
import com.netflix.spectator.api.Registry;
|
||||||
|
import com.netflix.zuul.BasicRequestCompleteHandler;
|
||||||
|
import com.netflix.zuul.FilterFileManager;
|
||||||
|
import com.netflix.zuul.RequestCompleteHandler;
|
||||||
|
import com.netflix.zuul.context.SessionContextDecorator;
|
||||||
|
import com.netflix.zuul.context.ZuulSessionContextDecorator;
|
||||||
|
import com.netflix.zuul.init.ZuulFiltersModule;
|
||||||
|
import com.netflix.zuul.netty.server.BaseServerStartup;
|
||||||
|
import com.netflix.zuul.netty.server.ClientRequestReceiver;
|
||||||
|
import com.netflix.zuul.origins.BasicNettyOriginManager;
|
||||||
|
import com.netflix.zuul.origins.OriginManager;
|
||||||
|
import com.netflix.zuul.stats.BasicRequestMetricsPublisher;
|
||||||
|
import com.netflix.zuul.stats.RequestMetricsPublisher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zuul Sample Module
|
||||||
|
*
|
||||||
|
* Author: Arthur Gonigberg
|
||||||
|
* Date: November 20, 2017
|
||||||
|
*/
|
||||||
|
public class ZuulSampleModule extends AbstractModule {
|
||||||
|
@Override
|
||||||
|
protected void configure() {
|
||||||
|
// sample specific bindings
|
||||||
|
bind(BaseServerStartup.class).to(SampleServerStartup.class);
|
||||||
|
|
||||||
|
// use provided basic netty origin manager
|
||||||
|
bind(OriginManager.class).to(BasicNettyOriginManager.class);
|
||||||
|
|
||||||
|
// zuul filter loading
|
||||||
|
install(new ZuulFiltersModule());
|
||||||
|
bind(FilterFileManager.class).asEagerSingleton();
|
||||||
|
|
||||||
|
install(new ZuulClasspathFiltersModule());
|
||||||
|
// general server bindings
|
||||||
|
// health/discovery status
|
||||||
|
bind(ServerStatusManager.class);
|
||||||
|
// decorate new sessions when requests come in
|
||||||
|
bind(SessionContextDecorator.class).to(ZuulSessionContextDecorator.class);
|
||||||
|
// atlas metrics registry
|
||||||
|
bind(Registry.class).to(DefaultRegistry.class);
|
||||||
|
// metrics post-request completion
|
||||||
|
bind(RequestCompleteHandler.class).to(BasicRequestCompleteHandler.class);
|
||||||
|
// discovery client
|
||||||
|
bind(AbstractDiscoveryClientOptionalArgs.class).to(DiscoveryClient.DiscoveryClientOptionalArgs.class);
|
||||||
|
// timings publisher
|
||||||
|
bind(RequestMetricsPublisher.class).to(BasicRequestMetricsPublisher.class);
|
||||||
|
|
||||||
|
// access logger, including request ID generator
|
||||||
|
bind(AccessLogPublisher.class).toInstance(new AccessLogPublisher("ACCESS",
|
||||||
|
(channel, httpRequest) -> ClientRequestReceiver.getRequestFromChannel(channel).getContext().getUUID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway.filters;
|
||||||
|
|
||||||
|
import com.netflix.zuul.filters.http.HttpSyncEndpoint;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
import com.netflix.zuul.message.http.HttpResponseMessage;
|
||||||
|
import com.netflix.zuul.message.http.HttpResponseMessageImpl;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
|
public class NotFoundEndpoint extends HttpSyncEndpoint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage apply(HttpRequestMessage request) {
|
||||||
|
HttpResponseMessage response = new HttpResponseMessageImpl(request.getContext(), request, HttpStatus.SC_NOT_FOUND);
|
||||||
|
response.finishBufferedBodyIfIncomplete();
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.alibaba.csp.sentinel.demo.zuul2.gateway.filters;
|
||||||
|
|
||||||
|
import com.netflix.zuul.context.SessionContext;
|
||||||
|
import com.netflix.zuul.filters.http.HttpInboundSyncFilter;
|
||||||
|
import com.netflix.zuul.message.http.HttpRequestMessage;
|
||||||
|
import com.netflix.zuul.netty.filter.ZuulEndPointRunner;
|
||||||
|
|
||||||
|
public class Route extends HttpInboundSyncFilter {
|
||||||
|
@Override
|
||||||
|
public HttpRequestMessage apply(HttpRequestMessage request) {
|
||||||
|
SessionContext context = request.getContext();
|
||||||
|
switch (request.getPath()) {
|
||||||
|
case "/images":
|
||||||
|
context.setEndpoint(ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME);
|
||||||
|
context.setRouteVIP("images");
|
||||||
|
break;
|
||||||
|
case "/comments":
|
||||||
|
context.setEndpoint(ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME);
|
||||||
|
context.setRouteVIP("comments");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
context.setEndpoint(NotFoundEndpoint.class.getCanonicalName());
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int filterOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldFilter(HttpRequestMessage msg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
zuul.server.port.main=8887
|
||||||
|
|
||||||
|
# Deactivate Eureka
|
||||||
|
eureka.registration.enabled = false
|
||||||
|
eureka.preferSameZone = false
|
||||||
|
eureka.shouldUseDns = false
|
||||||
|
eureka.shouldFetchRegistry=false
|
||||||
|
|
||||||
|
# Loading Filters
|
||||||
|
zuul.filters.packages = com.netflix.zuul.filters.common,com.alibaba.csp.sentinel.adapter.gateway.zuul2.filters.endpoint,com.alibaba.csp.sentinel.demo.zuul2.gateway.filters
|
||||||
|
|
||||||
|
# Routing to proxied back-end services
|
||||||
|
comments.ribbon.listOfServers=localhost:8081
|
||||||
|
comments.ribbon.client.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
|
||||||
|
images.ribbon.listOfServers=localhost:8082
|
||||||
|
images.ribbon.client.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
log4j.rootLogger=INFO,stdout
|
||||||
|
|
||||||
|
# stdout
|
||||||
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.stdout.layout=com.netflix.zuul.logging.FilteredPatternLayout
|
||||||
|
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c [%t] %m%n
|
||||||
Loading…
Reference in New Issue