Improvements for Redis data source and demo
Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
4ff2e37abb
commit
b1f33675c5
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.util;
|
||||
|
||||
/**
|
||||
* Util class for checking arguments.
|
||||
*/
|
||||
public class AssertUtil {
|
||||
|
||||
private AssertUtil(){}
|
||||
|
||||
public static void notEmpty(String string, String message) {
|
||||
if (StringUtil.isEmpty(string)) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notNull(Object object, String message) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void isTrue(boolean value, String message) {
|
||||
if (!value) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertState(boolean condition, String message) {
|
||||
if (!condition) {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,4 +6,4 @@ The examples demonstrate:
|
|||
- How to use various data source extensions of Sentinel (e.g. file, Nacos, ZooKeeper)
|
||||
- How to use Dubbo with Sentinel
|
||||
- How to use Apache RocketMQ client with Sentinel
|
||||
|
||||
- How to use Sentinel annotation support
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
# Sentinel DataSource Redis
|
||||
|
||||
Sentinel DataSource Redis provides integration with Redis. make Redis
|
||||
as dynamic rule data source of Sentinel. The data source uses push model (listener) with redis pub/sub feature.
|
||||
Sentinel DataSource Redis provides integration with Redis. The data source leverages Redis pub-sub feature to implement push model (listener).
|
||||
|
||||
**NOTE**:
|
||||
we not support redis cluster as a pub/sub dataSource now.
|
||||
The data source uses [Lettuce](https://lettuce.io/) as the Redis client internal. Requires JDK 1.8 or later.
|
||||
|
||||
> **NOTE**: Currently we do not support Redis Cluster now.
|
||||
|
||||
## Usage
|
||||
|
||||
To use Sentinel DataSource Redis, you should add the following dependency:
|
||||
|
||||
|
|
@ -25,39 +27,41 @@ ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource
|
|||
FlowRuleManager.register2Property(redisDataSource.getProperty());
|
||||
```
|
||||
|
||||
_**redisConnectionConfig**_ : use `RedisConnectionConfig` class to build your connection config.
|
||||
- `redisConnectionConfig`: use `RedisConnectionConfig` class to build your Redis connection config
|
||||
- `ruleKey`: the rule persistence key of a Redis String
|
||||
- `channel`: the channel to subscribe
|
||||
|
||||
_**ruleKey**_ : when the json rule data publish. it also should save to the key for init read.
|
||||
You can also create multi data sources to subscribe for different rule type.
|
||||
|
||||
_**channel**_ : the channel to listen.
|
||||
|
||||
you can also create multi data source listen for different rule type.
|
||||
|
||||
you can see test cases for usage.
|
||||
|
||||
## Before start
|
||||
|
||||
RedisDataSource init config by read from redis key `ruleKey`, value store the latest rule config data.
|
||||
so you should first config your redis ruleData in back end.
|
||||
|
||||
since update redis rule data. it should simultaneously send data to `channel`.
|
||||
|
||||
you may implement like this (using Redis transaction):
|
||||
Note that the data source first loads initial rules from a Redis String (provided `ruleKey`) during initialization.
|
||||
So for consistency, users should publish the value and save the value to the `ruleKey` simultaneously like this (using Redis transaction):
|
||||
|
||||
```
|
||||
|
||||
MULTI
|
||||
PUBLISH channel value
|
||||
SET ruleKey value
|
||||
PUBLISH channel value
|
||||
EXEC
|
||||
```
|
||||
|
||||
```
|
||||
An example using Lettuce Redis client:
|
||||
|
||||
```java
|
||||
public <T> void pushRules(List<T> rules, Converter<List<T>, String> encoder) {
|
||||
StatefulRedisPubSubConnection<String, String> connection = client.connectPubSub();
|
||||
RedisPubSubCommands<String, String> subCommands = connection.sync();
|
||||
String value = encoder.convert(rules);
|
||||
subCommands.multi();
|
||||
subCommands.set(ruleKey, value);
|
||||
subCommands.publish(ruleChannel, value);
|
||||
subCommands.exec();
|
||||
}
|
||||
```
|
||||
|
||||
## How to build RedisConnectionConfig
|
||||
|
||||
|
||||
### Build with redis standLone mode
|
||||
### Build with Redis standalone mode
|
||||
|
||||
```java
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
|
|
@ -70,7 +74,7 @@ RedisConnectionConfig config = RedisConnectionConfig.builder()
|
|||
```
|
||||
|
||||
|
||||
### Build with redis sentinel mode
|
||||
### Build with Redis Sentinel mode
|
||||
|
||||
```java
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
|
|
|
|||
|
|
@ -13,20 +13,24 @@
|
|||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.source.version>1.8</java.source.version>
|
||||
<java.target.version>1.8</java.target.version>
|
||||
<lettuce.version>5.0.1.RELEASE</lettuce.version>
|
||||
<redis.mock.version>0.1.6</redis.mock.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-extension</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.lettuce</groupId>
|
||||
<artifactId>lettuce-core</artifactId>
|
||||
<version>${lettuce.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-extension</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ package com.alibaba.csp.sentinel.datasource.redis;
|
|||
import com.alibaba.csp.sentinel.datasource.AbstractDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
import com.alibaba.csp.sentinel.datasource.redis.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
|
|
@ -29,38 +30,51 @@ import io.lettuce.core.pubsub.RedisPubSubAdapter;
|
|||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A read-only {@code DataSource} with Redis backend.
|
||||
* <p>
|
||||
* When data source init,reads form redis with string k-v,value is string format rule config data.
|
||||
* This data source subscribe from specific channel and then data published in redis with this channel,data source
|
||||
* will be notified and then update rule config in time.
|
||||
* A read-only {@code DataSource} with Redis backend.
|
||||
* </p>
|
||||
* <p>
|
||||
* The data source first loads initial rules from a Redis String during initialization.
|
||||
* Then the data source subscribe from specific channel. When new rules is published to the channel,
|
||||
* the data source will observe the change in realtime and update to memory.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that for consistency, users should publish the value and save the value to the ruleKey simultaneously
|
||||
* like this (using Redis transaction):
|
||||
* <pre>
|
||||
* MULTI
|
||||
* SET ruleKey value
|
||||
* PUBLISH channel value
|
||||
* EXEC
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
|
||||
public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||
|
||||
private RedisClient redisClient = null;
|
||||
private final RedisClient redisClient;
|
||||
|
||||
private String ruleKey;
|
||||
private final String ruleKey;
|
||||
|
||||
/**
|
||||
* Constructor of {@code RedisDataSource}
|
||||
* Constructor of {@code RedisDataSource}.
|
||||
*
|
||||
* @param connectionConfig redis connection config.
|
||||
* @param ruleKey data save in redis.
|
||||
* @param channel subscribe from channel.
|
||||
* @param parser convert <code>ruleKey<code>`s value to {@literal alibaba/Sentinel} rule type
|
||||
* @param connectionConfig Redis connection config
|
||||
* @param ruleKey data key in Redis
|
||||
* @param channel channel to subscribe in Redis
|
||||
* @param parser customized data parser, cannot be empty
|
||||
*/
|
||||
public RedisDataSource(RedisConnectionConfig connectionConfig, String ruleKey, String channel, Converter<String, T> parser) {
|
||||
public RedisDataSource(RedisConnectionConfig connectionConfig, String ruleKey, String channel,
|
||||
Converter<String, T> parser) {
|
||||
super(parser);
|
||||
AssertUtil.notNull(connectionConfig, "redis connection config can not be null");
|
||||
AssertUtil.notEmpty(ruleKey, "redis subscribe ruleKey can not be empty");
|
||||
AssertUtil.notEmpty(channel, "redis subscribe channel can not be empty");
|
||||
AssertUtil.notNull(connectionConfig, "Redis connection config can not be null");
|
||||
AssertUtil.notEmpty(ruleKey, "Redis ruleKey can not be empty");
|
||||
AssertUtil.notEmpty(channel, "Redis subscribe channel can not be empty");
|
||||
this.redisClient = getRedisClient(connectionConfig);
|
||||
this.ruleKey = ruleKey;
|
||||
loadInitialConfig();
|
||||
|
|
@ -68,28 +82,28 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* build redis client form {@code RedisConnectionConfig} with io.lettuce.
|
||||
* Build Redis client fromm {@code RedisConnectionConfig}.
|
||||
*
|
||||
* @return a new {@link RedisClient}
|
||||
*/
|
||||
private RedisClient getRedisClient(RedisConnectionConfig connectionConfig) {
|
||||
if (connectionConfig.getRedisSentinels().size() == 0) {
|
||||
RecordLog.info("start standLone mode to connect to redis");
|
||||
return getRedisStandLoneClient(connectionConfig);
|
||||
RecordLog.info("[RedisDataSource] Creating stand-alone mode Redis client");
|
||||
return getRedisStandaloneClient(connectionConfig);
|
||||
} else {
|
||||
RecordLog.info("start redis sentinel mode to connect to redis");
|
||||
RecordLog.info("[RedisDataSource] Creating Redis Sentinel mode Redis client");
|
||||
return getRedisSentinelClient(connectionConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private RedisClient getRedisStandLoneClient(RedisConnectionConfig connectionConfig) {
|
||||
private RedisClient getRedisStandaloneClient(RedisConnectionConfig connectionConfig) {
|
||||
char[] password = connectionConfig.getPassword();
|
||||
String clientName = connectionConfig.getClientName();
|
||||
RedisURI.Builder redisUriBuilder = RedisURI.builder();
|
||||
redisUriBuilder.withHost(connectionConfig.getHost())
|
||||
.withPort(connectionConfig.getPort())
|
||||
.withDatabase(connectionConfig.getDatabase())
|
||||
.withTimeout(connectionConfig.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
.withPort(connectionConfig.getPort())
|
||||
.withDatabase(connectionConfig.getDatabase())
|
||||
.withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
|
||||
if (password != null) {
|
||||
redisUriBuilder.withPassword(connectionConfig.getPassword());
|
||||
}
|
||||
|
|
@ -113,7 +127,7 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
|||
sentinelRedisUriBuilder.withClientName(clientName);
|
||||
}
|
||||
sentinelRedisUriBuilder.withSentinelMasterId(connectionConfig.getRedisSentinelMasterId())
|
||||
.withTimeout(connectionConfig.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
.withTimeout(connectionConfig.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
return RedisClient.create(sentinelRedisUriBuilder.build());
|
||||
}
|
||||
|
||||
|
|
@ -138,16 +152,16 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String readSource() throws Exception {
|
||||
public String readSource() {
|
||||
if (this.redisClient == null) {
|
||||
throw new IllegalStateException("redis client has not been initialized or error occurred");
|
||||
throw new IllegalStateException("Redis client has not been initialized or error occurred");
|
||||
}
|
||||
RedisCommands<String, String> stringRedisCommands = redisClient.connect().sync();
|
||||
return stringRedisCommands.get(ruleKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
public void close() {
|
||||
redisClient.shutdown();
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +172,8 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
|||
|
||||
@Override
|
||||
public void message(String channel, String message) {
|
||||
RecordLog.info(String.format("[RedisDataSource] New property value received for channel %s: %s", channel, message));
|
||||
RecordLog.info(String.format("[RedisDataSource] New property value received for channel %s: %s", channel, message));
|
||||
getProperty().updateValue(parser.convert(message));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.alibaba.csp.sentinel.datasource.redis.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.redis.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
|
@ -76,7 +76,6 @@ public class RedisConnectionConfig {
|
|||
setTimeout(timeout);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new {@link RedisConnectionConfig.Builder} to construct a {@link RedisConnectionConfig}.
|
||||
*
|
||||
|
|
@ -86,7 +85,6 @@ public class RedisConnectionConfig {
|
|||
return new RedisConnectionConfig.Builder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the host.
|
||||
*
|
||||
|
|
@ -240,7 +238,6 @@ public class RedisConnectionConfig {
|
|||
return redisSentinels;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
|
@ -269,7 +266,7 @@ public class RedisConnectionConfig {
|
|||
if (!(o instanceof RedisConnectionConfig)) {
|
||||
return false;
|
||||
}
|
||||
RedisConnectionConfig redisURI = (RedisConnectionConfig) o;
|
||||
RedisConnectionConfig redisURI = (RedisConnectionConfig)o;
|
||||
|
||||
if (port != redisURI.port) {
|
||||
return false;
|
||||
|
|
@ -280,10 +277,12 @@ public class RedisConnectionConfig {
|
|||
if (host != null ? !host.equals(redisURI.host) : redisURI.host != null) {
|
||||
return false;
|
||||
}
|
||||
if (redisSentinelMasterId != null ? !redisSentinelMasterId.equals(redisURI.redisSentinelMasterId) : redisURI.redisSentinelMasterId != null) {
|
||||
if (redisSentinelMasterId != null ? !redisSentinelMasterId.equals(redisURI.redisSentinelMasterId)
|
||||
: redisURI.redisSentinelMasterId != null) {
|
||||
return false;
|
||||
}
|
||||
return !(redisSentinels != null ? !redisSentinels.equals(redisURI.redisSentinels) : redisURI.redisSentinels != null);
|
||||
return !(redisSentinels != null ? !redisSentinels.equals(redisURI.redisSentinels)
|
||||
: redisURI.redisSentinels != null);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -297,7 +296,6 @@ public class RedisConnectionConfig {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for Redis RedisConnectionConfig.
|
||||
*/
|
||||
|
|
@ -315,7 +313,6 @@ public class RedisConnectionConfig {
|
|||
private Builder() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set Redis host. Creates a new builder.
|
||||
*
|
||||
|
|
@ -435,7 +432,8 @@ public class RedisConnectionConfig {
|
|||
*/
|
||||
public RedisConnectionConfig.Builder withHost(String host) {
|
||||
|
||||
AssertUtil.assertState(this.redisSentinels.isEmpty(), "Sentinels are non-empty. Cannot use in Sentinel mode.");
|
||||
AssertUtil.assertState(this.redisSentinels.isEmpty(),
|
||||
"Sentinels are non-empty. Cannot use in Sentinel mode.");
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
|
||||
this.host = host;
|
||||
|
|
@ -548,7 +546,8 @@ public class RedisConnectionConfig {
|
|||
|
||||
if (redisSentinels.isEmpty() && StringUtil.isEmpty(host)) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot build a RedisConnectionConfig. One of the following must be provided Host, Socket or Sentinel");
|
||||
"Cannot build a RedisConnectionConfig. One of the following must be provided Host, Socket or "
|
||||
+ "Sentinel");
|
||||
}
|
||||
|
||||
RedisConnectionConfig redisURI = new RedisConnectionConfig();
|
||||
|
|
@ -565,7 +564,8 @@ public class RedisConnectionConfig {
|
|||
redisURI.setRedisSentinelMasterId(redisSentinelMasterId);
|
||||
|
||||
for (RedisHostAndPort sentinel : redisSentinels) {
|
||||
redisURI.getRedisSentinels().add(new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||
redisURI.getRedisSentinels().add(
|
||||
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||
}
|
||||
|
||||
redisURI.setTimeout(timeout);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.alibaba.csp.sentinel.datasource.redis.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.redis.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* An immutable representation of a host and port.
|
||||
|
|
@ -85,7 +85,7 @@ public class RedisHostAndPort {
|
|||
if (!(o instanceof RedisHostAndPort)) {
|
||||
return false;
|
||||
}
|
||||
RedisHostAndPort that = (RedisHostAndPort) o;
|
||||
RedisHostAndPort that = (RedisHostAndPort)o;
|
||||
return port == that.port && (host != null ? host.equals(that.host) : that.host == null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.alibaba.csp.sentinel.datasource.redis.util;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* A util class for filed check
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class AssertUtil {
|
||||
|
||||
private AssertUtil(){}
|
||||
/**
|
||||
* Assert that a string is not empty, it must not be {@code null} and it must not be empty.
|
||||
*
|
||||
* @param string the object to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object is {@code null} or the underlying string is empty
|
||||
*/
|
||||
public static void notEmpty(String string, String message) {
|
||||
if (StringUtil.isEmpty(string)) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that an object is not {@code null} .
|
||||
*
|
||||
* @param object the object to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object is {@code null}
|
||||
*/
|
||||
public static void notNull(Object object, String message) {
|
||||
if (object == null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that {@code value} is {@literal true}.
|
||||
*
|
||||
* @param value the value to check
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalArgumentException if the object array contains a {@code null} element
|
||||
*/
|
||||
public static void isTrue(boolean value, String message) {
|
||||
if (!value) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the truth of an expression involving the state of the calling instance, but not involving any parameters to the
|
||||
* calling method.
|
||||
*
|
||||
* @param condition a boolean expression
|
||||
* @param message the exception message to use if the assertion fails
|
||||
* @throws IllegalStateException if {@code expression} is false
|
||||
*/
|
||||
public static void assertState(boolean condition, String message) {
|
||||
if (!condition) {
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,10 +17,10 @@
|
|||
package com.alibaba.csp.sentinel.datasource.redis;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
* Test cases for {@link RedisConnectionConfig}.
|
||||
*
|
||||
|
|
@ -42,8 +42,8 @@ public class RedisConnectionConfigTest {
|
|||
String host = "localhost";
|
||||
String clientName = "clientName";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host)
|
||||
.withClientName("clientName")
|
||||
.build();
|
||||
.withClientName("clientName")
|
||||
.build();
|
||||
Assert.assertEquals(redisConnectionConfig.getClientName(), clientName);
|
||||
}
|
||||
|
||||
|
|
@ -52,8 +52,8 @@ public class RedisConnectionConfigTest {
|
|||
String host = "localhost";
|
||||
long timeout = 70 * 1000;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host)
|
||||
.withTimeout(timeout)
|
||||
.build();
|
||||
.withTimeout(timeout)
|
||||
.build();
|
||||
Assert.assertEquals(redisConnectionConfig.getTimeout(), timeout);
|
||||
}
|
||||
|
||||
|
|
@ -61,11 +61,12 @@ public class RedisConnectionConfigTest {
|
|||
public void testRedisSentinelDefaultPortSuccess() {
|
||||
String host = "localhost";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertEquals(null, redisConnectionConfig.getHost());
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(1, redisConnectionConfig.getRedisSentinels().size());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_SENTINEL_PORT, redisConnectionConfig.getRedisSentinels().get(0).getPort());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_SENTINEL_PORT,
|
||||
redisConnectionConfig.getRedisSentinels().get(0).getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -74,9 +75,9 @@ public class RedisConnectionConfigTest {
|
|||
String host2 = "server2";
|
||||
int port2 = 1879;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.build();
|
||||
Assert.assertEquals(null, redisConnectionConfig.getHost());
|
||||
.withRedisSentinel(host2, port2)
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(2, redisConnectionConfig.getRedisSentinels().size());
|
||||
}
|
||||
|
||||
|
|
@ -86,12 +87,11 @@ public class RedisConnectionConfigTest {
|
|||
String host2 = "server2";
|
||||
int port2 = 1879;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertEquals(null, redisConnectionConfig.getHost());
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(3, redisConnectionConfig.getRedisSentinels().size());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
|||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
|
|
@ -36,39 +37,43 @@ import java.util.Random;
|
|||
*
|
||||
* @author tiger
|
||||
*/
|
||||
@Ignore(value = "before run this test. you should build your own redisSentinel config in local")
|
||||
@Ignore(value = "Before run this test, you need to set up your Redis Sentinel.")
|
||||
public class SentinelModeRedisDataSourceTest {
|
||||
|
||||
private String host = "localhost";
|
||||
|
||||
private int redisSentinelPort = 5000;
|
||||
|
||||
private String redisSentinelMasterId = "mymaster";
|
||||
private String redisSentinelMasterId = "myMaster";
|
||||
|
||||
private String ruleKey = "redis.redisSentinel.flow.rulekey";
|
||||
|
||||
private String channel = "redis.redisSentinel.flow.channel";
|
||||
private String ruleKey = "sentinel.rules.flow.ruleKey";
|
||||
private String channel = "sentinel.rules.flow.channel";
|
||||
|
||||
private final RedisClient client = RedisClient.create(RedisURI.Builder.sentinel(host, redisSentinelPort)
|
||||
.withSentinelMasterId(redisSentinelMasterId).build());
|
||||
.withSentinelMasterId(redisSentinelMasterId).build());
|
||||
|
||||
@Before
|
||||
public void initData() {
|
||||
Converter<String, List<FlowRule>> flowConfigParser = buildFlowConfigParser();
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withSentinelMasterId(redisSentinelMasterId).build();
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withSentinelMasterId(redisSentinelMasterId).build();
|
||||
initRedisRuleData();
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(config, ruleKey, channel, flowConfigParser);
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<>(config,
|
||||
ruleKey, channel, flowConfigParser);
|
||||
FlowRuleManager.register2Property(redisDataSource.getProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectToSentinelAndPubMsgSuccess() {
|
||||
int maxQueueingTimeMs = new Random().nextInt();
|
||||
String flowRulesJson = "[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, \"refResource\":null, " +
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":" + maxQueueingTimeMs + ", \"controller\":null}]";
|
||||
String flowRulesJson =
|
||||
"[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, "
|
||||
+ "\"refResource\":null, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":" + maxQueueingTimeMs
|
||||
+ ", \"controller\":null}]";
|
||||
RedisCommands<String, String> subCommands = client.connect().sync();
|
||||
subCommands.multi();
|
||||
subCommands.set(ruleKey, flowRulesJson);
|
||||
|
|
@ -97,20 +102,17 @@ public class SentinelModeRedisDataSourceTest {
|
|||
}
|
||||
|
||||
private Converter<String, List<FlowRule>> buildFlowConfigParser() {
|
||||
return new Converter<String, List<FlowRule>>() {
|
||||
@Override
|
||||
public List<FlowRule> convert(String source) {
|
||||
return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
|
||||
});
|
||||
}
|
||||
};
|
||||
return source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
|
||||
}
|
||||
|
||||
private void initRedisRuleData() {
|
||||
String flowRulesJson = "[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, \"refResource\":null, " +
|
||||
String flowRulesJson =
|
||||
"[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, "
|
||||
+ "\"refResource\":null, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":500, \"controller\":null}]";
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String ok = stringRedisCommands.set(ruleKey, flowRulesJson);
|
||||
Assert.assertTrue(ok.equals("OK"));
|
||||
Assert.assertEquals("OK", ok);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package com.alibaba.csp.sentinel.datasource.redis;
|
||||
|
||||
import ai.grakn.redismock.RedisServer;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
|
|
@ -24,6 +25,7 @@ import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
|||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
|
|
@ -39,24 +41,23 @@ import java.util.List;
|
|||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Redis standLone mode test cases for {@link RedisDataSource}.
|
||||
* Redis stand-alone mode test cases for {@link RedisDataSource}.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class StandLoneRedisDataSourceTest {
|
||||
public class StandaloneRedisDataSourceTest {
|
||||
|
||||
private static RedisServer server = null;
|
||||
|
||||
private RedisClient client;
|
||||
|
||||
private String ruleKey = "redisSentinel.flow.rulekey";
|
||||
|
||||
private String channel = "redisSentinel.flow.channel";
|
||||
private String ruleKey = "sentinel.rules.flow.ruleKey";
|
||||
private String channel = "sentinel.rules.flow.channel";
|
||||
|
||||
@Before
|
||||
public void buildResource() {
|
||||
try {
|
||||
//bind to a random port
|
||||
// Bind to a random port.
|
||||
server = RedisServer.newRedisServer();
|
||||
server.start();
|
||||
} catch (IOException e) {
|
||||
|
|
@ -65,11 +66,12 @@ public class StandLoneRedisDataSourceTest {
|
|||
Converter<String, List<FlowRule>> flowConfigParser = buildFlowConfigParser();
|
||||
client = RedisClient.create(RedisURI.create(server.getHost(), server.getBindPort()));
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withHost(server.getHost())
|
||||
.withPort(server.getBindPort())
|
||||
.build();
|
||||
.withHost(server.getHost())
|
||||
.withPort(server.getBindPort())
|
||||
.build();
|
||||
initRedisRuleData();
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(config, ruleKey, channel, flowConfigParser);
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(config,
|
||||
ruleKey, channel, flowConfigParser);
|
||||
FlowRuleManager.register2Property(redisDataSource.getProperty());
|
||||
}
|
||||
|
||||
|
|
@ -79,8 +81,12 @@ public class StandLoneRedisDataSourceTest {
|
|||
Assert.assertEquals(1, rules.size());
|
||||
int maxQueueingTimeMs = new Random().nextInt();
|
||||
StatefulRedisPubSubConnection<String, String> connection = client.connectPubSub();
|
||||
String flowRules = "[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, \"refResource\":null, " +
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":" + maxQueueingTimeMs + ", \"controller\":null}]";
|
||||
String flowRules =
|
||||
"[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, "
|
||||
+ "\"refResource\":null, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":" + maxQueueingTimeMs
|
||||
+ ", \"controller\":null}]";
|
||||
RedisPubSubCommands<String, String> subCommands = connection.sync();
|
||||
subCommands.multi();
|
||||
subCommands.set(ruleKey, flowRules);
|
||||
|
|
@ -113,7 +119,7 @@ public class StandLoneRedisDataSourceTest {
|
|||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
stringRedisCommands.del(ruleKey);
|
||||
String value = stringRedisCommands.get(ruleKey);
|
||||
Assert.assertEquals(value, null);
|
||||
Assert.assertNull(value);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -126,17 +132,14 @@ public class StandLoneRedisDataSourceTest {
|
|||
}
|
||||
|
||||
private Converter<String, List<FlowRule>> buildFlowConfigParser() {
|
||||
return new Converter<String, List<FlowRule>>() {
|
||||
@Override
|
||||
public List<FlowRule> convert(String source) {
|
||||
return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
|
||||
});
|
||||
}
|
||||
};
|
||||
return source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
|
||||
}
|
||||
|
||||
private void initRedisRuleData() {
|
||||
String flowRulesJson = "[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, \"refResource\":null, " +
|
||||
String flowRulesJson =
|
||||
"[{\"resource\":\"test\", \"limitApp\":\"default\", \"grade\":1, \"count\":\"0.0\", \"strategy\":0, "
|
||||
+ "\"refResource\":null, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":500, \"controller\":null}]";
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String ok = stringRedisCommands.set(ruleKey, flowRulesJson);
|
||||
Loading…
Reference in New Issue