sin*_*gel 5 java redis lettuce
我需要一些帮助,我们的服务使用lettuce 5.1.6版本,总共部署了22个docker节点。
每当部署服务时,多个 docker 节点都会出现 ERROR: READONLY You can't write against a read only Slave。
重启有问题的docker节点不再出现ERROR
8 主站 8 从站
停止写入 bgsave-错误 否
从站服务陈旧数据 是
从站只读
是 启用
集群 是 集群配置文件“/data/server/redis-cluster/{port} /conf/node.conf"
ClientResources res = DefaultClientResources.builder()
.commandLatencyPublisherOptions(
DefaultEventPublisherOptions.builder()
.eventEmitInterval(Duration.ofSeconds(5))
.build()
)
.build();
redisClusterClient = RedisClusterClient.create(res, REDIS_CLUSTER_URI);
redisClusterClient.setOptions(
ClusterClientOptions.builder()
.maxRedirects(99)
.socketOptions(SocketOptions.builder().keepAlive(true).build())
.topologyRefreshOptions(
ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers()
.build())
.build());
RedisAdvancedClusterCommands<String, String> command = redisClusterClient.connect().sync();
command.setex("some key", 18000, "some value");
Run Code Online (Sandbox Code Playgroud)
io.lettuce.core.RedisCommandExecutionException: READONLY You can't write against a read only slave.
at io.lettuce.core.ExceptionFactory.createExecutionException(ExceptionFactory.java:135)
at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:122)
at io.lettuce.core.cluster.ClusterFutureSyncInvocationHandler.handleInvocation(ClusterFutureSyncInvocationHandler.java:123)
at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
at com.sun.proxy.$Proxy135.setex(Unknown Source)
at com.xueqiu.infra.redis4.RedisClusterImpl.lambda$setex$164(RedisClusterImpl.java:1489)
at com.xueqiu.infra.redis4.RedisClusterImpl$$Lambda$1422/1017847781.apply(Unknown Source)
at com.xueqiu.infra.redis4.RedisClusterImpl.execute(RedisClusterImpl.java:526)
at com.xueqiu.infra.redis4.RedisClusterImpl.executeTotal(RedisClusterImpl.java:491)
at com.xueqiu.infra.redis4.RedisClusterImpl.setex(RedisClusterImpl.java:1489)
Run Code Online (Sandbox Code Playgroud)
而lettuce就是redis集群的slot映射管理:
采用的方法是使用slotCache的数组,将每个slot对应的节点以数组的形式缓存在本地。
当有key需要读写服务端时,会在客户端通过CRC16计算slot,然后在缓存中获取该节点。
当通过Gossip协议交换乒乓数据时,这些元数据信息被广播,形成最终一致的元数据信息。
但是,如果服务器端的槽位映射关系出现错误,客户端就会使用这些错误的数据。
这次问题就出现在这里了。服务器部分节点将slot映射到slave上,这样客户端缓存的slot就映射到了slave节点上,读写请求都发送到了slave节点上,导致出错。