Support redis cluster

This commit is contained in:
hongqiaowei
2022-07-01 17:17:27 +08:00
parent e567214dff
commit ee5b406026
9 changed files with 459 additions and 55 deletions

View File

@@ -18,31 +18,46 @@
package we.config;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.TimeoutOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import java.time.Duration;
/**
* @author hongqiaowei
*/
public abstract class RedisReactiveConfig {
protected static final Logger log = LoggerFactory.getLogger(RedisReactiveConfig.class);
protected static final Logger LOGGER = LoggerFactory.getLogger(RedisReactiveConfig.class);
// this should not be changed unless there is a truely good reason to do so
private static final int ps = Runtime.getRuntime().availableProcessors();
public static final ClientResources clientResources = DefaultClientResources.builder()
.ioThreadPoolSize(ps)
.computationThreadPoolSize(ps)
.build();
// this should not be changed unless there is a good reason to do so
private static final int ps = Runtime.getRuntime().availableProcessors();
/**
* @deprecated and renamed to CLIENT_RESOURCES
*/
@Deprecated
public static final ClientResources clientResources = DefaultClientResources.builder()
.ioThreadPoolSize(ps)
.computationThreadPoolSize(ps)
.build();
public static final ClientResources CLIENT_RESOURCES = clientResources;
private RedisReactiveProperties redisReactiveProperties;
@@ -56,23 +71,91 @@ public abstract class RedisReactiveConfig {
public ReactiveRedisConnectionFactory lettuceConnectionFactory() {
log.info("connect to {}", redisReactiveProperties);
ReactiveRedisConnectionFactory reactiveRedisConnectionFactory;
RedisStandaloneConfiguration rcs = new RedisStandaloneConfiguration(redisReactiveProperties.getHost(), redisReactiveProperties.getPort());
String password = redisReactiveProperties.getPassword();
if (password != null) {
rcs.setPassword(password);
if (redisReactiveProperties.getType() == RedisReactiveProperties.STANDALONE) {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisReactiveProperties.getHost(), redisReactiveProperties.getPort());
String password = redisReactiveProperties.getPassword();
if (password != null) {
redisStandaloneConfiguration.setPassword(password);
}
redisStandaloneConfiguration.setDatabase(redisReactiveProperties.getDatabase());
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(poolConfig.getMaxTotal() * 2);
LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder()
.clientResources(clientResources)
.clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
.poolConfig(poolConfig)
.build();
reactiveRedisConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfiguration);
} else {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
String password = redisReactiveProperties.getPassword();
if (password != null) {
redisClusterConfiguration.setPassword(password);
}
redisClusterConfiguration.setClusterNodes(redisReactiveProperties.getClusterNodes());
int maxRedirects = redisReactiveProperties.getMaxRedirects();
if (maxRedirects > 0) {
redisClusterConfiguration.setMaxRedirects(maxRedirects);
}
ClusterTopologyRefreshOptions.Builder builder = ClusterTopologyRefreshOptions.builder();
int clusterRefreshPeriod = redisReactiveProperties.getClusterRefreshPeriod();
builder = builder.enablePeriodicRefresh(Duration.ofSeconds(clusterRefreshPeriod));
boolean enableAllAdaptiveRefreshTriggers = redisReactiveProperties.isEnableAllAdaptiveRefreshTriggers();
if (enableAllAdaptiveRefreshTriggers) {
builder = builder.enableAllAdaptiveRefreshTriggers();
}
ClusterTopologyRefreshOptions topologyRefreshOptions = builder.build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(clusterRefreshPeriod)))
.topologyRefreshOptions(topologyRefreshOptions)
.publishOnScheduler(true)
.build();
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
int minIdle = redisReactiveProperties.getMinIdle();
if (minIdle > 0) {
poolConfig.setMinIdle(minIdle);
}
int maxIdle = redisReactiveProperties.getMaxIdle();
if (maxIdle > 0) {
poolConfig.setMaxIdle(maxIdle);
}
int maxTotal = redisReactiveProperties.getMaxTotal();
if (maxTotal > 0) {
poolConfig.setMaxTotal(maxTotal);
} else {
poolConfig.setMaxTotal(poolConfig.getMaxTotal() * 2);
}
Duration maxWait = redisReactiveProperties.getMaxWait();
if (maxWait != null) {
poolConfig.setMaxWait(maxWait);
}
Duration timeBetweenEvictionRuns = redisReactiveProperties.getTimeBetweenEvictionRuns();
if (timeBetweenEvictionRuns != null) {
poolConfig.setTimeBetweenEvictionRuns(timeBetweenEvictionRuns);
}
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.clientResources(clientResources)
// .commandTimeout(Duration.ofSeconds(60))
.poolConfig(poolConfig)
.readFrom(redisReactiveProperties.getReadFrom())
.clientOptions(clusterClientOptions)
.build();
reactiveRedisConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration, clientConfig);
}
rcs.setDatabase(redisReactiveProperties.getDatabase());
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(poolConfig.getMaxTotal() * 2);
LettucePoolingClientConfiguration ccs = LettucePoolingClientConfiguration.builder()
.clientResources(clientResources)
.clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
.poolConfig(poolConfig)
.build();
return new LettuceConnectionFactory(rcs, ccs);
LOGGER.info("build reactive redis connection factory for {}", redisReactiveProperties);
return reactiveRedisConnectionFactory;
}
}

