From 0e79b0ed3115c764409555033dcc54b945708051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B7=E5=86=B7?= Date: Wed, 24 Jul 2019 10:10:10 +0800 Subject: [PATCH] :sparkles: Introducing new features. Sentinel Support RedisDataSource Dynamic Rule Configuration --- spring-cloud-alibaba-dependencies/pom.xml | 5 + .../sentinel-core-example/pom.xml | 4 + .../sentinel-core-example/readme-zh.md | 4 +- .../sentinel-core-example/readme.md | 7 +- .../pom.xml | 7 + .../DataSourcePropertiesConfiguration.java | 17 +- .../config/RedisDataSourceProperties.java | 173 ++++++++++++++++ .../RedisDataSourceFactoryBean.java | 186 ++++++++++++++++++ .../META-INF/sentinel-datasource.properties | 3 +- 9 files changed, 398 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java create mode 100644 spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 57b7ed10..1cde0acf 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -143,6 +143,11 @@ sentinel-datasource-nacos ${sentinel.version} + + com.alibaba.csp + sentinel-datasource-redis + ${sentinel.version} + com.alibaba.csp sentinel-web-servlet diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml index 011989ab..1dc43f4b 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml @@ -43,6 +43,10 @@ + + + + diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md index 8b3a9214..0534bdee 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md @@ -214,9 +214,9 @@ spring.cloud.sentinel.datasource.ds2.nacos.data-type=json `ds1` 和 `ds2` 表示ReadableDataSource的名称,可随意编写。`ds1` 和 `ds2` 后面的 `file` 和 `nacos` 表示ReadableDataSource的类型。 -目前支持`file`, `nacos`, `zk`, `apollo` 这4种类型。 +目前支持`file`, `nacos`, `zk`, `apollo`,`redis` 这5种类型。 -其中`nacos`,`zk`,`apollo`这3种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`。 +其中`nacos`,`zk`,`apollo`,`redis` 这4种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`, `sentinel-datasource-redis`。 当ReadableDataSource加载规则数据成功的时候,控制台会打印出相应的日志信息: diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md index b95a97d2..8cc2f53e 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md @@ -189,9 +189,9 @@ spring.cloud.sentinel.datasource.ds2.nacos.data-type=json `ds1` and `ds2` means the name of ReadableDataSource, you can write whatever you want. The `file` and `nacos` after name `ds1` and `ds2` means the type of ReadableDataSource. -Now ReadableDataSource type support 4 categories: `file`, `nacos`, `zk` and `apollo`. +Now ReadableDataSource type support 5 categories: `file`, `nacos`, `zk`, `apollo` and `redis`. -If you want to use `nacos`, `zk` or `apollo` ReadableDataSource, you could add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper` or `sentinel-datasource-apollo` dependency. +If you want to use `nacos`, `zk`, `apollo` or `redis` ReadableDataSource, you could add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`,`sentinel-datasource-apollo` or `sentinel-datasource-redis` dependency. When ReadableDataSource load rule data successfully, console will print some logs: @@ -203,5 +203,4 @@ When ReadableDataSource load rule data successfully, console will print some log ## More For more information about Sentinel, see [Sentinel Project](https://github.com/alibaba/Sentinel). -If you have any ideas or suggestions for Spring Cloud Sentinel starter, please don't hesitate to tell us by submitting github issues. - +If you have any ideas or suggestions for Spring Cloud Sentinel starter, please don't hesitate to tell us by submitting github issues. \ No newline at end of file diff --git a/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-sentinel-datasource/pom.xml index ad4b67a9..213b8b64 100644 --- a/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -53,6 +53,13 @@ true + + com.alibaba.csp + sentinel-datasource-redis + provided + true + + com.fasterxml.jackson.core jackson-databind diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java index 12dde119..9726668f 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java @@ -33,6 +33,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; * @see ApolloDataSourceProperties * @see ZookeeperDataSourceProperties * @see FileDataSourceProperties + * @see RedisDataSourceProperties */ public class DataSourcePropertiesConfiguration { @@ -44,6 +45,8 @@ public class DataSourcePropertiesConfiguration { private ApolloDataSourceProperties apollo; + private RedisDataSourceProperties redis; + public DataSourcePropertiesConfiguration() { } @@ -63,7 +66,11 @@ public class DataSourcePropertiesConfiguration { this.apollo = apollo; } - public FileDataSourceProperties getFile() { + public DataSourcePropertiesConfiguration(RedisDataSourceProperties redis) { + this.redis = redis; + } + + public FileDataSourceProperties getFile() { return file; } @@ -95,6 +102,14 @@ public class DataSourcePropertiesConfiguration { this.apollo = apollo; } + public RedisDataSourceProperties getRedis() { + return redis; + } + + public void setRedis(RedisDataSourceProperties redis) { + this.redis = redis; + } + @JsonIgnore public List getValidField() { return Arrays.stream(this.getClass().getDeclaredFields()).map(field -> { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java new file mode 100644 index 00000000..8bea7625 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java @@ -0,0 +1,173 @@ +/* + * 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 + * + * 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.cloud.sentinel.datasource.config; + +import com.alibaba.cloud.sentinel.datasource.factorybean.RedisDataSourceFactoryBean; +import org.springframework.util.StringUtils; + +import java.time.Duration; +import java.util.List; + +/** + * Zookeeper Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link RedisDataSourceFactoryBean} + * + * @author lengleng + */ +public class RedisDataSourceProperties extends AbstractDataSourceProperties { + + public RedisDataSourceProperties() { + super(RedisDataSourceFactoryBean.class.getName()); + } + + /** + * redis server host + */ + private String host = "localhost"; + + /** + * redis server port + */ + private int port = 6379; + + /** + * redis server password + */ + private String password; + + /** + * redis server default select database + */ + private int database; + + /** + * redis server timeout + */ + private Duration timeout; + + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; + + /** + * data key in Redis + */ + private String ruleKey; + + /** + * channel to subscribe in Redis + */ + private String channel; + + /** + * redis sentinel model + */ + private String masterId; + + @Override + public void preCheck(String dataSourceName) { + super.preCheck(dataSourceName); + if (StringUtils.isEmpty(ruleKey)) { + throw new IllegalArgumentException( + "RedisDataSource ruleKey can not be empty"); + } + + if (StringUtils.isEmpty(channel)) { + throw new IllegalArgumentException( + "RedisDataSource channel can not be empty"); + } + + if (!StringUtils.isEmpty(masterId) && StringUtils.isEmpty(masterId)) { + throw new IllegalArgumentException( + "RedisDataSource sentinel model,masterId can not be empty"); + } + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getRuleKey() { + return ruleKey; + } + + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + public Duration getTimeout() { + return timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public String getMasterId() { + return masterId; + } + + public void setMasterId(String masterId) { + this.masterId = masterId; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java new file mode 100644 index 00000000..bbf8435b --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java @@ -0,0 +1,186 @@ +/* + * 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 + * + * 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.cloud.sentinel.datasource.factorybean; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource; +import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.time.Duration; +import java.util.List; + +/** + * A {@link FactoryBean} for creating {@link RedisDataSource} instance. + * + * @author lengleng + * @see RedisDataSource + */ +public class RedisDataSourceFactoryBean implements FactoryBean { + + private String host; + + private int port; + + private int database; + + private Duration timeout; + + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; + + private Converter converter; + + /** + * data key in Redis + */ + private String ruleKey; + + /** + * channel to subscribe in Redis + */ + private String channel; + + /** + * redis server password + */ + private String password; + + private String masterId; + + @Override + public RedisDataSource getObject() { + RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder(); + + if (nodes == null || nodes.isEmpty()) { + builder.withHost(host) + .withPort(port) + .withDatabase(database); + } else { + nodes.forEach(node -> { + try { + String[] parts = StringUtils.split(node, ":"); + Assert.state(parts.length == 2, "Must be defined as 'host:port'"); + builder.withRedisSentinel(parts[0], Integer.parseInt(parts[1])); + } catch (RuntimeException ex) { + throw new IllegalStateException("Invalid redis sentinel property " + node, ex); + } + }); + builder.withSentinelMasterId(masterId); + } + + if (timeout != null) { + builder.withTimeout(timeout.toMillis()); + } + + if (StringUtils.hasText(password)) { + builder.withPassword(password); + } + + return new RedisDataSource>(builder.build(), ruleKey, channel, converter); + } + + @Override + public Class getObjectType() { + return RedisDataSource.class; + } + + public Converter getConverter() { + return converter; + } + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getRuleKey() { + return ruleKey; + } + + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + public Duration getTimeout() { + return timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public String getMasterId() { + return masterId; + } + + public void setMasterId(String masterId) { + this.masterId = masterId; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties b/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties index 8326eff6..3fa290ee 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties @@ -1,4 +1,5 @@ nacos = com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource file =com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource apollo = com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource -zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource \ No newline at end of file +zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource +redis = com.alibaba.csp.sentinel.datasource.redis.RedisDataSource \ No newline at end of file