通过策略模式实现Redis几种集群模式
前言
Redis常用的几种部署模式分别为Standalone(单节点部署方式)、Sentinel (哨兵部署方式)、Cluster(集群方式)、Masterslave(主从部署方式),我们通过Java代码去连接redis的服务端实现不同的部署模式,当然也需要部署在服务端的Redis支持这种部署方式,我们可以根据策略模式来实现Redis不同的连接方式。
Redis连接策略枚举类
首先需要去定义Redis连接的枚举类,这里会将redis的几种连接方式定义成枚举;
public enum RedisConnectionTypeEnum {
STANDALONE("standalone", "单节点部署方式"),
SENTINEL("sentinel", "哨兵部署方式"),
CLUSTER("cluster", "集群方式"),
MASTERSLAVE("masterslave", "主从部署方式");
private final String connectionType;
private final String connectionDesc;
private RedisConnectionTypeEnum(String connectionType, String connectionDesc) {
this.connectionType = connectionType;
this.connectionDesc = connectionDesc;
}
public String getConnectionType() {
return connectionType;
}
public String getConnectionDesc() {
return connectionDesc;
}
}
Redis连接属性配置类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
@ConfigurationProperties(prefix = "spring.redis")
public class RedissonProperties {
private String nodeAddresses;
/**
* 连接类型,支持standalone-单机节点,sentinel-哨兵,cluster-集群,masterslave-主从
*/
private String serverType;
/**
* redis 连接密码
*/
private String password;
/**
* 如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,
* 那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。默认10000
*/
private int idleConnectionTimeout;
/**
* 建立连接等待超时时间
*/
private int connectTimeout;
/**
* 等待节点回复命令的时间。该时间从命令发送成功时开始计时。默认3000
*/
private int timeOut;
/***
* 失败重试次数
*/
private int retryAttempts;
/**
* 选取那个数据库
*/
private int database;
}
}
主要是一些Redis连接配置的常用属性,定义成Properties配置文件类,可以通过读取Nacos配置文件获取对应的属性
Redis Config创建的接口方法
public interface RedissonConfigService {
Config createRedissonConfig(RedissonProperties redissonProperties);
}
提供创建config连接的方法,可扩展不同连接模式可以实现RedissonConfigService接口,实现不同的连接模式,就是前面所说的策略模式,通过不同的策略类来定义不一样的连接模式;
ClusterConfigImpl实现类
@Slf4j
public class ClusterConfigImpl implements RedissonConfigService {
@Override
public Config createRedissonConfig(RedissonProperties redissonProperties) {
Config config =new Config();
try{
String nodeAddresses = redissonProperties.getNodeAddresses();
String password = redissonProperties.getPassword();
String[] split = nodeAddresses.split(",");
//设置cluster节点的服务IP和端口
for (int i = 0; i < split.length; i++) {
config.useClusterServers()
.addNodeAddress("redis://" + split[i]);
if (StringUtils.hasText(password)) {
config.useClusterServers().setPassword(password);
}
}
log.info("初始化[集群部署]方式Config,redisAddress:" + split);
}catch (Exception e){
log.error("集群部署 Redisson init error", e);
}
return config;
}
}
Cluster方式至少6个节点,3主3从,从节点用于主节点宕机后的高可用 还有几种连接模式的实现类,这里就进行省略了,只需要实现RedissonConfigService接口,重写createRedissonConfig方法即可,实际业务场景Cluster模式使用的是比较多的。
Redis核心配置
/**
* @Description: Redisson核心配置,用于提供初始化的redisson实例
*
*/
@Slf4j
public class RedissonManager {
private Config config = new Config();
private RedissonClient redisson = null;
public RedissonManager() {
}
public RedissonManager(RedissonProperties redissonProperties) {
try {
//通过不同部署方式获得不同config实体
config = RedissonConfigFactory.getInstance().createConfig(redissonProperties);
redisson =Redisson.create(config);
} catch (Exception e) {
log.error("Redisson init error", e);
throw new IllegalArgumentException("please input correct configurations," +
"connectionType must in standalone/sentinel/cluster/masterSlave");
}
}
public RedissonClient getRedisson() {
return redisson;
}
/**
* Redisson连接方式配置工厂
* 双重检查锁
*/
static class RedissonConfigFactory {
private RedissonConfigFactory() {
}
private static volatile RedissonConfigFactory factory = null;
public static RedissonConfigFactory getInstance() {
if (factory == null) {
synchronized (Object.class) {
if (factory == null) {
factory = new RedissonConfigFactory();
}
}
}
return factory;
}
/**
* 根据连接类型获取对应连接方式的配置,基于策略模式
*
* @param redissonProperties redis连接信息
* @return Config
*/
Config createConfig(RedissonProperties redissonProperties) {
Preconditions.checkNotNull(redissonProperties);
Preconditions.checkNotNull(redissonProperties.getAddress(), "redisson.lock.server.address cannot be NULL!");
Preconditions.checkNotNull(redissonProperties.getServerType(), "redisson.lock.server.password cannot be NULL");
Preconditions.checkNotNull(redissonProperties.getDatabase(), "redisson.lock.server.database cannot be NULL");
String connectionType = redissonProperties.getServerType();
//声明配置上下文
RedissonConfigService redissonConfigService = null;
if (connectionType.equals(RedisConnectionTypeEnum.STANDALONE.getConnectionType())) {
redissonConfigService = new StandaloneConfigImpl();
} else if (connectionType.equals(RedisConnectionTypeEnum.SENTINEL.getConnectionType())) {
redissonConfigService = new SentineConfigImpl();
} else if (connectionType.equals(RedisConnectionTypeEnum.CLUSTER.getConnectionType())) {
redissonConfigService = new ClusterConfigImpl();
} else if (connectionType.equals(RedisConnectionTypeEnum.MASTERSLAVE.getConnectionType())) {
redissonConfigService = new MasterslaveConfigImpl();
} else {
throw new IllegalArgumentException("创建Redisson连接Config失败!当前连接方式:" + connectionType);
}
return redissonConfigService.createRedissonConfig(redissonProperties);
}
}
}
这里我们将config实体换成Redisson包下的config,因为我们要构建Redisson 实例,毕竟Redisson相对来说更好用一些; RedissonManager的构造方法通过静态内部连接工厂RedissonConfigFactory ,来实现不同部署方式获取不同的config实体, 当然工厂类里也是使用懒汉单例的duoble check 模式来进行单例工厂的初始化,防止RedissonConfigFactory工厂的频繁创建,从而带来redisson连接频繁创建,带来不必要的性能影响;
总结
至此Redis的集群连接模式,通过Java来实现连接的部分已经实现完毕,剩下的需要在本地构建Redis Cluster环境,通过我们实现的客户端进行连接,完成集群模式的全部搭建,下一篇会写本地搭建Redis Cluster集群。
转载自:https://juejin.cn/post/7270971101014818857