1. 问题背景
调整redis的配置后,启动程序时, 会报如下错误:
[redis://172.16.0.8@xxx]: ERR This instance has cluster support disabled
Suppressed: io.lettuce.core.RedisCommandExecutionException: ERR This instance has cluster support disabled
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:147)
at io.lettuce.core.internal.Exceptions.bubble(Exceptions.java:72)
at io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh.getException(DefaultClusterTopologyRefresh.java:402)
at io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh.tryFail(DefaultClusterTopologyRefresh.java:173)
at io.lettuce.core.cluster.topology.DefaultClusterTopologyRefresh.lambda$null$9(DefaultClusterTopologyRefresh.java:150)
at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
at io.lettuce.core.RedisChannelHandler.lambda$closeAsync$0(RedisChannelHandler.java:189)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
at io.lettuce.core.internal.Futures.lambda$adapt$1(Futures.java:120)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:590)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:557)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:492)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:636)
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:625)
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:105)
at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
at io.netty.channel.AbstractChannel$AbstractUnsafe.safeSetSuccess(AbstractChannel.java:990)
at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:756)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:731)
at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:620)
at io.netty.channel.DefaultChannelPipeline$HeadContext.close(DefaultChannelPipeline.java:1352)
at io.netty.channel.AbstractChannelHandlerContext.invokeClose(AbstractChannelHandlerContext.java:749)
at io.netty.channel.AbstractChannelHandlerContext.access$1200(AbstractChannelHandlerContext.java:61)
at io.netty.channel.AbstractChannelHandlerContext$11.run(AbstractChannelHandlerContext.java:732)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: io.lettuce.core.RedisCommandExecutionException: ERR This instance has cluster support disabled
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:147)
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:116)
at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120)
at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111)
at io.lettuce.core.cluster.topology.TimedAsyncCommand.complete(TimedAsyncCommand.java:52)
at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:747)
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:682)
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:599)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
3. 问题原因
这是由于redis的实例不支持集群模式,但是在redisson的配置文件里,使用的是集群模式的方法。因此会产生如下报错。
那么redis的具体有几种模式呢,主要有4种,这个需要根据项目的实际情况进行配置。
4. redis的四种模式
redis的模式主要有4种,分别是单机模式、主从模式、哨兵模式、集群模式。
3.1 单机模式
单机模式就是在一台服务器上安装redis,然后启动,所有业务都调用这一台redis服务器。
优点:部署简单,只需要在一台服务器上安装并启动redis就行。
成本低,没有备用节点,不需要其他的开支。
高性能,单机不需要同步数据,数据天然一致性。
缺点:可靠性较弱,一旦服务器宕机,所有业务都无法使用redis服务。单机模式redis性能受限于CPU的处理能力。
3.2 主从模式
主从模式是指有多台redis服务器,其中一台专门用来负责写入客户端请求数据,称为主节点(master),
其他服务专门负责处理客户端的读取数据的请求不负责写入数据,称为从节点。
主节点写入的数据会复制的各个从节点上,并且数据复制的方向是单向的,只能从主节点复制到从节点。
优点:
- 读写分离:master写,slave读,同时可以根据访问需求⼤⼩来添加slave节点数量。
- 负载均衡:通过读写分离的⽅式来分但服务器负载,主节点负责写⼊数据,多个从节点负责读取数据,缓解单个服务器访问的压⼒,提⾼读的吞吐量。
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的⼀种数据冗余⽅式。
- ⾼可⽤性:提⾼了服务的可⽤性,多个节点同时提供服务,当前其中⼀台节点宕机后,其他节点仍然可以提供服务。
- 故障恢复:master宕机后,快速升级slave为master。
缺点: - master宕机后,slave节点升级成新的master节点后,需要⼿动切换主机,同时会有部分数据不能及时同步从服务器,造成数据不⼀致。
- slave宕机后,多个slave恢复后,⼤量的SYNC同步会造成master IO压⼒倍增。
- 主从模式只要⼀个maste节点,所以master节点的写数据的能⼒会受到单机的限制。
3.3 哨兵模式
哨兵模式主要是在主从复制模式基础上,增加了⾃动化的故障恢复功能。
因为主从复制模式下,当master节点宕机后,虽然slave节点会升级成新的master节点,但是升级后的slave节点的ip顺其⾃然的就称为了新的master节点ip,原先其他的slave节点配置的master节点的ip也是需要变更的,在主从复制模式下,这种变更是需要⼈⼯⼿动去修改配置⽂件的。⽽哨兵模式则完美的解决了这⼀缺陷,实现了快速的⾃动恢复,不再需要⼈⼯修改。
主要功能:
- 集群监控:负责监控 master 和 slave 进程是否正常⼯作。
- 消息通知:如果某个 Redis 实例宕机,那么哨兵负责向(哨兵间,客户端)发送消息。
- ⾃动故障转移:当master宕机后,由哨兵负责重新在slave节点中选举出新的master节点,并将其他slave连接到新的master,以及告知客户端新的服务器地址。更好的为客户端提供⾼可⽤服务。
缺点:
哨兵的部署会占⽤额外的服务器资源,且不提供数据的读取服务。哨兵模式除了解决了主从模式⼿动切换主从节点的问题外,其他主从模式存在的问题,哨兵模式基本也都存在。
3.4 集群模式
主从模式实现了数据的热备份,哨兵模式实现了redis的⾼可⽤。但是这两种模式存在两个问题:
(1)写并发:这两种都只能有⼀个master节点负责写操作,在⾼并发的写操作场景,master节点就会成为性能瓶颈。⽽redis集群则可以实现多个节点同时提供写操作,redis集群模式采⽤⽆中⼼结构,每个节点都可以看做是⼀个主从模式,节点之间互相连接从⽽知道整个集群状态。
(2)海量数据的存储压⼒:⽆论是哨兵模式还是主从模式,每台机器上存储的数据都是⼀样的,都是从主节点上复制过去的,本质上只有⼀台Master作为存储。所以⽆论增加多少slave节点都⽆法解决单台机器存储的上限问题。但是集群模式就解决了这以问题,因为Redis Cluster是⼀种服务器 Sharding 技术,采⽤数据分⽚的⽅式,将不同的数据存储在不同的master节点上⾯,因为我们可以通过不断的增加集群中的节点,从⽽达到存储海量数据。
redis集群的实现原理
Redis集群采⽤去中⼼化的思想,没有中⼼节点的说法,对于客户端来说,整个集群可以看成⼀个整体,可以连接任意⼀个节点进⾏操作,就像操作单⼀Redis实例⼀样,不需要任何代理中间件,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。Redis也内置了⾼可⽤机制,⽀持N个master节点,每个master节点都可以挂载多个slave节点,当master节点挂掉时,集群会提升它的某个slave节点作为新的master节点。Redis集群可以看成多个主从架构组合起来的,每⼀个主从架构可以看成⼀个节点(其中,只有master节点具有处理请求的能⼒,slave节点主要是⽤于节点的⾼可⽤)。
集群的扩容与收缩
集群主要是采⽤hash slot 算法进⾏寻址的,由于集群hash槽的数⽬是固定的,所以随着集群中节点数⽬的增减,每个节点所负责的hash槽的数⽬也会随之发⽣变化,因此对应槽和数据都会在节点之间移动。
1.扩容步骤:
(1)启动新节点;
(2)刚启动的新节点是没有和其他节点进⾏通讯的,因此需要在集群中任意节点执⾏ clustermeet 命令让新节点加⼊到集群中;
(3)新的节点加⼊到集群后,迁移槽和数据,将⼀些槽和数据从旧节点迁移到新节点;
(4)槽和数据迁移到新加⼊的节点后,新的节点会向集群中的各个主节点⼴播通知迁移过来的槽,并更新⾃身的槽节点对应表。
2. 收缩步骤:
(1)迁移槽,⾸先判断要下线的节点是否有负责的槽,如果有,则先将该节点下的槽全部迁移到其他节点,否则直接删除该节点。
(2)通知集群的各个节点忘记⾃⼰。
5 修改RedisServer配置解决
提示语ERR This instance has cluster support disabled说明RedisServer不支持集群。
修改配置文件redis.conf
中的cluster-enabled
参数的值为yes
并把参数前#
去除,重启Redis服务器。
注意:不能通过命令config set cluster-enable yes来解决,因为config命令里面没有cluster-enable配置项。
6 修改RedisClient配置解决
如果无法修改RedisServer权限,并且RedisServer不是集群部署,而是单机模式,那就只能从代码配置上修改一下了,Configuration从Cluster变成Standalone即可。以下代码兼容不同配置的redis集群:
@Configuration
public class RedisConfiguration{
//加载配置
@Autowired
RedisProperty redisProperty
@Bean("myRedisConfig")
public Config myRedisConfig(){
Conifg config = new Config();
if("single".equalsIgnoreCase(RedisProperty.getRedisMode)){
// 单机模式
conifg.userSingleServer().........
}else if("cluster".equalsIgnoreCase(RedisProperty.getRedisMode)){
// 集群模式
conifg.userClusterServer().........
}
return config;
}
}
@Configuration
@ConfigurationProperties(prefix="my.redis")
public class RedisProperty{
...
redisMode="";
...
}
//配置.property文件
# 单机模式,可以根据实际情况进行配置
# my.redis.single
# 集群模式
my.redis.cluster
参考:
https://www.cnblogs.com/laityguanguan/p/17473862.html