Support Redis cluster mode in Redis data-source extension (#1751)
This commit is contained in:
parent
217cccd704
commit
3e438b3dba
|
|
@ -57,6 +57,24 @@ public <T> void pushRules(List<T> rules, Converter<List<T>, String> encoder) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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
|
## How to build RedisConnectionConfig
|
||||||
|
|
||||||
### Build with Redis standalone mode
|
### Build with Redis standalone mode
|
||||||
|
|
@ -79,3 +97,11 @@ RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||||
.withRedisSentinel("redisSentinelServer2",5001)
|
.withRedisSentinel("redisSentinelServer2",5001)
|
||||||
.withRedisSentinelMasterId("redisSentinelMasterId").build();
|
.withRedisSentinelMasterId("redisSentinelMasterId").build();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build with Redis Cluster mode
|
||||||
|
|
||||||
|
```java
|
||||||
|
RedisConnectionConfig config = RedisConnectionConfig.builder()
|
||||||
|
.withRedisCluster("redisSentinelServer1",5000)
|
||||||
|
.withRedisCluster("redisSentinelServer2",5001).build();
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,16 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
import io.lettuce.core.RedisClient;
|
import io.lettuce.core.RedisClient;
|
||||||
import io.lettuce.core.RedisURI;
|
import io.lettuce.core.RedisURI;
|
||||||
import io.lettuce.core.api.sync.RedisCommands;
|
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.RedisPubSubAdapter;
|
||||||
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
|
||||||
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
|
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -59,6 +64,8 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||||
|
|
||||||
private final RedisClient redisClient;
|
private final RedisClient redisClient;
|
||||||
|
|
||||||
|
private final RedisClusterClient redisClusterClient;
|
||||||
|
|
||||||
private final String ruleKey;
|
private final String ruleKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -75,7 +82,13 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||||
AssertUtil.notNull(connectionConfig, "Redis connection config can not be null");
|
AssertUtil.notNull(connectionConfig, "Redis connection config can not be null");
|
||||||
AssertUtil.notEmpty(ruleKey, "Redis ruleKey can not be empty");
|
AssertUtil.notEmpty(ruleKey, "Redis ruleKey can not be empty");
|
||||||
AssertUtil.notEmpty(channel, "Redis subscribe channel can not be empty");
|
AssertUtil.notEmpty(channel, "Redis subscribe channel can not be empty");
|
||||||
|
if (connectionConfig.getRedisClusters().size() == 0) {
|
||||||
this.redisClient = getRedisClient(connectionConfig);
|
this.redisClient = getRedisClient(connectionConfig);
|
||||||
|
this.redisClusterClient = null;
|
||||||
|
} else {
|
||||||
|
this.redisClusterClient = getRedisClusterClient(connectionConfig);
|
||||||
|
this.redisClient = null;
|
||||||
|
}
|
||||||
this.ruleKey = ruleKey;
|
this.ruleKey = ruleKey;
|
||||||
loadInitialConfig();
|
loadInitialConfig();
|
||||||
subscribeFromChannel(channel);
|
subscribeFromChannel(channel);
|
||||||
|
|
@ -96,6 +109,26 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
private RedisClient getRedisStandaloneClient(RedisConnectionConfig connectionConfig) {
|
||||||
char[] password = connectionConfig.getPassword();
|
char[] password = connectionConfig.getPassword();
|
||||||
String clientName = connectionConfig.getClientName();
|
String clientName = connectionConfig.getClientName();
|
||||||
|
|
@ -132,11 +165,18 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void subscribeFromChannel(String channel) {
|
private void subscribeFromChannel(String channel) {
|
||||||
StatefulRedisPubSubConnection<String, String> pubSubConnection = redisClient.connectPubSub();
|
|
||||||
RedisPubSubAdapter<String, String> adapterListener = new DelegatingRedisPubSubListener();
|
RedisPubSubAdapter<String, String> adapterListener = new DelegatingRedisPubSubListener();
|
||||||
|
if (redisClient != null) {
|
||||||
|
StatefulRedisPubSubConnection<String, String> pubSubConnection = redisClient.connectPubSub();
|
||||||
pubSubConnection.addListener(adapterListener);
|
pubSubConnection.addListener(adapterListener);
|
||||||
RedisPubSubCommands<String, String> sync = pubSubConnection.sync();
|
RedisPubSubCommands<String, String> sync = pubSubConnection.sync();
|
||||||
sync.subscribe(channel);
|
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() {
|
private void loadInitialConfig() {
|
||||||
|
|
@ -153,16 +193,27 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String readSource() {
|
public String readSource() {
|
||||||
if (this.redisClient == null) {
|
if (this.redisClient == null && this.redisClusterClient == null) {
|
||||||
throw new IllegalStateException("Redis client has not been initialized or error occurred");
|
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();
|
RedisCommands<String, String> stringRedisCommands = redisClient.connect().sync();
|
||||||
return stringRedisCommands.get(ruleKey);
|
return stringRedisCommands.get(ruleKey);
|
||||||
|
} else {
|
||||||
|
RedisAdvancedClusterCommands<String, String> stringRedisCommands = redisClusterClient.connect().sync();
|
||||||
|
return stringRedisCommands.get(ruleKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
if (redisClient != null) {
|
||||||
redisClient.shutdown();
|
redisClient.shutdown();
|
||||||
|
} else {
|
||||||
|
redisClusterClient.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DelegatingRedisPubSubListener extends RedisPubSubAdapter<String, String> {
|
private class DelegatingRedisPubSubListener extends RedisPubSubAdapter<String, String> {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ public class RedisConnectionConfig {
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_SENTINEL_PORT = 26379;
|
public static final int DEFAULT_SENTINEL_PORT = 26379;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default redisCluster port.
|
||||||
|
*/
|
||||||
|
public static final int DEFAULT_CLUSTER_PORT = 6379;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default redis port.
|
* The default redis port.
|
||||||
*/
|
*/
|
||||||
|
|
@ -51,6 +56,7 @@ public class RedisConnectionConfig {
|
||||||
private char[] password;
|
private char[] password;
|
||||||
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
||||||
private final List<RedisConnectionConfig> redisSentinels = new ArrayList<RedisConnectionConfig>();
|
private final List<RedisConnectionConfig> redisSentinels = new ArrayList<RedisConnectionConfig>();
|
||||||
|
private final List<RedisConnectionConfig> redisClusters = new ArrayList<RedisConnectionConfig>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default empty constructor.
|
* Default empty constructor.
|
||||||
|
|
@ -238,6 +244,13 @@ public class RedisConnectionConfig {
|
||||||
return redisSentinels;
|
return redisSentinels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of {@link RedisConnectionConfig Redis Cluster URIs}.
|
||||||
|
*/
|
||||||
|
public List<RedisConnectionConfig> getRedisClusters() {
|
||||||
|
return redisClusters;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
@ -254,6 +267,10 @@ public class RedisConnectionConfig {
|
||||||
sb.append(", redisSentinelMasterId=").append(redisSentinelMasterId);
|
sb.append(", redisSentinelMasterId=").append(redisSentinelMasterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (redisClusters.size() > 0) {
|
||||||
|
sb.append("redisClusters=").append(getRedisClusters());
|
||||||
|
}
|
||||||
|
|
||||||
sb.append(']');
|
sb.append(']');
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -281,6 +298,10 @@ public class RedisConnectionConfig {
|
||||||
: redisURI.redisSentinelMasterId != null) {
|
: redisURI.redisSentinelMasterId != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (redisClusters != null ? !redisClusters.equals(redisURI.redisClusters)
|
||||||
|
: redisURI.redisClusters != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return !(redisSentinels != null ? !redisSentinels.equals(redisURI.redisSentinels)
|
return !(redisSentinels != null ? !redisSentinels.equals(redisURI.redisSentinels)
|
||||||
: redisURI.redisSentinels != null);
|
: redisURI.redisSentinels != null);
|
||||||
|
|
||||||
|
|
@ -293,6 +314,7 @@ public class RedisConnectionConfig {
|
||||||
result = 31 * result + port;
|
result = 31 * result + port;
|
||||||
result = 31 * result + database;
|
result = 31 * result + database;
|
||||||
result = 31 * result + (redisSentinels != null ? redisSentinels.hashCode() : 0);
|
result = 31 * result + (redisSentinels != null ? redisSentinels.hashCode() : 0);
|
||||||
|
result = 31 * result + (redisClusters != null ? redisClusters.hashCode() : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,6 +331,7 @@ public class RedisConnectionConfig {
|
||||||
private char[] password;
|
private char[] password;
|
||||||
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
|
||||||
private final List<RedisHostAndPort> redisSentinels = new ArrayList<RedisHostAndPort>();
|
private final List<RedisHostAndPort> redisSentinels = new ArrayList<RedisHostAndPort>();
|
||||||
|
private final List<RedisHostAndPort> redisClusters = new ArrayList<RedisHostAndPort>();
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
}
|
}
|
||||||
|
|
@ -424,6 +447,63 @@ public class RedisConnectionConfig {
|
||||||
return this;
|
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.
|
* Adds host information to the builder. Does only affect Redis URI, cannot be used with Sentinel connections.
|
||||||
*
|
*
|
||||||
|
|
@ -544,9 +624,9 @@ public class RedisConnectionConfig {
|
||||||
*/
|
*/
|
||||||
public RedisConnectionConfig build() {
|
public RedisConnectionConfig build() {
|
||||||
|
|
||||||
if (redisSentinels.isEmpty() && StringUtil.isEmpty(host)) {
|
if (redisSentinels.isEmpty() && redisClusters.isEmpty() && StringUtil.isEmpty(host)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Cannot build a RedisConnectionConfig. One of the following must be provided Host, Socket or "
|
"Cannot build a RedisConnectionConfig. One of the following must be provided Host, Socket, Cluster or "
|
||||||
+ "Sentinel");
|
+ "Sentinel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -568,6 +648,11 @@ public class RedisConnectionConfig {
|
||||||
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (RedisHostAndPort sentinel : redisClusters) {
|
||||||
|
redisURI.getRedisClusters().add(
|
||||||
|
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
|
||||||
|
}
|
||||||
|
|
||||||
redisURI.setTimeout(timeout);
|
redisURI.setTimeout(timeout);
|
||||||
|
|
||||||
return redisURI;
|
return redisURI;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -94,4 +94,43 @@ public class RedisConnectionConfigTest {
|
||||||
Assert.assertNull(redisConnectionConfig.getHost());
|
Assert.assertNull(redisConnectionConfig.getHost());
|
||||||
Assert.assertEquals(3, redisConnectionConfig.getRedisSentinels().size());
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue