Add sentinel-api-gateway-adapter-common module for universal gateway rule and API definition management
Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
5f713bd404
commit
faceb5f419
|
|
@ -22,6 +22,7 @@
|
|||
<module>sentinel-zuul-adapter</module>
|
||||
<module>sentinel-reactor-adapter</module>
|
||||
<module>sentinel-spring-webflux-adapter</module>
|
||||
<module>sentinel-api-gateway-adapter-common</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
@ -46,6 +47,11 @@
|
|||
<artifactId>sentinel-reactor-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# Sentinel API Gateway Adapter Common
|
||||
|
||||
The `sentinel-api-gateway-adapter-common` module provides common abstraction for
|
||||
API gateway flow control:
|
||||
|
||||
- `GatewayFlowRule`: flow control rule specific for route or API defined in API gateway.
|
||||
This can be automatically converted to `FlowRule` or `ParamFlowRule`.
|
||||
- `ApiDefinition`: gateway API definition with a group of predicates
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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.6.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.source.version>1.7</java.source.version>
|
||||
<java.target.version>1.7</java.target.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.common;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class SentinelGatewayConstants {
|
||||
|
||||
public static final int APP_TYPE_GATEWAY = 1;
|
||||
|
||||
public static final int RESOURCE_MODE_ROUTE_ID = 0;
|
||||
public static final int RESOURCE_MODE_CUSTOM_API_NAME = 1;
|
||||
|
||||
public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
|
||||
public static final int PARAM_PARSE_STRATEGY_HOST = 1;
|
||||
public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
|
||||
public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
|
||||
|
||||
public static final int PARAM_MATCH_STRATEGY_EXACT = 0;
|
||||
public static final int PARAM_MATCH_STRATEGY_PREFIX = 1;
|
||||
public static final int PARAM_MATCH_STRATEGY_REGEX = 2;
|
||||
|
||||
public static final String GATEWAY_CONTEXT_DEFAULT = "sentinel_gateway_context_default";
|
||||
public static final String GATEWAY_CONTEXT_PREFIX = "sentinel_gateway_context$$";
|
||||
public static final String GATEWAY_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$";
|
||||
|
||||
public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match";
|
||||
|
||||
private SentinelGatewayConstants() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.common.api;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiDefinition {
|
||||
|
||||
private String apiName;
|
||||
private Set<ApiPredicateItem> predicateItems;
|
||||
|
||||
public ApiDefinition() {}
|
||||
|
||||
public ApiDefinition(String apiName) {
|
||||
this.apiName = apiName;
|
||||
}
|
||||
|
||||
public String getApiName() {
|
||||
return apiName;
|
||||
}
|
||||
|
||||
public ApiDefinition setApiName(String apiName) {
|
||||
this.apiName = apiName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<ApiPredicateItem> getPredicateItems() {
|
||||
return predicateItems;
|
||||
}
|
||||
|
||||
public ApiDefinition setPredicateItems(Set<ApiPredicateItem> predicateItems) {
|
||||
this.predicateItems = predicateItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
ApiDefinition that = (ApiDefinition)o;
|
||||
|
||||
if (!Objects.equals(apiName, that.apiName)) { return false; }
|
||||
return Objects.equals(predicateItems, that.predicateItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = apiName != null ? apiName.hashCode() : 0;
|
||||
result = 31 * result + (predicateItems != null ? predicateItems.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiDefinition{" +
|
||||
"apiName='" + apiName + '\'' +
|
||||
", predicateItems=" + predicateItems +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface ApiDefinitionChangeObserver {
|
||||
|
||||
/**
|
||||
* Notify the observer about the new gateway API definitions.
|
||||
*
|
||||
* @param apiDefinitions new set of gateway API definition
|
||||
*/
|
||||
void onChange(Set<ApiDefinition> apiDefinitions);
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.common.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiPathPredicateItem implements ApiPredicateItem {
|
||||
|
||||
private String pattern;
|
||||
private int matchStrategy;
|
||||
|
||||
public ApiPathPredicateItem setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiPathPredicateItem setMatchStrategy(int matchStrategy) {
|
||||
this.matchStrategy = matchStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public int getMatchStrategy() {
|
||||
return matchStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
ApiPathPredicateItem that = (ApiPathPredicateItem)o;
|
||||
|
||||
if (matchStrategy != that.matchStrategy) { return false; }
|
||||
return Objects.equals(pattern, that.pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = pattern != null ? pattern.hashCode() : 0;
|
||||
result = 31 * result + matchStrategy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiPathPredicateItem{" +
|
||||
"pattern='" + pattern + '\'' +
|
||||
", matchStrategy=" + matchStrategy +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiPredicateGroupItem implements ApiPredicateItem {
|
||||
|
||||
private final Set<ApiPredicateItem> items = new HashSet<>();
|
||||
|
||||
public ApiPredicateGroupItem addItem(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
items.add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<ApiPredicateItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public ApiPredicateItem and(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
return this.addItem(item);
|
||||
}*/
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.common.api;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface ApiPredicateItem {
|
||||
|
||||
/**
|
||||
* Combine two {@link ApiPredicateItem}.
|
||||
*
|
||||
* @param item another predicate item
|
||||
* @return combined predicate group item
|
||||
*/
|
||||
/*default ApiPredicateItem and(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
return new ApiPredicateGroupItem()
|
||||
.addItem(this).addItem(item);
|
||||
}*/
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* 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.common.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Manager for gateway API definitions.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class GatewayApiDefinitionManager {
|
||||
|
||||
private static final Map<String, ApiDefinition> API_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ApiDefinitionPropertyListener LISTENER = new ApiDefinitionPropertyListener();
|
||||
private static SentinelProperty<Set<ApiDefinition>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
||||
/**
|
||||
* The map keeps all found ApiDefinitionChangeObserver (class name as key).
|
||||
*/
|
||||
private static final Map<String, ApiDefinitionChangeObserver> API_CHANGE_OBSERVERS = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
currentProperty.addListener(LISTENER);
|
||||
initializeApiChangeObserverSpi();
|
||||
} catch (Throwable ex) {
|
||||
RecordLog.warn("[GatewayApiDefinitionManager] Failed to initialize", ex);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeApiChangeObserverSpi() {
|
||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.loadInstanceList(ApiDefinitionChangeObserver.class);
|
||||
for (ApiDefinitionChangeObserver e : listeners) {
|
||||
API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {0}",
|
||||
e.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void register2Property(SentinelProperty<Set<ApiDefinition>> property) {
|
||||
AssertUtil.notNull(property, "property cannot be null");
|
||||
synchronized (LISTENER) {
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Registering new property to gateway API definition manager");
|
||||
currentProperty.removeListener(LISTENER);
|
||||
property.addListener(LISTENER);
|
||||
currentProperty = property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load given gateway API definitions and apply to downstream observers.
|
||||
*
|
||||
* @param apiDefinitions set of gateway API definitions
|
||||
* @return true if updated, or else false
|
||||
*/
|
||||
public static boolean loadApiDefinitions(Set<ApiDefinition> apiDefinitions) {
|
||||
return currentProperty.updateValue(apiDefinitions);
|
||||
}
|
||||
|
||||
public static ApiDefinition getApiDefinition(final String apiName) {
|
||||
if (apiName == null) {
|
||||
return null;
|
||||
}
|
||||
return API_MAP.get(apiName);
|
||||
}
|
||||
|
||||
public static Set<ApiDefinition> getApiDefinitions() {
|
||||
return new HashSet<>(API_MAP.values());
|
||||
}
|
||||
|
||||
private static final class ApiDefinitionPropertyListener implements PropertyListener<Set<ApiDefinition>> {
|
||||
|
||||
@Override
|
||||
public void configUpdate(Set<ApiDefinition> set) {
|
||||
applyApiUpdateInternal(set);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: " + API_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(Set<ApiDefinition> set) {
|
||||
applyApiUpdateInternal(set);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Api definition loaded: " + API_MAP);
|
||||
}
|
||||
|
||||
private static synchronized void applyApiUpdateInternal(Set<ApiDefinition> set) {
|
||||
if (set == null || set.isEmpty()) {
|
||||
API_MAP.clear();
|
||||
notifyDownstreamListeners(new HashSet<ApiDefinition>());
|
||||
return;
|
||||
}
|
||||
Map<String, ApiDefinition> map = new HashMap<>(set.size());
|
||||
Set<ApiDefinition> validSet = new HashSet<>();
|
||||
for (ApiDefinition definition : set) {
|
||||
if (isValidApi(definition)) {
|
||||
map.put(definition.getApiName(), definition);
|
||||
validSet.add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
API_MAP.clear();
|
||||
API_MAP.putAll(map);
|
||||
|
||||
// propagate to downstream.
|
||||
notifyDownstreamListeners(validSet);
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyDownstreamListeners(/*@Valid*/ final Set<ApiDefinition> definitions) {
|
||||
try {
|
||||
for (Map.Entry<?, ApiDefinitionChangeObserver> entry : API_CHANGE_OBSERVERS.entrySet()) {
|
||||
entry.getValue().onChange(definitions);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[GatewayApiDefinitionManager] WARN: failed to notify downstream api listeners", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidApi(ApiDefinition apiDefinition) {
|
||||
return apiDefinition != null && StringUtil.isNotBlank(apiDefinition.getApiName())
|
||||
&& apiDefinition.getPredicateItems() != null;
|
||||
}
|
||||
|
||||
static void addApiChangeListener(ApiDefinitionChangeObserver listener) {
|
||||
AssertUtil.notNull(listener, "listener cannot be null");
|
||||
API_CHANGE_OBSERVERS.put(listener.getClass().getCanonicalName(), listener);
|
||||
}
|
||||
|
||||
static void removeApiChangeListener(Class<?> clazz) {
|
||||
AssertUtil.notNull(clazz, "class cannot be null");
|
||||
API_CHANGE_OBSERVERS.remove(clazz.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.common.api.matcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public abstract class AbstractApiMatcher<T> implements Predicate<T> {
|
||||
|
||||
protected final String apiName;
|
||||
protected final ApiDefinition apiDefinition;
|
||||
/**
|
||||
* We use {@link com.alibaba.csp.sentinel.util.function.Predicate} here as the min JDK version is 1.7.
|
||||
*/
|
||||
protected final Set<Predicate<T>> matchers = new HashSet<>();
|
||||
|
||||
public AbstractApiMatcher(ApiDefinition apiDefinition) {
|
||||
AssertUtil.notNull(apiDefinition, "apiDefinition cannot be null");
|
||||
AssertUtil.assertNotBlank(apiDefinition.getApiName(), "apiName cannot be empty");
|
||||
this.apiName = apiDefinition.getApiName();
|
||||
this.apiDefinition = apiDefinition;
|
||||
|
||||
try {
|
||||
initializeMatchers();
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[GatewayApiMatcher] Failed to initialize internal matchers", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the matchers.
|
||||
*/
|
||||
protected abstract void initializeMatchers();
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> matcher : matchers) {
|
||||
if (matcher.test(t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getApiName() {
|
||||
return apiName;
|
||||
}
|
||||
|
||||
public ApiDefinition getApiDefinition() {
|
||||
return apiDefinition;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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.common.param;
|
||||
|
||||
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.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.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayParamParser<T> {
|
||||
|
||||
private final RequestItemParser<T> requestItemParser;
|
||||
|
||||
public GatewayParamParser(RequestItemParser<T> requestItemParser) {
|
||||
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
|
||||
this.requestItemParser = requestItemParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameters for given resource from the request entity on condition of the rule predicate.
|
||||
*
|
||||
* @param resource valid resource name
|
||||
* @param request valid request
|
||||
* @param rulePredicate rule predicate indicating the rules to refer
|
||||
* @return the parameter array
|
||||
*/
|
||||
public Object[] parseParameterFor(String resource, T request, Predicate<GatewayFlowRule> rulePredicate) {
|
||||
if (StringUtil.isEmpty(resource) || request == null || rulePredicate == null) {
|
||||
return new Object[0];
|
||||
}
|
||||
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
|
||||
Set<Boolean> predSet = new HashSet<>();
|
||||
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
|
||||
if (rule.getParamItem() != null) {
|
||||
gatewayRules.add(rule);
|
||||
predSet.add(rulePredicate.test(rule));
|
||||
}
|
||||
}
|
||||
if (gatewayRules.isEmpty()) {
|
||||
return new Object[0];
|
||||
}
|
||||
if (predSet.size() != 1 || predSet.contains(false)) {
|
||||
return new Object[0];
|
||||
}
|
||||
Object[] arr = new Object[gatewayRules.size()];
|
||||
for (GatewayFlowRule rule : gatewayRules) {
|
||||
GatewayParamFlowItem paramItem = rule.getParamItem();
|
||||
int idx = paramItem.getIndex();
|
||||
String param = parseInternal(paramItem, request);
|
||||
arr[idx] = param;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private String parseInternal(GatewayParamFlowItem item, T request) {
|
||||
switch (item.getParseStrategy()) {
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP:
|
||||
return parseClientIp(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST:
|
||||
return parseHost(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER:
|
||||
return parseHeader(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
|
||||
return parseUrlParameter(item, request);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String clientIp = requestItemParser.getRemoteAddress(request);
|
||||
String pattern = item.getPattern();
|
||||
if (pattern == null) {
|
||||
return clientIp;
|
||||
}
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
|
||||
}
|
||||
|
||||
private String parseHeader(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String headerKey = item.getFieldName();
|
||||
String pattern = item.getPattern();
|
||||
// TODO: what if the header has multiple values?
|
||||
String headerValue = requestItemParser.getHeader(request, headerKey);
|
||||
if (pattern == null) {
|
||||
return headerValue;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), headerValue, pattern);
|
||||
}
|
||||
|
||||
private String parseHost(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String pattern = item.getPattern();
|
||||
String host = requestItemParser.getHeader(request, "Host");
|
||||
if (pattern == null) {
|
||||
return host;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), host, pattern);
|
||||
}
|
||||
|
||||
private String parseUrlParameter(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String paramName = item.getFieldName();
|
||||
String pattern = item.getPattern();
|
||||
String param = requestItemParser.getUrlParam(request, paramName);
|
||||
if (pattern == null) {
|
||||
return param;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
||||
}
|
||||
|
||||
private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
|
||||
// TODO: implement here.
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (matchStrategy == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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.common.param;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface RequestItemParser<T> {
|
||||
|
||||
/**
|
||||
* Get API path from the request.
|
||||
*
|
||||
* @param request valid request
|
||||
* @return API path
|
||||
*/
|
||||
String getPath(T request);
|
||||
|
||||
/**
|
||||
* Get remote address from the request.
|
||||
*
|
||||
* @param request valid request
|
||||
* @return remote address
|
||||
*/
|
||||
String getRemoteAddress(T request);
|
||||
|
||||
/**
|
||||
* Get the header associated with the header key.
|
||||
*
|
||||
* @param request valid request
|
||||
* @param key valid header key
|
||||
* @return the header
|
||||
*/
|
||||
String getHeader(T request, String key);
|
||||
|
||||
/**
|
||||
* Get the parameter value associated with the parameter name.
|
||||
*
|
||||
* @param request valid request
|
||||
* @param paramName valid parameter name
|
||||
* @return the parameter value
|
||||
*/
|
||||
String getUrlParam(T request, String paramName);
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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.common.rule;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayFlowRule {
|
||||
|
||||
private String resource;
|
||||
private int resourceMode = SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID;
|
||||
|
||||
private int grade = RuleConstant.FLOW_GRADE_QPS;
|
||||
private double count;
|
||||
private long intervalSec = 1;
|
||||
|
||||
private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
|
||||
private int burst;
|
||||
/**
|
||||
* For throttle (rate limiting with queueing).
|
||||
*/
|
||||
private int maxQueueingTimeoutMs = 500;
|
||||
|
||||
/**
|
||||
* For parameter flow control. If not set, the gateway rule will be
|
||||
* converted to normal flow rule.
|
||||
*/
|
||||
private GatewayParamFlowItem paramItem;
|
||||
|
||||
public GatewayFlowRule() {}
|
||||
|
||||
public GatewayFlowRule(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setResource(String resource) {
|
||||
this.resource = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getResourceMode() {
|
||||
return resourceMode;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setResourceMode(int resourceMode) {
|
||||
this.resourceMode = resourceMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getGrade() {
|
||||
return grade;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setGrade(int grade) {
|
||||
this.grade = grade;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getControlBehavior() {
|
||||
return controlBehavior;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setControlBehavior(int controlBehavior) {
|
||||
this.controlBehavior = controlBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setCount(double count) {
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getIntervalSec() {
|
||||
return intervalSec;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setIntervalSec(long intervalSec) {
|
||||
this.intervalSec = intervalSec;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBurst() {
|
||||
return burst;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setBurst(int burst) {
|
||||
this.burst = burst;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem getParamItem() {
|
||||
return paramItem;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setParamItem(GatewayParamFlowItem paramItem) {
|
||||
this.paramItem = paramItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxQueueingTimeoutMs() {
|
||||
return maxQueueingTimeoutMs;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setMaxQueueingTimeoutMs(int maxQueueingTimeoutMs) {
|
||||
this.maxQueueingTimeoutMs = maxQueueingTimeoutMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
GatewayFlowRule rule = (GatewayFlowRule)o;
|
||||
|
||||
if (resourceMode != rule.resourceMode) { return false; }
|
||||
if (grade != rule.grade) { return false; }
|
||||
if (Double.compare(rule.count, count) != 0) { return false; }
|
||||
if (intervalSec != rule.intervalSec) { return false; }
|
||||
if (controlBehavior != rule.controlBehavior) { return false; }
|
||||
if (burst != rule.burst) { return false; }
|
||||
if (maxQueueingTimeoutMs != rule.maxQueueingTimeoutMs) { return false; }
|
||||
if (!Objects.equals(resource, rule.resource)) { return false; }
|
||||
return Objects.equals(paramItem, rule.paramItem);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = resource != null ? resource.hashCode() : 0;
|
||||
result = 31 * result + resourceMode;
|
||||
result = 31 * result + grade;
|
||||
temp = Double.doubleToLongBits(count);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
result = 31 * result + (int)(intervalSec ^ (intervalSec >>> 32));
|
||||
result = 31 * result + controlBehavior;
|
||||
result = 31 * result + burst;
|
||||
result = 31 * result + maxQueueingTimeoutMs;
|
||||
result = 31 * result + (paramItem != null ? paramItem.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GatewayFlowRule{" +
|
||||
"resource='" + resource + '\'' +
|
||||
", resourceMode=" + resourceMode +
|
||||
", grade=" + grade +
|
||||
", count=" + count +
|
||||
", intervalSec=" + intervalSec +
|
||||
", controlBehavior=" + controlBehavior +
|
||||
", burst=" + burst +
|
||||
", maxQueueingTimeoutMs=" + maxQueueingTimeoutMs +
|
||||
", paramItem=" + paramItem +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayParamFlowItem {
|
||||
|
||||
/**
|
||||
* Should be set when applying to parameter flow rules.
|
||||
*/
|
||||
private Integer index;
|
||||
|
||||
/**
|
||||
* Strategy for parsing item (e.g. client IP, arbitrary headers and URL parameters).
|
||||
*/
|
||||
private int parseStrategy;
|
||||
/**
|
||||
* Field to get (only required for arbitrary headers or URL parameters mode).
|
||||
*/
|
||||
private String fieldName;
|
||||
/**
|
||||
* Matching pattern. If not set, all values will be kept in LRU map.
|
||||
*/
|
||||
private String pattern;
|
||||
/**
|
||||
* Matching strategy for item value.
|
||||
*/
|
||||
private int matchStrategy = SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT;
|
||||
|
||||
public Integer getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
GatewayParamFlowItem setIndex(Integer index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getParseStrategy() {
|
||||
return parseStrategy;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setParseStrategy(int parseStrategy) {
|
||||
this.parseStrategy = parseStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMatchStrategy() {
|
||||
return matchStrategy;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setMatchStrategy(int matchStrategy) {
|
||||
this.matchStrategy = matchStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GatewayParamFlowItem{" +
|
||||
"index=" + index +
|
||||
", parseStrategy=" + parseStrategy +
|
||||
", fieldName='" + fieldName + '\'' +
|
||||
", pattern='" + pattern + '\'' +
|
||||
", matchStrategy=" + matchStrategy +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
final class GatewayRuleConverter {
|
||||
|
||||
static FlowRule toFlowRule(/*@Valid*/ GatewayFlowRule rule) {
|
||||
return new FlowRule(rule.getResource())
|
||||
.setControlBehavior(rule.getControlBehavior())
|
||||
.setCount(rule.getCount())
|
||||
.setGrade(rule.getGrade())
|
||||
.setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gateway rule to parameter flow rule, then apply the generated
|
||||
* parameter index to {@link GatewayParamFlowItem} of the rule.
|
||||
*
|
||||
* @param gatewayRule a valid gateway rule that should contain valid parameter items
|
||||
* @param idx generated parameter index (callers should guarantee it's unique and incremental)
|
||||
* @return converted parameter flow rule
|
||||
*/
|
||||
static ParamFlowRule applyToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
|
||||
ParamFlowRule paramRule = new ParamFlowRule(gatewayRule.getResource())
|
||||
.setCount(gatewayRule.getCount())
|
||||
.setGrade(gatewayRule.getGrade())
|
||||
.setDurationInSec(gatewayRule.getIntervalSec())
|
||||
.setBurstCount(gatewayRule.getBurst())
|
||||
.setControlBehavior(gatewayRule.getControlBehavior())
|
||||
.setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
|
||||
.setParamIdx(idx);
|
||||
GatewayParamFlowItem gatewayItem = gatewayRule.getParamItem();
|
||||
// Apply the current idx to gateway rule item.
|
||||
gatewayItem.setIndex(idx);
|
||||
// TODO: implement for pattern-based parameters.
|
||||
return paramRule;
|
||||
}
|
||||
|
||||
private GatewayRuleConverter() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* 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.common.rule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class GatewayRuleManager {
|
||||
|
||||
private static final Map<String, Set<GatewayFlowRule>> RULE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
|
||||
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
||||
static {
|
||||
currentProperty.addListener(LISTENER);
|
||||
}
|
||||
|
||||
public static void register2Property(SentinelProperty<Set<GatewayFlowRule>> property) {
|
||||
AssertUtil.notNull(property, "property cannot be null");
|
||||
synchronized (LISTENER) {
|
||||
RecordLog.info("[GatewayRuleManager] Registering new property to gateway flow rule manager");
|
||||
currentProperty.removeListener(LISTENER);
|
||||
property.addListener(LISTENER);
|
||||
currentProperty = property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all provided gateway rules into memory, while
|
||||
* previous rules will be replaced.
|
||||
*
|
||||
* @param rules rule set
|
||||
* @return true if updated, otherwise false
|
||||
*/
|
||||
public static boolean loadRules(Set<GatewayFlowRule> rules) {
|
||||
return currentProperty.updateValue(rules);
|
||||
}
|
||||
|
||||
public static Set<GatewayFlowRule> getRules() {
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
for (Set<GatewayFlowRule> ruleSet : RULE_MAP.values()) {
|
||||
rules.addAll(ruleSet);
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) {
|
||||
AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
|
||||
Set<GatewayFlowRule> set = RULE_MAP.get(resourceName);
|
||||
if (set == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return new HashSet<>(set);
|
||||
}
|
||||
|
||||
private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> {
|
||||
|
||||
@Override
|
||||
public void configUpdate(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + RULE_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + RULE_MAP);
|
||||
}
|
||||
|
||||
private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) {
|
||||
if (conf == null || conf.isEmpty()) {
|
||||
FlowRuleManager.loadRules(new ArrayList<FlowRule>());
|
||||
ParamFlowRuleManager.loadRules(new ArrayList<ParamFlowRule>());
|
||||
RULE_MAP.clear();
|
||||
return;
|
||||
}
|
||||
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
|
||||
Map<String, Integer> idxMap = new HashMap<>();
|
||||
List<FlowRule> flowRules = new ArrayList<>();
|
||||
Set<ParamFlowRule> paramFlowRules = new HashSet<>();
|
||||
|
||||
for (GatewayFlowRule rule : conf) {
|
||||
if (!isValidRule(rule)) {
|
||||
RecordLog.warn("[GatewayRuleManager] Ignoring invalid rule when loading new rules: " + rule);
|
||||
continue;
|
||||
}
|
||||
String resourceName = rule.getResource();
|
||||
if (rule.getParamItem() == null) {
|
||||
// If param item is absent, it will be converted to normal flow rule.
|
||||
flowRules.add(GatewayRuleConverter.toFlowRule(rule));
|
||||
} else {
|
||||
// Prepare index map.
|
||||
if (!idxMap.containsKey(resourceName)) {
|
||||
idxMap.put(resourceName, 0);
|
||||
}
|
||||
int idx = idxMap.get(resourceName);
|
||||
// Convert to parameter flow rule.
|
||||
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
|
||||
idxMap.put(rule.getResource(), idx + 1);
|
||||
}
|
||||
}
|
||||
// Apply to the gateway rule map.
|
||||
Set<GatewayFlowRule> ruleSet = gatewayRuleMap.get(resourceName);
|
||||
if (ruleSet == null) {
|
||||
ruleSet = new HashSet<>();
|
||||
gatewayRuleMap.put(resourceName, ruleSet);
|
||||
}
|
||||
ruleSet.add(rule);
|
||||
}
|
||||
FlowRuleManager.loadRules(flowRules);
|
||||
ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules));
|
||||
|
||||
RULE_MAP.clear();
|
||||
RULE_MAP.putAll(gatewayRuleMap);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidRule(GatewayFlowRule rule) {
|
||||
if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0
|
||||
|| rule.getGrade() < 0 || rule.getCount() < 0 || rule.getBurst() < 0 || rule.getControlBehavior() < 0) {
|
||||
return false;
|
||||
}
|
||||
if (rule.getGrade() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
|
||||
&& rule.getMaxQueueingTimeoutMs() < 0) {
|
||||
return false;
|
||||
}
|
||||
if (rule.getIntervalSec() <= 0) {
|
||||
return false;
|
||||
}
|
||||
GatewayParamFlowItem item = rule.getParamItem();
|
||||
if (item != null) {
|
||||
return isValidParamItem(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isValidParamItem(/*@NonNull*/ GatewayParamFlowItem item) {
|
||||
if (item.getParseStrategy() < 0) {
|
||||
return false;
|
||||
}
|
||||
if (item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM ||
|
||||
item.getParseStrategy() == SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) {
|
||||
if (StringUtil.isBlank(item.getFieldName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
|
||||
}
|
||||
|
||||
private GatewayRuleManager() {}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayApiDefinitionManagerTest {
|
||||
|
||||
@Test
|
||||
public void testIsValidApi() {
|
||||
ApiDefinition bad1 = new ApiDefinition();
|
||||
ApiDefinition bad2 = new ApiDefinition("foo");
|
||||
ApiDefinition good1 = new ApiDefinition("foo")
|
||||
.setPredicateItems(Collections.<ApiPredicateItem>singleton(new ApiPathPredicateItem()
|
||||
.setPattern("/abc")
|
||||
));
|
||||
|
||||
assertFalse(GatewayApiDefinitionManager.isValidApi(bad1));
|
||||
assertFalse(GatewayApiDefinitionManager.isValidApi(bad2));
|
||||
assertTrue(GatewayApiDefinitionManager.isValidApi(good1));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayRuleConverterTest {
|
||||
|
||||
@Test
|
||||
public void testConvertToFlowRule() {
|
||||
GatewayFlowRule rule = new GatewayFlowRule("routeId1")
|
||||
.setCount(10)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(1000);
|
||||
FlowRule flowRule = GatewayRuleConverter.toFlowRule(rule);
|
||||
assertEquals(rule.getResource(), flowRule.getResource());
|
||||
assertEquals(rule.getCount(), flowRule.getCount(), 0.01);
|
||||
assertEquals(rule.getControlBehavior(), flowRule.getControlBehavior());
|
||||
assertEquals(rule.getMaxQueueingTimeoutMs(), flowRule.getMaxQueueingTimeMs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertAndApplyToParamRule() {
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule("routeId1")
|
||||
.setCount(2)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(2)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
int idx = 1;
|
||||
ParamFlowRule paramRule = GatewayRuleConverter.applyToParamRule(routeRule1, idx);
|
||||
assertEquals(routeRule1.getResource(), paramRule.getResource());
|
||||
assertEquals(routeRule1.getCount(), paramRule.getCount(), 0.01);
|
||||
assertEquals(routeRule1.getControlBehavior(), paramRule.getControlBehavior());
|
||||
assertEquals(routeRule1.getIntervalSec(), paramRule.getDurationInSec());
|
||||
assertEquals(routeRule1.getBurst(), paramRule.getBurstCount());
|
||||
assertEquals(idx, (int)paramRule.getParamIdx());
|
||||
assertEquals(idx, (int)routeRule1.getParamItem().getIndex());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.common.rule;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayRuleManagerTest {
|
||||
|
||||
@Test
|
||||
public void testLoadAndGetGatewayRules() {
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
String ahasRoute = "ahas_route";
|
||||
GatewayFlowRule rule1 = new GatewayFlowRule(ahasRoute)
|
||||
.setCount(500)
|
||||
.setIntervalSec(1);
|
||||
GatewayFlowRule rule2 = new GatewayFlowRule(ahasRoute)
|
||||
.setCount(20)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(5)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
GatewayFlowRule rule3 = new GatewayFlowRule("complex_route_ZZZ")
|
||||
.setCount(10)
|
||||
.setIntervalSec(1)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(600)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName("X-Sentinel-Flag")
|
||||
);
|
||||
rules.add(rule1);
|
||||
rules.add(rule2);
|
||||
rules.add(rule3);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
assertTrue(FlowRuleManager.hasConfig(ahasRoute));
|
||||
assertTrue(ParamFlowRuleManager.hasRules(ahasRoute));
|
||||
assertEquals(0, (int)rule2.getParamItem().getIndex());
|
||||
assertEquals(0, (int)rule3.getParamItem().getIndex());
|
||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
|
||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidRule() {
|
||||
GatewayFlowRule bad1 = new GatewayFlowRule();
|
||||
GatewayFlowRule bad2 = new GatewayFlowRule("abc")
|
||||
.setIntervalSec(0);
|
||||
GatewayFlowRule bad3 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM));
|
||||
GatewayFlowRule bad4 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName("p")
|
||||
.setPattern("def")
|
||||
.setMatchStrategy(-1)
|
||||
);
|
||||
GatewayFlowRule good1 = new GatewayFlowRule("abc");
|
||||
GatewayFlowRule good2 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem().setParseStrategy(0));
|
||||
GatewayFlowRule good3 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName("Origin")
|
||||
.setPattern("def"));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad1));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad2));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad3));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad4));
|
||||
|
||||
assertTrue(GatewayRuleManager.isValidRule(good1));
|
||||
assertTrue(GatewayRuleManager.isValidRule(good2));
|
||||
assertTrue(GatewayRuleManager.isValidRule(good3));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue