init
This commit is contained in:
107
sentinel/sentinel-extension/sentinel-datasource-redis/README.md
Normal file
107
sentinel/sentinel-extension/sentinel-datasource-redis/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Sentinel DataSource Redis
|
||||
|
||||
Sentinel DataSource Redis provides integration with Redis. The data source leverages Redis pub-sub feature to implement push model (listener).
|
||||
|
||||
The data source uses [Lettuce](https://lettuce.io/) as the Redis client, which 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:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-redis</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
|
||||
```
|
||||
|
||||
Then you can create an `RedisDataSource` and register to rule managers.
|
||||
For instance:
|
||||
|
||||
```java
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(redisConnectionConfig, ruleKey, channel, flowConfigParser);
|
||||
FlowRuleManager.register2Property(redisDataSource.getProperty());
|
||||
```
|
||||
|
||||
- `redisConnectionConfig`: use `RedisConnectionConfig` class to build your Redis connection config
|
||||
- `ruleKey`: the rule persistence key of a Redis String
|
||||
- `channel`: the channel to subscribe
|
||||
|
||||
You can also create multi data sources to subscribe for different rule type.
|
||||
|
||||
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
|
||||
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();
|
||||
}
|
||||
```
|
||||
|
||||
Transaction can be handled in Redis Cluster when just using the same key.
|
||||
|
||||
An example using Lettuce Redis Cluster client:
|
||||
|
||||
```java
|
||||
public <T> void pushRules(List<T> rules, Converter<List<T>, String> encoder) {
|
||||
RedisAdvancedClusterCommands<String, String> subCommands = client.connect().sync();
|
||||
int slot = SlotHash.getSlot(ruleKey);
|
||||
NodeSelection<String, String> nodes = subCommands.nodes((n)->n.hasSlot(slot));
|
||||
RedisCommands<String, String> commands = nodes.commands(0);
|
||||
String value = encoder.convert(rules);
|
||||
commands.multi();
|
||||
commands.set(ruleKey, value);
|
||||
commands.publish(channel, value);
|
||||
commands.exec();
|
||||
}
|
||||
```
|
||||
|
||||
## How to build RedisConnectionConfig
|
||||
|
||||
### Build with Redis standalone mode
|
||||
|
||||
```java
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withHost("localhost")
|
||||
.withPort(6379)
|
||||
.withPassword("pwd")
|
||||
.withDataBase(2)
|
||||
.build();
|
||||
|
||||
```
|
||||
|
||||
### Build with Redis Sentinel mode
|
||||
|
||||
```java
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withRedisSentinel("redisSentinelServer1",5000)
|
||||
.withRedisSentinel("redisSentinelServer2",5001)
|
||||
.withRedisSentinelMasterId("redisSentinelMasterId").build();
|
||||
```
|
||||
|
||||
### Build with Redis Cluster mode
|
||||
|
||||
```java
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withRedisCluster("redisSentinelServer1",5000)
|
||||
.withRedisCluster("redisSentinelServer2",5001).build();
|
||||
```
|
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-extension</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-datasource-redis</artifactId>
|
||||
<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>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.grakn</groupId>
|
||||
<artifactId>redis-mock</artifactId>
|
||||
<version>${redis.mock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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.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;
|
||||
import io.lettuce.core.cluster.RedisClusterClient;
|
||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||
import io.lettuce.core.cluster.pubsub.StatefulRedisClusterPubSubConnection;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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 final RedisClient redisClient;
|
||||
|
||||
private final RedisClusterClient redisClusterClient;
|
||||
|
||||
private final String ruleKey;
|
||||
|
||||
/**
|
||||
* Constructor of {@code RedisDataSource}.
|
||||
*
|
||||
* @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) {
|
||||
super(parser);
|
||||
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");
|
||||
if (connectionConfig.getRedisClusters().size() == 0) {
|
||||
this.redisClient = getRedisClient(connectionConfig);
|
||||
this.redisClusterClient = null;
|
||||
} else {
|
||||
this.redisClusterClient = getRedisClusterClient(connectionConfig);
|
||||
this.redisClient = null;
|
||||
}
|
||||
this.ruleKey = ruleKey;
|
||||
loadInitialConfig();
|
||||
subscribeFromChannel(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Redis client fromm {@code RedisConnectionConfig}.
|
||||
*
|
||||
* @return a new {@link RedisClient}
|
||||
*/
|
||||
private RedisClient getRedisClient(RedisConnectionConfig connectionConfig) {
|
||||
if (connectionConfig.getRedisSentinels().size() == 0) {
|
||||
RecordLog.info("[RedisDataSource] Creating stand-alone mode Redis client");
|
||||
return getRedisStandaloneClient(connectionConfig);
|
||||
} else {
|
||||
RecordLog.info("[RedisDataSource] Creating Redis Sentinel mode Redis client");
|
||||
return getRedisSentinelClient(connectionConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private RedisClusterClient getRedisClusterClient(RedisConnectionConfig connectionConfig) {
|
||||
char[] password = connectionConfig.getPassword();
|
||||
String clientName = connectionConfig.getClientName();
|
||||
|
||||
//If any uri is successful for connection, the others are not tried anymore
|
||||
List<RedisURI> redisUris = new ArrayList<>();
|
||||
for (RedisConnectionConfig config : connectionConfig.getRedisClusters()) {
|
||||
RedisURI.Builder clusterRedisUriBuilder = RedisURI.builder();
|
||||
clusterRedisUriBuilder.withHost(config.getHost())
|
||||
.withPort(config.getPort())
|
||||
.withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
|
||||
//All redis nodes must have same password
|
||||
if (password != null) {
|
||||
clusterRedisUriBuilder.withPassword(connectionConfig.getPassword());
|
||||
}
|
||||
redisUris.add(clusterRedisUriBuilder.build());
|
||||
}
|
||||
return RedisClusterClient.create(redisUris);
|
||||
}
|
||||
|
||||
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(Duration.ofMillis(connectionConfig.getTimeout()));
|
||||
if (password != null) {
|
||||
redisUriBuilder.withPassword(connectionConfig.getPassword());
|
||||
}
|
||||
if (StringUtil.isNotEmpty(connectionConfig.getClientName())) {
|
||||
redisUriBuilder.withClientName(clientName);
|
||||
}
|
||||
return RedisClient.create(redisUriBuilder.build());
|
||||
}
|
||||
|
||||
private RedisClient getRedisSentinelClient(RedisConnectionConfig connectionConfig) {
|
||||
char[] password = connectionConfig.getPassword();
|
||||
String clientName = connectionConfig.getClientName();
|
||||
RedisURI.Builder sentinelRedisUriBuilder = RedisURI.builder();
|
||||
for (RedisConnectionConfig config : connectionConfig.getRedisSentinels()) {
|
||||
sentinelRedisUriBuilder.withSentinel(config.getHost(), config.getPort());
|
||||
}
|
||||
if (password != null) {
|
||||
sentinelRedisUriBuilder.withPassword(connectionConfig.getPassword());
|
||||
}
|
||||
if (StringUtil.isNotEmpty(connectionConfig.getClientName())) {
|
||||
sentinelRedisUriBuilder.withClientName(clientName);
|
||||
}
|
||||
sentinelRedisUriBuilder.withSentinelMasterId(connectionConfig.getRedisSentinelMasterId())
|
||||
.withTimeout(connectionConfig.getTimeout(), TimeUnit.MILLISECONDS);
|
||||
return RedisClient.create(sentinelRedisUriBuilder.build());
|
||||
}
|
||||
|
||||
private void subscribeFromChannel(String channel) {
|
||||
RedisPubSubAdapter<String, String> adapterListener = new DelegatingRedisPubSubListener();
|
||||
if (redisClient != null) {
|
||||
StatefulRedisPubSubConnection<String, String> pubSubConnection = redisClient.connectPubSub();
|
||||
pubSubConnection.addListener(adapterListener);
|
||||
RedisPubSubCommands<String, String> sync = pubSubConnection.sync();
|
||||
sync.subscribe(channel);
|
||||
} else {
|
||||
StatefulRedisClusterPubSubConnection<String, String> pubSubConnection = redisClusterClient.connectPubSub();
|
||||
pubSubConnection.addListener(adapterListener);
|
||||
RedisPubSubCommands<String, String> sync = pubSubConnection.sync();
|
||||
sync.subscribe(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadInitialConfig() {
|
||||
try {
|
||||
T newValue = loadConfig();
|
||||
if (newValue == null) {
|
||||
RecordLog.warn("[RedisDataSource] WARN: initial config is null, you may have to check your data source");
|
||||
}
|
||||
getProperty().updateValue(newValue);
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[RedisDataSource] Error when loading initial config", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readSource() {
|
||||
if (this.redisClient == null && this.redisClusterClient == null) {
|
||||
throw new IllegalStateException("Redis client or Redis Cluster client has not been initialized or error occurred");
|
||||
}
|
||||
|
||||
if (redisClient != null) {
|
||||
RedisCommands<String, String> stringRedisCommands = redisClient.connect().sync();
|
||||
return stringRedisCommands.get(ruleKey);
|
||||
} else {
|
||||
RedisAdvancedClusterCommands<String, String> stringRedisCommands = redisClusterClient.connect().sync();
|
||||
return stringRedisCommands.get(ruleKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (redisClient != null) {
|
||||
redisClient.shutdown();
|
||||
} else {
|
||||
redisClusterClient.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DelegatingRedisPubSubListener extends RedisPubSubAdapter<String, String> {
|
||||
|
||||
DelegatingRedisPubSubListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void message(String channel, String message) {
|
||||
RecordLog.info("[RedisDataSource] New property value received for channel {}: {}", channel, message);
|
||||
getProperty().updateValue(parser.convert(message));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,668 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class provide a builder to build redis client connection config.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class RedisConnectionConfig {
|
||||
|
||||
/**
|
||||
* The default redisSentinel port.
|
||||
*/
|
||||
public static final int DEFAULT_SENTINEL_PORT = 26379;
|
||||
|
||||
/**
|
||||
* The default redisCluster port.
|
||||
*/
|
||||
public static final int DEFAULT_CLUSTER_PORT = 6379;
|
||||
|
||||
/**
|
||||
* The default redis port.
|
||||
*/
|
||||
public static final int DEFAULT_REDIS_PORT = 6379;
|
||||
|
||||
/**
|
||||
* Default timeout: 60 sec
|
||||
*/
|
||||
public static final long DEFAULT_TIMEOUT_MILLISECONDS = 60 * 1000;
|
||||
|
||||
private String host;
|
||||
private String redisSentinelMasterId;
|
||||
private int port;
|
||||
private int database;
|
||||
private String clientName;
|
||||
private char[] password;
|
||||
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
||||
private final List<RedisConnectionConfig> redisSentinels = new ArrayList<RedisConnectionConfig>();
|
||||
private final List<RedisConnectionConfig> redisClusters = new ArrayList<RedisConnectionConfig>();
|
||||
|
||||
/**
|
||||
* Default empty constructor.
|
||||
*/
|
||||
public RedisConnectionConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with host/port and timeout.
|
||||
*
|
||||
* @param host the host
|
||||
* @param port the port
|
||||
* @param timeout timeout value . unit is mill seconds
|
||||
*/
|
||||
public RedisConnectionConfig(String host, int port, long timeout) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.notNull(timeout, "Timeout duration must not be null");
|
||||
AssertUtil.isTrue(timeout >= 0, "Timeout duration must be greater or equal to zero");
|
||||
|
||||
setHost(host);
|
||||
setPort(port);
|
||||
setTimeout(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link RedisConnectionConfig.Builder} to construct a {@link RedisConnectionConfig}.
|
||||
*
|
||||
* @return a new {@link RedisConnectionConfig.Builder} to construct a {@link RedisConnectionConfig}.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder builder() {
|
||||
return new RedisConnectionConfig.Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host.
|
||||
*
|
||||
* @return the host.
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis host.
|
||||
*
|
||||
* @param host the host
|
||||
*/
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Sentinel Master Id.
|
||||
*
|
||||
* @return the Sentinel Master Id.
|
||||
*/
|
||||
public String getRedisSentinelMasterId() {
|
||||
return redisSentinelMasterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Sentinel Master Id.
|
||||
*
|
||||
* @param redisSentinelMasterId the Sentinel Master Id.
|
||||
*/
|
||||
public void setRedisSentinelMasterId(String redisSentinelMasterId) {
|
||||
this.redisSentinelMasterId = redisSentinelMasterId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Redis port.
|
||||
*
|
||||
* @return the Redis port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis port. Defaults to {@link #DEFAULT_REDIS_PORT}.
|
||||
*
|
||||
* @param port the Redis port
|
||||
*/
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the password.
|
||||
*
|
||||
* @return the password
|
||||
*/
|
||||
public char[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password. Use empty string to skip authentication.
|
||||
*
|
||||
* @param password the password, must not be {@literal null}.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
|
||||
AssertUtil.notNull(password, "Password must not be null");
|
||||
this.password = password.toCharArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password. Use empty char array to skip authentication.
|
||||
*
|
||||
* @param password the password, must not be {@literal null}.
|
||||
*/
|
||||
public void setPassword(char[] password) {
|
||||
|
||||
AssertUtil.notNull(password, "Password must not be null");
|
||||
this.password = Arrays.copyOf(password, password.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the command timeout for synchronous command execution.
|
||||
*
|
||||
* @return the Timeout
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command timeout for synchronous command execution.
|
||||
*
|
||||
* @param timeout the command timeout for synchronous command execution.
|
||||
*/
|
||||
public void setTimeout(Long timeout) {
|
||||
|
||||
AssertUtil.notNull(timeout, "Timeout must not be null");
|
||||
AssertUtil.isTrue(timeout >= 0, "Timeout must be greater or equal 0");
|
||||
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Redis database number. Databases are only available for Redis Standalone and Redis Master/Slave.
|
||||
*
|
||||
* @return database
|
||||
*/
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Redis database number. Databases are only available for Redis Standalone and Redis Master/Slave.
|
||||
*
|
||||
* @param database the Redis database number.
|
||||
*/
|
||||
public void setDatabase(int database) {
|
||||
|
||||
AssertUtil.isTrue(database >= 0, "Invalid database number: " + database);
|
||||
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getClientName() {
|
||||
return clientName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client name to be applied on Redis connections.
|
||||
*
|
||||
* @param clientName the client name.
|
||||
*/
|
||||
public void setClientName(String clientName) {
|
||||
this.clientName = clientName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of {@link RedisConnectionConfig Redis Sentinel URIs}.
|
||||
*/
|
||||
public List<RedisConnectionConfig> getRedisSentinels() {
|
||||
return redisSentinels;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of {@link RedisConnectionConfig Redis Cluster URIs}.
|
||||
*/
|
||||
public List<RedisConnectionConfig> getRedisClusters() {
|
||||
return redisClusters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName());
|
||||
|
||||
sb.append(" [");
|
||||
|
||||
if (host != null) {
|
||||
sb.append("host='").append(host).append('\'');
|
||||
sb.append(", port=").append(port);
|
||||
}
|
||||
if (redisSentinelMasterId != null) {
|
||||
sb.append("redisSentinels=").append(getRedisSentinels());
|
||||
sb.append(", redisSentinelMasterId=").append(redisSentinelMasterId);
|
||||
}
|
||||
|
||||
if (redisClusters.size() > 0) {
|
||||
sb.append("redisClusters=").append(getRedisClusters());
|
||||
}
|
||||
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof RedisConnectionConfig)) {
|
||||
return false;
|
||||
}
|
||||
RedisConnectionConfig redisURI = (RedisConnectionConfig)o;
|
||||
|
||||
if (port != redisURI.port) {
|
||||
return false;
|
||||
}
|
||||
if (database != redisURI.database) {
|
||||
return false;
|
||||
}
|
||||
if (host != null ? !host.equals(redisURI.host) : redisURI.host != null) {
|
||||
return false;
|
||||
}
|
||||
if (redisSentinelMasterId != null ? !redisSentinelMasterId.equals(redisURI.redisSentinelMasterId)
|
||||
: redisURI.redisSentinelMasterId != null) {
|
||||
return false;
|
||||
}
|
||||
if (redisClusters != null ? !redisClusters.equals(redisURI.redisClusters)
|
||||
: redisURI.redisClusters != null) {
|
||||
return false;
|
||||
}
|
||||
return !(redisSentinels != null ? !redisSentinels.equals(redisURI.redisSentinels)
|
||||
: redisURI.redisSentinels != null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = host != null ? host.hashCode() : 0;
|
||||
result = 31 * result + (redisSentinelMasterId != null ? redisSentinelMasterId.hashCode() : 0);
|
||||
result = 31 * result + port;
|
||||
result = 31 * result + database;
|
||||
result = 31 * result + (redisSentinels != null ? redisSentinels.hashCode() : 0);
|
||||
result = 31 * result + (redisClusters != null ? redisClusters.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for Redis RedisConnectionConfig.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private String host;
|
||||
private String redisSentinelMasterId;
|
||||
private int port;
|
||||
private int database;
|
||||
private String clientName;
|
||||
private char[] password;
|
||||
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
||||
private final List<RedisHostAndPort> redisSentinels = new ArrayList<RedisHostAndPort>();
|
||||
private final List<RedisHostAndPort> redisClusters = new ArrayList<RedisHostAndPort>();
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Redis host. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @return New builder with Redis host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redis(String host) {
|
||||
return redis(host, DEFAULT_REDIS_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Redis host and port. Creates a new builder
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @return New builder with Redis host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redis(String host, int port) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withHost(host).withPort(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Sentinel host. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @return New builder with Sentinel host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisSentinel(String host) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
|
||||
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withRedisSentinel(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Sentinel host and port. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @return New builder with Sentinel host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisSentinel(String host, int port) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withRedisSentinel(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Sentinel host and master id. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param masterId redisSentinel master id
|
||||
* @return New builder with Sentinel host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisSentinel(String host, String masterId) {
|
||||
return redisSentinel(host, DEFAULT_SENTINEL_PORT, masterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Sentinel host, port and master id. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @param masterId redisSentinel master id
|
||||
* @return New builder with Sentinel host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisSentinel(String host, int port, String masterId) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withSentinelMasterId(masterId).withRedisSentinel(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a withRedisSentinel host to the existing builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withRedisSentinel(String host) {
|
||||
return withRedisSentinel(host, DEFAULT_SENTINEL_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a withRedisSentinel host/port to the existing builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withRedisSentinel(String host, int port) {
|
||||
|
||||
AssertUtil.assertState(this.host == null, "Cannot use with Redis mode.");
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
redisSentinels.add(RedisHostAndPort.of(host, port));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Cluster host. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @return New builder with Cluster host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisCluster(String host) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
|
||||
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withRedisCluster(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Cluster host and port. Creates a new builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @return New builder with Cluster host/port.
|
||||
*/
|
||||
public static RedisConnectionConfig.Builder redisCluster(String host, int port) {
|
||||
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder();
|
||||
return builder.withRedisCluster(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a withRedisCluster host to the existing builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withRedisCluster(String host) {
|
||||
return withRedisCluster(host, DEFAULT_CLUSTER_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a withRedisCluster host/port to the existing builder.
|
||||
*
|
||||
* @param host the host name
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withRedisCluster(String host, int port) {
|
||||
|
||||
AssertUtil.assertState(this.host == null, "Cannot use with Redis mode.");
|
||||
AssertUtil.notEmpty(host, "Host must not be empty");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
redisClusters.add(RedisHostAndPort.of(host, port));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds host information to the builder. Does only affect Redis URI, cannot be used with Sentinel connections.
|
||||
*
|
||||
* @param host the port
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withHost(String host) {
|
||||
|
||||
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;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds port information to the builder. Does only affect Redis URI, cannot be used with Sentinel connections.
|
||||
*
|
||||
* @param port the port
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withPort(int port) {
|
||||
|
||||
AssertUtil.assertState(this.host != null, "Host is null. Cannot use in Sentinel mode.");
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the database number.
|
||||
*
|
||||
* @param database the database number
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withDatabase(int database) {
|
||||
|
||||
AssertUtil.isTrue(database >= 0, "Invalid database number: " + database);
|
||||
|
||||
this.database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a client name.
|
||||
*
|
||||
* @param clientName the client name
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withClientName(String clientName) {
|
||||
|
||||
AssertUtil.notNull(clientName, "Client name must not be null");
|
||||
|
||||
this.clientName = clientName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures authentication.
|
||||
*
|
||||
* @param password the password
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withPassword(String password) {
|
||||
|
||||
AssertUtil.notNull(password, "Password must not be null");
|
||||
|
||||
return withPassword(password.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures authentication.
|
||||
*
|
||||
* @param password the password
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withPassword(char[] password) {
|
||||
|
||||
AssertUtil.notNull(password, "Password must not be null");
|
||||
|
||||
this.password = Arrays.copyOf(password, password.length);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a timeout.
|
||||
*
|
||||
* @param timeout must not be {@literal null} or negative.
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withTimeout(long timeout) {
|
||||
|
||||
AssertUtil.notNull(timeout, "Timeout must not be null");
|
||||
AssertUtil.notNull(timeout >= 0, "Timeout must be greater or equal 0");
|
||||
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a redisSentinel master Id.
|
||||
*
|
||||
* @param sentinelMasterId redisSentinel master id, must not be empty or {@literal null}
|
||||
* @return the builder
|
||||
*/
|
||||
public RedisConnectionConfig.Builder withSentinelMasterId(String sentinelMasterId) {
|
||||
|
||||
AssertUtil.notEmpty(sentinelMasterId, "Sentinel master id must not empty");
|
||||
|
||||
this.redisSentinelMasterId = sentinelMasterId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the RedisConnectionConfig.
|
||||
*/
|
||||
public RedisConnectionConfig build() {
|
||||
|
||||
if (redisSentinels.isEmpty() && redisClusters.isEmpty() && StringUtil.isEmpty(host)) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot build a RedisConnectionConfig. One of the following must be provided Host, Socket, Cluster or "
|
||||
+ "Sentinel");
|
||||
}
|
||||
|
||||
RedisConnectionConfig redisURI = new RedisConnectionConfig();
|
||||
redisURI.setHost(host);
|
||||
redisURI.setPort(port);
|
||||
|
||||
if (password != null) {
|
||||
redisURI.setPassword(password);
|
||||
}
|
||||
|
||||
redisURI.setDatabase(database);
|
||||
redisURI.setClientName(clientName);
|
||||
|
||||
redisURI.setRedisSentinelMasterId(redisSentinelMasterId);
|
||||
|
||||
for (RedisHostAndPort sentinel : redisSentinels) {
|
||||
redisURI.getRedisSentinels().add(
|
||||
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||
}
|
||||
|
||||
for (RedisHostAndPort sentinel : redisClusters) {
|
||||
redisURI.getRedisClusters().add(
|
||||
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||
}
|
||||
|
||||
redisURI.setTimeout(timeout);
|
||||
|
||||
return redisURI;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true for valid port numbers.
|
||||
*/
|
||||
private static boolean isValidPort(int port) {
|
||||
return port >= 0 && port <= 65535;
|
||||
}
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* An immutable representation of a host and port.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class RedisHostAndPort {
|
||||
|
||||
private static final int NO_PORT = -1;
|
||||
|
||||
public final String host;
|
||||
public final int port;
|
||||
|
||||
/**
|
||||
* @param host must not be empty or {@literal null}.
|
||||
* @param port
|
||||
*/
|
||||
private RedisHostAndPort(String host, int port) {
|
||||
AssertUtil.notNull(host, "host must not be null");
|
||||
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RedisHostAndPort} of {@code host} and {@code port}
|
||||
*
|
||||
* @param host the hostname
|
||||
* @param port a valid port
|
||||
* @return the {@link RedisHostAndPort} of {@code host} and {@code port}
|
||||
*/
|
||||
public static RedisHostAndPort of(String host, int port) {
|
||||
AssertUtil.isTrue(isValidPort(port), String.format("Port out of range: %s", port));
|
||||
return new RedisHostAndPort(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if has a port.
|
||||
*/
|
||||
public boolean hasPort() {
|
||||
return port != NO_PORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the host text.
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the port.
|
||||
*/
|
||||
public int getPort() {
|
||||
if (!hasPort()) {
|
||||
throw new IllegalStateException("No port present.");
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof RedisHostAndPort)) {
|
||||
return false;
|
||||
}
|
||||
RedisHostAndPort that = (RedisHostAndPort)o;
|
||||
return port == that.port && (host != null ? host.equals(that.host) : that.host == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = host != null ? host.hashCode() : 0;
|
||||
result = 31 * result + port;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param port the port number
|
||||
* @return {@literal true} for valid port numbers.
|
||||
*/
|
||||
private static boolean isValidPort(int port) {
|
||||
return port >= 0 && port <= 65535;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(host);
|
||||
if (hasPort()) {
|
||||
sb.append(':').append(port);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 1999-2020 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;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
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.RedisURI;
|
||||
import io.lettuce.core.api.sync.RedisCommands;
|
||||
import io.lettuce.core.cluster.RedisClusterClient;
|
||||
import io.lettuce.core.cluster.SlotHash;
|
||||
import io.lettuce.core.cluster.api.sync.NodeSelection;
|
||||
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
/**
|
||||
* Redis redisCluster mode test cases for {@link RedisDataSource}.
|
||||
*
|
||||
* @author liqiangz
|
||||
*/
|
||||
@Ignore(value = "Before run this test, you need to set up your Redis Cluster.")
|
||||
public class ClusterModeRedisDataSourceTest {
|
||||
|
||||
private String host = "localhost";
|
||||
|
||||
private int redisSentinelPort = 7000;
|
||||
private final RedisClusterClient client = RedisClusterClient.create(RedisURI.Builder.redis(host, redisSentinelPort).build());
|
||||
private String ruleKey = "sentinel.rules.flow.ruleKey";
|
||||
private String channel = "sentinel.rules.flow.channel";
|
||||
|
||||
@Before
|
||||
public void initData() {
|
||||
Converter<String, List<FlowRule>> flowConfigParser = buildFlowConfigParser();
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withRedisCluster(host, redisSentinelPort).build();
|
||||
initRedisRuleData();
|
||||
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}]";
|
||||
RedisAdvancedClusterCommands<String, String> subCommands = client.connect().sync();
|
||||
int slot = SlotHash.getSlot(ruleKey);
|
||||
NodeSelection<String, String> nodes = subCommands.nodes((n) -> n.hasSlot(slot));
|
||||
RedisCommands<String, String> commands = nodes.commands(0);
|
||||
commands.multi();
|
||||
commands.set(ruleKey, flowRulesJson);
|
||||
commands.publish(channel, flowRulesJson);
|
||||
commands.exec();
|
||||
|
||||
await().timeout(2, TimeUnit.SECONDS)
|
||||
.until(new Callable<List<FlowRule>>() {
|
||||
@Override
|
||||
public List<FlowRule> call() throws Exception {
|
||||
return FlowRuleManager.getRules();
|
||||
}
|
||||
}, Matchers.hasSize(1));
|
||||
|
||||
List<FlowRule> rules = FlowRuleManager.getRules();
|
||||
Assert.assertEquals(rules.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
String value = subCommands.get(ruleKey);
|
||||
List<FlowRule> flowRulesValuesInRedis = buildFlowConfigParser().convert(value);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.size(), 1);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearResource() {
|
||||
RedisAdvancedClusterCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
stringRedisCommands.del(ruleKey);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
private Converter<String, List<FlowRule>> buildFlowConfigParser() {
|
||||
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, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":500, \"controller\":null}]";
|
||||
RedisAdvancedClusterCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String ok = stringRedisCommands.set(ruleKey, flowRulesJson);
|
||||
Assert.assertEquals("OK", ok);
|
||||
}
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test cases for {@link RedisConnectionConfig}.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class RedisConnectionConfigTest {
|
||||
|
||||
@Test
|
||||
public void testRedisDefaultPropertySuccess() {
|
||||
String host = "localhost";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host).build();
|
||||
Assert.assertEquals(host, redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_REDIS_PORT, redisConnectionConfig.getPort());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_TIMEOUT_MILLISECONDS, redisConnectionConfig.getTimeout());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisClientNamePropertySuccess() {
|
||||
String host = "localhost";
|
||||
String clientName = "clientName";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host)
|
||||
.withClientName("clientName")
|
||||
.build();
|
||||
Assert.assertEquals(redisConnectionConfig.getClientName(), clientName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisTimeOutPropertySuccess() {
|
||||
String host = "localhost";
|
||||
long timeout = 70 * 1000;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host)
|
||||
.withTimeout(timeout)
|
||||
.build();
|
||||
Assert.assertEquals(redisConnectionConfig.getTimeout(), timeout);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisSentinelDefaultPortSuccess() {
|
||||
String host = "localhost";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(1, redisConnectionConfig.getRedisSentinels().size());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_SENTINEL_PORT,
|
||||
redisConnectionConfig.getRedisSentinels().get(0).getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisSentinelMoreThanOneServerSuccess() {
|
||||
String host = "localhost";
|
||||
String host2 = "server2";
|
||||
int port2 = 1879;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(2, redisConnectionConfig.getRedisSentinels().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisSentinelMoreThanOneDuplicateServerSuccess() {
|
||||
String host = "localhost";
|
||||
String host2 = "server2";
|
||||
int port2 = 1879;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisSentinel(host)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withRedisSentinel(host2, port2)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(3, redisConnectionConfig.getRedisSentinels().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisClusterDefaultPortSuccess() {
|
||||
String host = "localhost";
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisCluster(host)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(1, redisConnectionConfig.getRedisClusters().size());
|
||||
Assert.assertEquals(RedisConnectionConfig.DEFAULT_CLUSTER_PORT,
|
||||
redisConnectionConfig.getRedisClusters().get(0).getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisClusterMoreThanOneServerSuccess() {
|
||||
String host = "localhost";
|
||||
String host2 = "server2";
|
||||
int port1 = 1879;
|
||||
int port2 = 1880;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisCluster(host, port1)
|
||||
.withRedisCluster(host2, port2)
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(2, redisConnectionConfig.getRedisClusters().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisClusterMoreThanOneDuplicateServerSuccess() {
|
||||
String host = "localhost";
|
||||
String host2 = "server2";
|
||||
int port2 = 1879;
|
||||
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redisCluster(host)
|
||||
.withRedisCluster(host2, port2)
|
||||
.withRedisCluster(host2, port2)
|
||||
.withPassword("211233")
|
||||
.build();
|
||||
Assert.assertNull(redisConnectionConfig.getHost());
|
||||
Assert.assertEquals(3, redisConnectionConfig.getRedisClusters().size());
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.alibaba.csp.sentinel.datasource.Converter;
|
||||
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
|
||||
import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig;
|
||||
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;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
/**
|
||||
* Redis redisSentinel mode test cases for {@link RedisDataSource}.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
@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 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());
|
||||
|
||||
@Before
|
||||
public void initData() {
|
||||
Converter<String, List<FlowRule>> flowConfigParser = buildFlowConfigParser();
|
||||
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withRedisSentinel(host, redisSentinelPort)
|
||||
.withSentinelMasterId(redisSentinelMasterId).build();
|
||||
initRedisRuleData();
|
||||
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}]";
|
||||
RedisCommands<String, String> subCommands = client.connect().sync();
|
||||
subCommands.multi();
|
||||
subCommands.set(ruleKey, flowRulesJson);
|
||||
subCommands.publish(channel, flowRulesJson);
|
||||
subCommands.exec();
|
||||
|
||||
await().timeout(2, TimeUnit.SECONDS)
|
||||
.until(new Callable<List<FlowRule>>() {
|
||||
@Override
|
||||
public List<FlowRule> call() throws Exception {
|
||||
return FlowRuleManager.getRules();
|
||||
}
|
||||
}, Matchers.hasSize(1));
|
||||
|
||||
List<FlowRule> rules = FlowRuleManager.getRules();
|
||||
Assert.assertEquals(rules.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
String value = subCommands.get(ruleKey);
|
||||
List<FlowRule> flowRulesValuesInRedis = buildFlowConfigParser().convert(value);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.size(), 1);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearResource() {
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
stringRedisCommands.del(ruleKey);
|
||||
client.shutdown();
|
||||
}
|
||||
|
||||
private Converter<String, List<FlowRule>> buildFlowConfigParser() {
|
||||
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, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":500, \"controller\":null}]";
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String ok = stringRedisCommands.set(ruleKey, flowRulesJson);
|
||||
Assert.assertEquals("OK", ok);
|
||||
}
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
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;
|
||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
|
||||
/**
|
||||
* Redis stand-alone mode test cases for {@link RedisDataSource}.
|
||||
*
|
||||
* @author tiger
|
||||
*/
|
||||
public class StandaloneRedisDataSourceTest {
|
||||
|
||||
private static RedisServer server = null;
|
||||
|
||||
private RedisClient client;
|
||||
|
||||
private String ruleKey = "sentinel.rules.flow.ruleKey";
|
||||
private String channel = "sentinel.rules.flow.channel";
|
||||
|
||||
@Before
|
||||
public void buildResource() {
|
||||
try {
|
||||
// Bind to a random port.
|
||||
server = RedisServer.newRedisServer();
|
||||
server.start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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();
|
||||
initRedisRuleData();
|
||||
ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<List<FlowRule>>(config,
|
||||
ruleKey, channel, flowConfigParser);
|
||||
FlowRuleManager.register2Property(redisDataSource.getProperty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPubMsgAndReceiveSuccess() {
|
||||
List<FlowRule> rules = FlowRuleManager.getRules();
|
||||
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}]";
|
||||
RedisPubSubCommands<String, String> subCommands = connection.sync();
|
||||
subCommands.multi();
|
||||
subCommands.set(ruleKey, flowRules);
|
||||
subCommands.publish(channel, flowRules);
|
||||
subCommands.exec();
|
||||
|
||||
await().timeout(2, TimeUnit.SECONDS)
|
||||
.until(new Callable<List<FlowRule>>() {
|
||||
@Override
|
||||
public List<FlowRule> call() throws Exception {
|
||||
return FlowRuleManager.getRules();
|
||||
}
|
||||
}, Matchers.hasSize(1));
|
||||
|
||||
rules = FlowRuleManager.getRules();
|
||||
Assert.assertEquals(rules.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
String value = subCommands.get(ruleKey);
|
||||
List<FlowRule> flowRulesValuesInRedis = buildFlowConfigParser().convert(value);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.size(), 1);
|
||||
Assert.assertEquals(flowRulesValuesInRedis.get(0).getMaxQueueingTimeMs(), maxQueueingTimeMs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitAndParseFlowRuleSuccess() {
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String value = stringRedisCommands.get(ruleKey);
|
||||
List<FlowRule> flowRules = buildFlowConfigParser().convert(value);
|
||||
Assert.assertEquals(flowRules.size(), 1);
|
||||
stringRedisCommands.del(ruleKey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadResourceFail() {
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
stringRedisCommands.del(ruleKey);
|
||||
String value = stringRedisCommands.get(ruleKey);
|
||||
Assert.assertNull(value);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clearResource() {
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
stringRedisCommands.del(ruleKey);
|
||||
client.shutdown();
|
||||
server.stop();
|
||||
server = null;
|
||||
}
|
||||
|
||||
private Converter<String, List<FlowRule>> buildFlowConfigParser() {
|
||||
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, "
|
||||
+
|
||||
"\"controlBehavior\":0, \"warmUpPeriodSec\":10, \"maxQueueingTimeMs\":500, \"controller\":null}]";
|
||||
RedisCommands<String, String> stringRedisCommands = client.connect().sync();
|
||||
String ok = stringRedisCommands.set(ruleKey, flowRulesJson);
|
||||
Assert.assertEquals(ok, "OK");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user