From 6d89d556bf74c49c451632b163e48013fe6c562b Mon Sep 17 00:00:00 2001 From: fangjian0423 Date: Mon, 6 May 2019 15:57:32 +0800 Subject: [PATCH] Polish #615 --- spring-cloud-alibaba-dependencies/pom.xml | 10 + .../pom.xml | 6 + .../alibaba/sentinel/datasource/RuleType.java | 12 +- .../config/AbstractDataSourceProperties.java | 8 + .../converter/SentinelConverter.java | 55 ++- .../datasource/SentinelConverterTests.java | 6 +- spring-cloud-alibaba-sentinel-zuul/pom.xml | 11 +- ...elSpringCloudGatewayAutoConfiguration.java | 91 ++++ .../zuul/SentinelZuulAutoConfiguration.java | 106 +++-- .../zuul/handler/FallBackProviderHandler.java | 4 +- .../main/resources/META-INF/spring.factories | 3 +- spring-cloud-alibaba-sentinel/pom.xml | 5 + .../custom/SentinelAutoConfiguration.java | 421 +++++++++++------- .../custom/SentinelDataSourceHandler.java | 25 +- 14 files changed, 530 insertions(+), 233 deletions(-) create mode 100644 spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelSpringCloudGatewayAutoConfiguration.java diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index ccb740da..51a27335 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -152,6 +152,11 @@ sentinel-zuul-adapter ${sentinel.version} + + com.alibaba.csp + sentinel-spring-cloud-gateway-adapter + ${sentinel.version} + com.alibaba.csp sentinel-transport-simple-http @@ -192,6 +197,11 @@ sentinel-spring-webflux-adapter ${sentinel.version} + + com.alibaba.csp + sentinel-api-gateway-adapter-common + ${sentinel.version} + diff --git a/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-sentinel-datasource/pom.xml index 63203844..6dfee847 100644 --- a/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -27,6 +27,12 @@ true + + com.alibaba.csp + sentinel-api-gateway-adapter-common + true + + com.alibaba.csp sentinel-datasource-nacos diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleType.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleType.java index e59a5735..a9f0a2f8 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleType.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/RuleType.java @@ -6,6 +6,8 @@ import java.util.Optional; import org.springframework.cloud.alibaba.sentinel.datasource.config.AbstractDataSourceProperties; import org.springframework.util.StringUtils; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.slots.block.AbstractRule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; @@ -40,7 +42,15 @@ public enum RuleType { /** * authority */ - AUTHORITY("authority", AuthorityRule.class); + AUTHORITY("authority", AuthorityRule.class), + /** + * gateway flow + */ + GATEWAY("gateway-flow", GatewayFlowRule.class), + /** + * api + */ + API("api", ApiDefinition.class); /** * alias for {@link AbstractRule} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java index 272252c2..bfb3217d 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/config/AbstractDataSourceProperties.java @@ -5,6 +5,8 @@ import javax.validation.constraints.NotNull; import org.springframework.cloud.alibaba.sentinel.datasource.RuleType; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; import com.alibaba.csp.sentinel.datasource.AbstractDataSource; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; @@ -82,6 +84,12 @@ public class AbstractDataSourceProperties { case AUTHORITY: AuthorityRuleManager.register2Property(dataSource.getProperty()); break; + case GATEWAY: + GatewayRuleManager.register2Property(dataSource.getProperty()); + break; + case API: + GatewayApiDefinitionManager.register2Property(dataSource.getProperty()); + break; default: break; } diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/SentinelConverter.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/SentinelConverter.java index 302bcd45..649f8f3b 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/SentinelConverter.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/org/springframework/cloud/alibaba/sentinel/datasource/converter/SentinelConverter.java @@ -1,7 +1,20 @@ package org.springframework.cloud.alibaba.sentinel.datasource.converter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.alibaba.sentinel.datasource.RuleType; +import org.springframework.util.StringUtils; + +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; import com.alibaba.csp.sentinel.datasource.Converter; -import com.alibaba.csp.sentinel.slots.block.AbstractRule; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; @@ -13,15 +26,6 @@ import com.alibaba.csp.sentinel.slots.system.SystemRule; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.cloud.alibaba.sentinel.datasource.RuleType; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; /** * Convert sentinel rules for json or xml array Using strict mode to parse json or xml @@ -34,8 +38,8 @@ import java.util.Optional; * @see ParamFlowRule * @see ObjectMapper */ -public abstract class SentinelConverter - implements Converter> { +public abstract class SentinelConverter + implements Converter> { private static final Logger log = LoggerFactory.getLogger(SentinelConverter.class); @@ -49,11 +53,20 @@ public abstract class SentinelConverter } @Override - public List convert(String source) { - List ruleList = new ArrayList<>(); + public Collection convert(String source) { + Collection ruleCollection; + + // hard code + if (ruleClass == GatewayFlowRule.class || ruleClass == ApiDefinition.class) { + ruleCollection = new HashSet<>(); + } + else { + ruleCollection = new ArrayList<>(); + } + if (StringUtils.isEmpty(source)) { log.warn("converter can not convert rules because source is empty"); - return ruleList; + return ruleCollection; } try { List sourceArray = objectMapper.readValue(source, @@ -70,11 +83,11 @@ public abstract class SentinelConverter } Optional.ofNullable(convertRule(item)) - .ifPresent(convertRule -> ruleList.add(convertRule)); + .ifPresent(convertRule -> ruleCollection.add(convertRule)); }); - if (ruleList.size() != sourceArray.size()) { - throw new IllegalArgumentException("convert " + ruleList.size() + if (ruleCollection.size() != sourceArray.size()) { + throw new IllegalArgumentException("convert " + ruleCollection.size() + " rules but there are " + sourceArray.size() + " rules from datasource. RuleClass: " + ruleClass.getSimpleName()); @@ -83,12 +96,12 @@ public abstract class SentinelConverter catch (Exception e) { throw new RuntimeException("convert error: " + e.getMessage(), e); } - return ruleList; + return ruleCollection; } - private AbstractRule convertRule(String ruleStr) { + private Object convertRule(String ruleStr) { try { - final AbstractRule rule = objectMapper.readValue(ruleStr, ruleClass); + final Object rule = objectMapper.readValue(ruleStr, ruleClass); RuleType ruleType = RuleType.getByClass(ruleClass).get(); switch (ruleType) { case FLOW: diff --git a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java index c6ae7a67..c4ffdc3b 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/test/java/org/springframework/cloud/alibaba/sentinel/datasource/SentinelConverterTests.java @@ -46,7 +46,7 @@ public class SentinelConverterTests { @Test public void testJsonConverter() { JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class); - List flowRules = jsonConverter + List flowRules = (List) jsonConverter .convert(readFileContent("classpath: flowrule.json")); assertEquals("json converter flow rule size was wrong", 1, flowRules.size()); assertEquals("json converter flow rule resource name was wrong", "resource", @@ -67,7 +67,7 @@ public class SentinelConverterTests { @Test public void testConverterEmptyContent() { JsonConverter jsonConverter = new JsonConverter(objectMapper, FlowRule.class); - List flowRules = jsonConverter.convert(""); + List flowRules = (List) jsonConverter.convert(""); assertEquals("json converter flow rule size was not empty", 0, flowRules.size()); } @@ -86,7 +86,7 @@ public class SentinelConverterTests { @Test public void testXmlConverter() { XmlConverter jsonConverter = new XmlConverter(xmlMapper, FlowRule.class); - List flowRules = jsonConverter + List flowRules = (List) jsonConverter .convert(readFileContent("classpath: flowrule.xml")); assertEquals("xml converter flow rule size was wrong", 2, flowRules.size()); assertEquals("xml converter flow rule1 resource name was wrong", "resource", diff --git a/spring-cloud-alibaba-sentinel-zuul/pom.xml b/spring-cloud-alibaba-sentinel-zuul/pom.xml index 3f59bc1f..ee21854e 100644 --- a/spring-cloud-alibaba-sentinel-zuul/pom.xml +++ b/spring-cloud-alibaba-sentinel-zuul/pom.xml @@ -17,12 +17,21 @@ org.springframework.cloud spring-cloud-starter-netflix-zuul - provided + true com.alibaba.csp sentinel-zuul-adapter + + com.alibaba.csp + sentinel-spring-cloud-gateway-adapter + + + org.springframework.cloud + spring-cloud-starter-gateway + true + org.springframework.boot spring-boot-configuration-processor diff --git a/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelSpringCloudGatewayAutoConfiguration.java b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelSpringCloudGatewayAutoConfiguration.java new file mode 100644 index 00000000..86969722 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelSpringCloudGatewayAutoConfiguration.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * 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 org.springframework.cloud.alibaba.sentinel.zuul; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import javax.annotation.PostConstruct; + +import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler; +import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; +import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.web.reactive.result.view.ViewResolver; + +/** + * @author Jim + */ +@Configuration +@ConditionalOnClass(GlobalFilter.class) +@ConditionalOnProperty(prefix = "spring.cloud.sentinel.spring-cloud-gateway", name = "enabled", havingValue = "true", + matchIfMissing = true) +public class SentinelSpringCloudGatewayAutoConfiguration { + + private static final Logger logger = LoggerFactory + .getLogger(SentinelSpringCloudGatewayAutoConfiguration.class); + + private final List viewResolvers; + private final ServerCodecConfigurer serverCodecConfigurer; + + @Autowired + private Optional blockRequestHandlerOptional; + + @PostConstruct + private void init() { + blockRequestHandlerOptional + .ifPresent(GatewayCallbackManager::setBlockHandler); + } + + public SentinelSpringCloudGatewayAutoConfiguration( + ObjectProvider> viewResolversProvider, + ServerCodecConfigurer serverCodecConfigurer) { + this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); + this.serverCodecConfigurer = serverCodecConfigurer; + } + + @Bean + @Order(Ordered.HIGHEST_PRECEDENCE) + public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { + // Register the block exception handler for Spring Cloud Gateway. + logger.info("[Sentinel SpringCloudGateway] register SentinelGatewayBlockExceptionHandler"); + return new SentinelGatewayBlockExceptionHandler(viewResolvers, + serverCodecConfigurer); + } + + @Bean + @Order(-1) + public GlobalFilter sentinelGatewayFilter() { + logger.info("[Sentinel SpringCloudGateway] register SentinelGatewayFilter"); + return new SentinelGatewayFilter(); + } + +} diff --git a/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelZuulAutoConfiguration.java b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelZuulAutoConfiguration.java index 311b4609..c19b1666 100644 --- a/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelZuulAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/SentinelZuulAutoConfiguration.java @@ -16,22 +16,28 @@ package org.springframework.cloud.alibaba.sentinel.zuul; -import static org.springframework.cloud.commons.util.InetUtilsProperties.PREFIX; +import java.util.Optional; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.alibaba.sentinel.zuul.handler.FallBackProviderHandler; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import javax.annotation.PostConstruct; -import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.DefaultRequestOriginParser; import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.RequestOriginParser; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.ZuulGatewayCallbackManager; import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulErrorFilter; import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPostFilter; import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPreFilter; import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.http.ZuulServlet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.alibaba.sentinel.zuul.handler.FallBackProviderHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; /** * Sentinel Spring Cloud Zuul AutoConfiguration @@ -39,35 +45,71 @@ import com.netflix.zuul.ZuulFilter; * @author tiger */ @Configuration -@ConditionalOnProperty(prefix = PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true) +@ConditionalOnClass(ZuulServlet.class) +@ConditionalOnProperty(prefix = SentinelZuulAutoConfiguration.PREFIX, name = "enabled", havingValue = "true", + matchIfMissing = true) public class SentinelZuulAutoConfiguration { - @Bean - @ConditionalOnMissingBean(RequestOriginParser.class) - public RequestOriginParser requestOriginParser() { - return new DefaultRequestOriginParser(); - } + private static final Logger logger = LoggerFactory + .getLogger(SentinelZuulAutoConfiguration.class); - @Bean - public ZuulFilter sentinelZuulPreFilter() { - // We can also provider the filter order in the constructor. - return new SentinelZuulPreFilter(); - } + public static final String PREFIX = "spring.cloud.sentinel.zuul"; - @Bean - public ZuulFilter sentinelZuulPostFilter() { - return new SentinelZuulPostFilter(); - } + @Autowired + private Environment environment; - @Bean - public ZuulFilter sentinelZuulErrorFilter() { - return new SentinelZuulErrorFilter(); - } + @Autowired + private Optional requestOriginParserOptional; - @Bean - public FallBackProviderHandler fallBackProviderListener( - DefaultListableBeanFactory beanFactory) { - return new FallBackProviderHandler(beanFactory); - } + @PostConstruct + private void init() { + requestOriginParserOptional + .ifPresent(ZuulGatewayCallbackManager::setOriginParser); + } + + @Bean + public ZuulFilter sentinelZuulPreFilter() { + String preOrderStr = environment.getProperty(PREFIX + "." + "order.pre"); + int order = 10000; + try { + order = Integer.parseInt(preOrderStr); + } catch (NumberFormatException e) { + // ignore + } + logger.info("[Sentinel Zuul] register SentinelZuulPreFilter {}", order); + return new SentinelZuulPreFilter(order); + } + + @Bean + public ZuulFilter sentinelZuulPostFilter() { + String postOrderStr = environment.getProperty(PREFIX + "." + "order.post"); + int order = 1000; + try { + order = Integer.parseInt(postOrderStr); + } catch (NumberFormatException e) { + // ignore + } + logger.info("[Sentinel Zuul] register SentinelZuulPostFilter {}", order); + return new SentinelZuulPostFilter(order); + } + + @Bean + public ZuulFilter sentinelZuulErrorFilter() { + String errorOrderStr = environment.getProperty(PREFIX + "." + "order.error"); + int order = -1; + try { + order = Integer.parseInt(errorOrderStr); + } catch (NumberFormatException e) { + // ignore + } + logger.info("[Sentinel Zuul] register SentinelZuulErrorFilter {}", order); + return new SentinelZuulErrorFilter(order); + } + + @Bean + public FallBackProviderHandler fallBackProviderHandler( + DefaultListableBeanFactory beanFactory) { + return new FallBackProviderHandler(beanFactory); + } } diff --git a/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/handler/FallBackProviderHandler.java b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/handler/FallBackProviderHandler.java index 0628d5d5..6b3c21f6 100644 --- a/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/handler/FallBackProviderHandler.java +++ b/spring-cloud-alibaba-sentinel-zuul/src/main/java/org/springframework/cloud/alibaba/sentinel/zuul/handler/FallBackProviderHandler.java @@ -2,11 +2,11 @@ package org.springframework.cloud.alibaba.sentinel.zuul.handler; import java.util.Map; -import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.util.CollectionUtils; import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.DefaultBlockFallbackProvider; import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager; @@ -30,7 +30,7 @@ public class FallBackProviderHandler implements SmartInitializingSingleton { public void afterSingletonsInstantiated() { Map providerMap = beanFactory .getBeansOfType(ZuulBlockFallbackProvider.class); - if (MapUtils.isNotEmpty(providerMap)) { + if (!CollectionUtils.isEmpty(providerMap)) { providerMap.forEach((k, v) -> { logger.info("[Sentinel Zuul] Register provider name:{}, instance: {}", k, v); diff --git a/spring-cloud-alibaba-sentinel-zuul/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-sentinel-zuul/src/main/resources/META-INF/spring.factories index b4ced151..5917e8ff 100644 --- a/spring-cloud-alibaba-sentinel-zuul/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-sentinel-zuul/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.alibaba.sentinel.zuul.SentinelZuulAutoConfiguration \ No newline at end of file +org.springframework.cloud.alibaba.sentinel.zuul.SentinelZuulAutoConfiguration,\ +org.springframework.cloud.alibaba.sentinel.zuul.SentinelSpringCloudGatewayAutoConfiguration \ No newline at end of file diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml index a88c7f05..a832136b 100644 --- a/spring-cloud-alibaba-sentinel/pom.xml +++ b/spring-cloud-alibaba-sentinel/pom.xml @@ -105,6 +105,11 @@ provided + + com.alibaba.csp + sentinel-api-gateway-adapter-common + + diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java index 05984c24..40aaf183 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java @@ -16,8 +16,42 @@ package org.springframework.cloud.alibaba.sentinel.custom; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + import javax.annotation.PostConstruct; +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.ApiPredicateGroupItem; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; +import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig; +import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; +import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.init.InitExecutor; +import com.alibaba.csp.sentinel.log.LogBase; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; +import com.alibaba.csp.sentinel.slots.system.SystemRule; +import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import com.alibaba.csp.sentinel.util.AppNameUtil; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -33,23 +67,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.StringUtils; -import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig; -import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; -import com.alibaba.csp.sentinel.config.SentinelConfig; -import com.alibaba.csp.sentinel.init.InitExecutor; -import com.alibaba.csp.sentinel.log.LogBase; -import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; -import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; -import com.alibaba.csp.sentinel.slots.system.SystemRule; -import com.alibaba.csp.sentinel.transport.config.TransportConfig; -import com.alibaba.csp.sentinel.util.AppNameUtil; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; - /** * @author xiaojing * @author jiashuai.xie @@ -60,173 +77,257 @@ import com.fasterxml.jackson.dataformat.xml.XmlMapper; @EnableConfigurationProperties(SentinelProperties.class) public class SentinelAutoConfiguration { - @Value("${project.name:${spring.application.name:}}") - private String projectName; + @Value("${project.name:${spring.application.name:}}") + private String projectName; - @Autowired - private SentinelProperties properties; + @Autowired + private SentinelProperties properties; - @PostConstruct - private void init() { - if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR)) - && StringUtils.hasText(properties.getLog().getDir())) { - System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir()); - } - if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID)) - && properties.getLog().isSwitchPid()) { - System.setProperty(LogBase.LOG_NAME_USE_PID, - String.valueOf(properties.getLog().isSwitchPid())); - } - if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME)) - && StringUtils.hasText(projectName)) { - System.setProperty(AppNameUtil.APP_NAME, projectName); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT)) - && StringUtils.hasText(properties.getTransport().getPort())) { - System.setProperty(TransportConfig.SERVER_PORT, - properties.getTransport().getPort()); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER)) - && StringUtils.hasText(properties.getTransport().getDashboard())) { - System.setProperty(TransportConfig.CONSOLE_SERVER, - properties.getTransport().getDashboard()); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS)) - && StringUtils - .hasText(properties.getTransport().getHeartbeatIntervalMs())) { - System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS, - properties.getTransport().getHeartbeatIntervalMs()); - } - if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP)) - && StringUtils.hasText(properties.getTransport().getClientIp())) { - System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP, - properties.getTransport().getClientIp()); - } - if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET)) - && StringUtils.hasText(properties.getMetric().getCharset())) { - System.setProperty(SentinelConfig.CHARSET, - properties.getMetric().getCharset()); - } - if (StringUtils - .isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE)) - && StringUtils.hasText(properties.getMetric().getFileSingleSize())) { - System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE, - properties.getMetric().getFileSingleSize()); - } - if (StringUtils - .isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT)) - && StringUtils.hasText(properties.getMetric().getFileTotalCount())) { - System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT, - properties.getMetric().getFileTotalCount()); - } - if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR)) - && StringUtils.hasText(properties.getFlow().getColdFactor())) { - System.setProperty(SentinelConfig.COLD_FACTOR, - properties.getFlow().getColdFactor()); - } - if (StringUtils.hasText(properties.getServlet().getBlockPage())) { - WebServletConfig.setBlockPage(properties.getServlet().getBlockPage()); - } + @PostConstruct + private void init() { + if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_DIR)) + && StringUtils.hasText(properties.getLog().getDir())) { + System.setProperty(LogBase.LOG_DIR, properties.getLog().getDir()); + } + if (StringUtils.isEmpty(System.getProperty(LogBase.LOG_NAME_USE_PID)) + && properties.getLog().isSwitchPid()) { + System.setProperty(LogBase.LOG_NAME_USE_PID, + String.valueOf(properties.getLog().isSwitchPid())); + } + if (StringUtils.isEmpty(System.getProperty(AppNameUtil.APP_NAME)) + && StringUtils.hasText(projectName)) { + System.setProperty(AppNameUtil.APP_NAME, projectName); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.SERVER_PORT)) + && StringUtils.hasText(properties.getTransport().getPort())) { + System.setProperty(TransportConfig.SERVER_PORT, + properties.getTransport().getPort()); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.CONSOLE_SERVER)) + && StringUtils.hasText(properties.getTransport().getDashboard())) { + System.setProperty(TransportConfig.CONSOLE_SERVER, + properties.getTransport().getDashboard()); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_INTERVAL_MS)) + && StringUtils + .hasText(properties.getTransport().getHeartbeatIntervalMs())) { + System.setProperty(TransportConfig.HEARTBEAT_INTERVAL_MS, + properties.getTransport().getHeartbeatIntervalMs()); + } + if (StringUtils.isEmpty(System.getProperty(TransportConfig.HEARTBEAT_CLIENT_IP)) + && StringUtils.hasText(properties.getTransport().getClientIp())) { + System.setProperty(TransportConfig.HEARTBEAT_CLIENT_IP, + properties.getTransport().getClientIp()); + } + if (StringUtils.isEmpty(System.getProperty(SentinelConfig.CHARSET)) + && StringUtils.hasText(properties.getMetric().getCharset())) { + System.setProperty(SentinelConfig.CHARSET, + properties.getMetric().getCharset()); + } + if (StringUtils + .isEmpty(System.getProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE)) + && StringUtils.hasText(properties.getMetric().getFileSingleSize())) { + System.setProperty(SentinelConfig.SINGLE_METRIC_FILE_SIZE, + properties.getMetric().getFileSingleSize()); + } + if (StringUtils + .isEmpty(System.getProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT)) + && StringUtils.hasText(properties.getMetric().getFileTotalCount())) { + System.setProperty(SentinelConfig.TOTAL_METRIC_FILE_COUNT, + properties.getMetric().getFileTotalCount()); + } + if (StringUtils.isEmpty(System.getProperty(SentinelConfig.COLD_FACTOR)) + && StringUtils.hasText(properties.getFlow().getColdFactor())) { + System.setProperty(SentinelConfig.COLD_FACTOR, + properties.getFlow().getColdFactor()); + } + if (StringUtils.hasText(properties.getServlet().getBlockPage())) { + WebServletConfig.setBlockPage(properties.getServlet().getBlockPage()); + } - // earlier initialize - if (properties.isEager()) { - InitExecutor.doInit(); - } + // earlier initialize + if (properties.isEager()) { + InitExecutor.doInit(); + } - } + } - @Bean - @ConditionalOnMissingBean - public SentinelResourceAspect sentinelResourceAspect() { - return new SentinelResourceAspect(); - } + @Bean + @ConditionalOnMissingBean + public SentinelResourceAspect sentinelResourceAspect() { + return new SentinelResourceAspect(); + } - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") - @ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true", matchIfMissing = true) - public SentinelBeanPostProcessor sentinelBeanPostProcessor( - ApplicationContext applicationContext) { - return new SentinelBeanPostProcessor(applicationContext); - } + @Bean + @ConditionalOnMissingBean + @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") + @ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true", matchIfMissing = true) + public SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } - @Bean - public SentinelDataSourceHandler sentinelDataSourceHandler( - DefaultListableBeanFactory beanFactory) { - return new SentinelDataSourceHandler(beanFactory); - } + @Bean + public SentinelDataSourceHandler sentinelDataSourceHandler( + DefaultListableBeanFactory beanFactory) { + return new SentinelDataSourceHandler(beanFactory); + } - @ConditionalOnClass(ObjectMapper.class) - protected static class SentinelConverterConfiguration { + @ConditionalOnClass(ObjectMapper.class) + @Configuration + protected static class SentinelConverterConfiguration { - private ObjectMapper objectMapper = new ObjectMapper(); + static class ApiPredicateItemDeserializer + extends StdDeserializer { + private Map> registry + = new HashMap>(); - public SentinelConverterConfiguration() { - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, - false); - } + ApiPredicateItemDeserializer() { + super(ApiPredicateItem.class); + } - @Bean("sentinel-json-flow-converter") - public JsonConverter jsonFlowConverter() { - return new JsonConverter(objectMapper, FlowRule.class); - } + void registerApiPredicateItem(String uniqueAttribute, + Class apiPredicateItemClass) { + registry.put(uniqueAttribute, apiPredicateItemClass); + } - @Bean("sentinel-json-degrade-converter") - public JsonConverter jsonDegradeConverter() { - return new JsonConverter(objectMapper, DegradeRule.class); - } + @Override + public ApiPredicateItem deserialize(JsonParser jp, + DeserializationContext ctxt) + throws IOException { + ObjectMapper mapper = (ObjectMapper)jp.getCodec(); + ObjectNode root = mapper.readTree(jp); + Class apiPredicateItemClass = null; + Iterator> elementsIterator = root.fields(); + while (elementsIterator.hasNext()) { + Entry element = elementsIterator.next(); + String name = element.getKey(); + if (registry.containsKey(name)) { + apiPredicateItemClass = registry.get(name); + break; + } + } + if (apiPredicateItemClass == null) { + return null; + } + return mapper.readValue(root.toString(), apiPredicateItemClass); + } + } - @Bean("sentinel-json-system-converter") - public JsonConverter jsonSystemConverter() { - return new JsonConverter(objectMapper, SystemRule.class); - } + @Configuration + protected static class SentinelJsonConfiguration { - @Bean("sentinel-json-authority-converter") - public JsonConverter jsonAuthorityConverter() { - return new JsonConverter(objectMapper, AuthorityRule.class); - } + private ObjectMapper objectMapper = new ObjectMapper(); - @Bean("sentinel-json-param-flow-converter") - public JsonConverter jsonParamFlowConverter() { - return new JsonConverter(objectMapper, ParamFlowRule.class); - } + public SentinelJsonConfiguration() { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false); - } + ApiPredicateItemDeserializer deserializer = new ApiPredicateItemDeserializer(); + deserializer.registerApiPredicateItem("pattern", + ApiPathPredicateItem.class); + deserializer.registerApiPredicateItem("items", + ApiPredicateGroupItem.class); + SimpleModule module = new SimpleModule( + "PolymorphicApiPredicateItemDeserializerModule", + new Version(1, 0, 0, null)); + module.addDeserializer(ApiPredicateItem.class, deserializer); + objectMapper.registerModule(module); + } - @ConditionalOnClass(XmlMapper.class) - protected static class SentinelXmlConfiguration { + @Bean("sentinel-json-flow-converter") + public JsonConverter jsonFlowConverter() { + return new JsonConverter(objectMapper, FlowRule.class); + } - private XmlMapper xmlMapper = new XmlMapper(); + @Bean("sentinel-json-degrade-converter") + public JsonConverter jsonDegradeConverter() { + return new JsonConverter(objectMapper, DegradeRule.class); + } - public SentinelXmlConfiguration() { - xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } + @Bean("sentinel-json-system-converter") + public JsonConverter jsonSystemConverter() { + return new JsonConverter(objectMapper, SystemRule.class); + } - @Bean("sentinel-xml-flow-converter") - public XmlConverter xmlFlowConverter() { - return new XmlConverter(xmlMapper, FlowRule.class); - } + @Bean("sentinel-json-authority-converter") + public JsonConverter jsonAuthorityConverter() { + return new JsonConverter(objectMapper, AuthorityRule.class); + } - @Bean("sentinel-xml-degrade-converter") - public XmlConverter xmlDegradeConverter() { - return new XmlConverter(xmlMapper, DegradeRule.class); - } + @Bean("sentinel-json-param-flow-converter") + public JsonConverter jsonParamFlowConverter() { + return new JsonConverter(objectMapper, ParamFlowRule.class); + } - @Bean("sentinel-xml-system-converter") - public XmlConverter xmlSystemConverter() { - return new XmlConverter(xmlMapper, SystemRule.class); - } + @Bean("sentinel-json-gateway-flow-converter") + public JsonConverter jsonGatewayFlowConverter() { + return new JsonConverter(objectMapper, GatewayFlowRule.class); + } - @Bean("sentinel-xml-authority-converter") - public XmlConverter xmlAuthorityConverter() { - return new XmlConverter(xmlMapper, AuthorityRule.class); - } + @Bean("sentinel-json-api-converter") + public JsonConverter jsonApiConverter() { + return new JsonConverter(objectMapper, ApiDefinition.class); + } + } - @Bean("sentinel-xml-param-flow-converter") - public XmlConverter xmlParamFlowConverter() { - return new XmlConverter(xmlMapper, ParamFlowRule.class); - } + @ConditionalOnClass(XmlMapper.class) + @Configuration + protected static class SentinelXmlConfiguration { - } + private XmlMapper xmlMapper = new XmlMapper(); + + public SentinelXmlConfiguration() { + xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false); + ApiPredicateItemDeserializer deserializer = new ApiPredicateItemDeserializer(); + deserializer.registerApiPredicateItem("pattern", + ApiPathPredicateItem.class); + deserializer.registerApiPredicateItem("items", + ApiPredicateGroupItem.class); + SimpleModule module = new SimpleModule( + "PolymorphicGatewayDeserializerModule", + new Version(1, 0, 0, null)); + module.addDeserializer(ApiPredicateItem.class, deserializer); + xmlMapper.registerModule(module); + } + + @Bean("sentinel-xml-flow-converter") + public XmlConverter xmlFlowConverter() { + return new XmlConverter(xmlMapper, FlowRule.class); + } + + @Bean("sentinel-xml-degrade-converter") + public XmlConverter xmlDegradeConverter() { + return new XmlConverter(xmlMapper, DegradeRule.class); + } + + @Bean("sentinel-xml-system-converter") + public XmlConverter xmlSystemConverter() { + return new XmlConverter(xmlMapper, SystemRule.class); + } + + @Bean("sentinel-xml-authority-converter") + public XmlConverter xmlAuthorityConverter() { + return new XmlConverter(xmlMapper, AuthorityRule.class); + } + + @Bean("sentinel-xml-param-flow-converter") + public XmlConverter xmlParamFlowConverter() { + return new XmlConverter(xmlMapper, ParamFlowRule.class); + } + + @Bean("sentinel-xml-gateway-flow-converter") + public XmlConverter xmlGatewayFlowConverter() { + return new XmlConverter(xmlMapper, GatewayFlowRule.class); + } + + @Bean("sentinel-xml-api-converter") + public XmlConverter xmlApiConverter() { + return new XmlConverter(xmlMapper, ApiDefinition.class); + } + + } + } } diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java index df57f919..c98b82e9 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelDataSourceHandler.java @@ -1,8 +1,13 @@ package org.springframework.cloud.alibaba.sentinel.custom; -import com.alibaba.csp.sentinel.datasource.AbstractDataSource; -import com.alibaba.csp.sentinel.datasource.ReadableDataSource; -import com.alibaba.csp.sentinel.slots.block.AbstractRule; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,12 +23,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import com.alibaba.csp.sentinel.datasource.AbstractDataSource; +import com.alibaba.csp.sentinel.datasource.ReadableDataSource; /** * Sentinel {@link ReadableDataSource} Handler Handle the configurations of @@ -189,7 +190,7 @@ public class SentinelDataSourceHandler implements SmartInitializingSingleton { } private void logAndCheckRuleType(AbstractDataSource dataSource, String dataSourceName, - Class ruleClass) { + Class ruleClass) { Object ruleConfig; try { ruleConfig = dataSource.loadConfig(); @@ -199,8 +200,8 @@ public class SentinelDataSourceHandler implements SmartInitializingSingleton { + " loadConfig error: " + e.getMessage(), e); return; } - if (ruleConfig instanceof List) { - List convertedRuleList = (List) ruleConfig; + if (ruleConfig instanceof List || ruleConfig instanceof Set) { + Collection convertedRuleList = (Collection) ruleConfig; if (CollectionUtils.isEmpty(convertedRuleList)) { log.warn("[Sentinel Starter] DataSource {} rule list is empty.", dataSourceName);