View File

@@ -17,20 +17,68 @@
package we.config;
import we.util.Constants;
import io.lettuce.core.ReadFrom;
import org.springframework.data.redis.connection.RedisNode;
import we.util.Consts;
import we.util.StringUtils;
import we.util.Utils;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
/**
* @author hongqiaowei
*/
public abstract class RedisReactiveProperties {
private String host = "127.0.0.1";
private int port = 6379;
private String password;
private int database = 0;
public static final String STANDALONE = "standalone";
public static final String CLUSTER = "cluster";
private String type = STANDALONE;
private String host = "127.0.0.1";
private int port = 6379;
private String password;
private int database = 0;
private List<RedisNode> clusterNodes;
private int maxRedirects = 0;
private int clusterRefreshPeriod = 60;
private boolean clusterRefreshAdaptive = true;
private boolean enableAllAdaptiveRefreshTriggers = true;
private ReadFrom readFrom = ReadFrom.REPLICA_PREFERRED;
private int minIdle = 0;
private int maxIdle = 0;
private int maxTotal = 0;
private Duration maxWait;
private Duration timeBetweenEvictionRuns;
public String getType() {
return type;
}
public void setType(String type) {
if (type.equals(STANDALONE)) {
this.type = STANDALONE;
} else {
this.type = CLUSTER;
}
}
public String getHost() {
return host;
@@ -64,19 +112,127 @@ public abstract class RedisReactiveProperties {
this.database = database;
}
public List<RedisNode> getClusterNodes() {
return clusterNodes;
}
public void setClusterNodes(String clusterNodes) {
String[] nodeArr = StringUtils.split(clusterNodes, ',');
this.clusterNodes = new ArrayList<>(nodeArr.length);
for (String n : nodeArr) {
String[] ipAndPort = StringUtils.split(n.trim(), ':');
RedisNode redisNode = new RedisNode(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
this.clusterNodes.add(redisNode);
}
}
public int getMaxRedirects() {
return maxRedirects;
}
public void setMaxRedirects(int maxRedirects) {
this.maxRedirects = maxRedirects;
}
public int getClusterRefreshPeriod() {
return clusterRefreshPeriod;
}
public void setClusterRefreshPeriod(int clusterRefreshPeriod) {
this.clusterRefreshPeriod = clusterRefreshPeriod;
}
public boolean isClusterRefreshAdaptive() {
return clusterRefreshAdaptive;
}
public void setClusterRefreshAdaptive(boolean clusterRefreshAdaptive) {
this.clusterRefreshAdaptive = clusterRefreshAdaptive;
}
public boolean isEnableAllAdaptiveRefreshTriggers() {
return enableAllAdaptiveRefreshTriggers;
}
public void setEnableAllAdaptiveRefreshTriggers(boolean enableAllAdaptiveRefreshTriggers) {
this.enableAllAdaptiveRefreshTriggers = enableAllAdaptiveRefreshTriggers;
}
public ReadFrom getReadFrom() {
return readFrom;
}
public void setReadFrom(String readFrom) {
this.readFrom = ReadFrom.valueOf(readFrom);
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaxTotal() {
return maxTotal;
}
public void setMaxTotal(int maxTotal) {
this.maxTotal = maxTotal;
}
public Duration getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = Duration.ofMillis(maxWait);
}
public Duration getTimeBetweenEvictionRuns() {
return timeBetweenEvictionRuns;
}
public void setTimeBetweenEvictionRuns(long timeBetweenEvictionRuns) {
this.timeBetweenEvictionRuns = Duration.ofMillis(timeBetweenEvictionRuns);
}
@Override
public String toString() {
StringBuilder b = new StringBuilder(48);
StringBuilder b = new StringBuilder(256);
appendTo(b);
return b.toString();
}
public void appendTo(StringBuilder b) {
b.append(Consts.S.LEFT_BRACE);
Utils.addTo(b, "host", Consts.S.EQUAL, host, Consts.S.SPACE_STR);
Utils.addTo(b, "port", Consts.S.EQUAL, port, Consts.S.SPACE_STR);
// Utils.addTo(b, "password", Consts.S.EQUAL, password, Consts.S.SPACE_STR);
Utils.addTo(b, "database", Consts.S.EQUAL, database, Consts.S.EMPTY);
if (type == STANDALONE) {
Utils.addTo(b, "host", Consts.S.EQUAL, host, Consts.S.SPACE_STR);
Utils.addTo(b, "port", Consts.S.EQUAL, port, Consts.S.SPACE_STR);
Utils.addTo(b, "database", Consts.S.EQUAL, database, Consts.S.SPACE_STR);
} else {
Utils.addTo(b, "clusterNodes", Consts.S.EQUAL, clusterNodes, Consts.S.SPACE_STR);
Utils.addTo(b, "maxRedirects", Consts.S.EQUAL, maxRedirects, Consts.S.SPACE_STR);
Utils.addTo(b, "clusterRefreshPeriod", Consts.S.EQUAL, clusterRefreshPeriod, Consts.S.SPACE_STR);
Utils.addTo(b, "clusterRefreshAdaptive", Consts.S.EQUAL, clusterRefreshAdaptive, Consts.S.SPACE_STR);
Utils.addTo(b, "enableAllAdaptiveRefreshTriggers", Consts.S.EQUAL, enableAllAdaptiveRefreshTriggers, Consts.S.SPACE_STR);
Utils.addTo(b, "readFrom", Consts.S.EQUAL, readFrom, Consts.S.SPACE_STR);
}
Utils.addTo(b, "minIdle", Consts.S.EQUAL, minIdle, Consts.S.SPACE_STR);
Utils.addTo(b, "maxIdle", Consts.S.EQUAL, maxIdle, Consts.S.SPACE_STR);
Utils.addTo(b, "maxWait", Consts.S.EQUAL, maxWait, Consts.S.SPACE_STR);
Utils.addTo(b, "maxTotal", Consts.S.EQUAL, maxTotal, Consts.S.SPACE_STR);
Utils.addTo(b, "timeBetweenEvictionRuns", Consts.S.EQUAL, timeBetweenEvictionRuns, Consts.S.EMPTY);
b.append(Consts.S.RIGHT_BRACE);
}
}

View File

@@ -18,13 +18,21 @@
package we.util;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.TimeoutOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import we.config.RedisReactiveConfig;
import we.config.RedisReactiveProperties;
import java.time.Duration;
/**
* @apiNote just helper, RedisReactiveConfig is best practice
@@ -38,26 +46,114 @@ public abstract class ReactiveRedisHelper {
private ReactiveRedisHelper() {
}
public static ReactiveRedisConnectionFactory getConnectionFactory(String host, int port, String password, int database) {
RedisStandaloneConfiguration rcs = new RedisStandaloneConfiguration(host, port);
if (password != null) {
rcs.setPassword(password);
public static ReactiveRedisConnectionFactory getConnectionFactory(RedisReactiveProperties redisReactiveProperties) {
if (redisReactiveProperties.getType() == RedisReactiveProperties.STANDALONE) {
return getConnectionFactory(redisReactiveProperties.getHost(), redisReactiveProperties.getPort(), redisReactiveProperties.getPassword(), redisReactiveProperties.getDatabase());
} else {
return getClusterConnectionFactory(redisReactiveProperties);
}
rcs.setDatabase(database);
}
public static ReactiveStringRedisTemplate getStringRedisTemplate(RedisReactiveProperties redisReactiveProperties) {
ReactiveRedisConnectionFactory connectionFactory = getConnectionFactory(redisReactiveProperties);
return new ReactiveStringRedisTemplate(connectionFactory);
}
/**
* For standalone redis.
*/
public static ReactiveRedisConnectionFactory getConnectionFactory(String host, int port, String password, int database) {
RedisStandaloneConfiguration rsc = new RedisStandaloneConfiguration(host, port);
if (password != null) {
rsc.setPassword(password);
}
rsc.setDatabase(database);
LettucePoolingClientConfiguration ccs = LettucePoolingClientConfiguration.builder()
.clientResources(RedisReactiveConfig.clientResources)
.clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
.poolConfig(new GenericObjectPoolConfig<>())
.build();
.clientResources(RedisReactiveConfig.CLIENT_RESOURCES)
.clientOptions(ClientOptions.builder().publishOnScheduler(true).build())
.poolConfig(new GenericObjectPoolConfig<>())
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(rcs, ccs);
LettuceConnectionFactory factory = new LettuceConnectionFactory(rsc, ccs);
factory.afterPropertiesSet();
return factory;
}
/**
* For standalone redis.
*/
public static ReactiveStringRedisTemplate getStringRedisTemplate(String host, int port, String password, int database) {
ReactiveRedisConnectionFactory connectionFactory = getConnectionFactory(host, port, password, database);
return new ReactiveStringRedisTemplate(connectionFactory);
}
public static ReactiveRedisConnectionFactory getClusterConnectionFactory(RedisReactiveProperties redisReactiveProperties) {
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
String password = redisReactiveProperties.getPassword();
if (password != null) {
redisClusterConfiguration.setPassword(password);
}
redisClusterConfiguration.setClusterNodes(redisReactiveProperties.getClusterNodes());
int maxRedirects = redisReactiveProperties.getMaxRedirects();
if (maxRedirects > 0) {
redisClusterConfiguration.setMaxRedirects(maxRedirects);
}
ClusterTopologyRefreshOptions.Builder builder = ClusterTopologyRefreshOptions.builder();
int clusterRefreshPeriod = redisReactiveProperties.getClusterRefreshPeriod();
builder = builder.enablePeriodicRefresh(Duration.ofSeconds(clusterRefreshPeriod));
boolean enableAllAdaptiveRefreshTriggers = redisReactiveProperties.isEnableAllAdaptiveRefreshTriggers();
if (enableAllAdaptiveRefreshTriggers) {
builder = builder.enableAllAdaptiveRefreshTriggers();
}
ClusterTopologyRefreshOptions topologyRefreshOptions = builder.build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(clusterRefreshPeriod)))
.topologyRefreshOptions(topologyRefreshOptions)
.publishOnScheduler(true)
.build();
GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
int minIdle = redisReactiveProperties.getMinIdle();
if (minIdle > 0) {
poolConfig.setMinIdle(minIdle);
}
int maxIdle = redisReactiveProperties.getMaxIdle();
if (maxIdle > 0) {
poolConfig.setMaxIdle(maxIdle);
}
int maxTotal = redisReactiveProperties.getMaxTotal();
if (maxTotal > 0) {
poolConfig.setMaxTotal(maxTotal);
} else {
poolConfig.setMaxTotal(poolConfig.getMaxTotal() * 2);
}
Duration maxWait = redisReactiveProperties.getMaxWait();
if (maxWait != null) {
poolConfig.setMaxWait(maxWait);
}
Duration timeBetweenEvictionRuns = redisReactiveProperties.getTimeBetweenEvictionRuns();
if (timeBetweenEvictionRuns != null) {
poolConfig.setTimeBetweenEvictionRuns(timeBetweenEvictionRuns);
}
LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.clientResources(RedisReactiveConfig.CLIENT_RESOURCES)
// .commandTimeout(Duration.ofSeconds(60))
.poolConfig(poolConfig)
.readFrom(redisReactiveProperties.getReadFrom())
.clientOptions(clusterClientOptions)
.build();
LettuceConnectionFactory reactiveRedisConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration, clientConfig);
reactiveRedisConnectionFactory.afterPropertiesSet();
return reactiveRedisConnectionFactory;
}
public static ReactiveStringRedisTemplate getClusterStringRedisTemplate(RedisReactiveProperties redisReactiveProperties) {
ReactiveRedisConnectionFactory connectionFactory = getClusterConnectionFactory(redisReactiveProperties);
return new ReactiveStringRedisTemplate(connectionFactory);
}
}