에러 내용
## master 죽였을 때
Caused by: io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica.
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:144) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:116) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:747) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:682) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:599) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
... 1 common frames omitted.
아래와 같이 master 1 개와 slave 1 개로 구성이 되어있다고 가정합니다.
master 가 node 가 죽었든 pod 가 죽었든 죽어버렸습니다. ( 이때 에러 발생 )
slave 중 1 개가 master 로 승격이 됩니다.
slave 는 하나가 되었기 때문에 slave 하나가 재생성 됩니다.
spring 에 redis 설정은 아래와 같이 되어있습니다.
@Configuration
public class WriteToMasterReadFromReplicaConfiguration {
final String hostName = "redis";
final String password = "1234!@#";
final int port = 6379;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(hostName, port);
serverConfig.setPassword(RedisPassword.of(password));
return new LettuceConnectionFactory(serverConfig, clientConfig);
}
}
readFrom 이라는 속성에 대해 알아보았습니다.
ReadFrom.REPLICA
- 오직 레플리카 노드에서만 읽기를 수행합니다.
ReadFrom.REPLICA_PREFERRED
- 가능하면 Slave (Replica) 노드에서 읽기를 수행합니다.
- Slave 노드가 사용 불가능한 경우에만 Master 노드에서 읽기를 수행합니다.
- 이 옵션을 사용하면 읽기 부하를 Master 노드에서 분산시킬 수 있지만, Master가 다운되고 새로운 Master가 선출되는 시간 동안 문제가 발생할 수 있습니다.
ReadFrom.MASTER
- 오직 마스터 노드에서만 읽기를 수행합니다.
ReadFrom.MASTER_PREFERRED
- 가능하면 Master 노드에서 읽기를 수행합니다.
- Master 노드가 사용 불가능한 경우에만 Slave (Replica) 노드에서 읽기를 수행합니다.
- 이 설정은 Master의 데이터가 가장 최신이며 일관성이 있기 때문에 읽기 작업의 정확성을 높이는 데 도움이 됩니다. 그러나 이렇게 하면 모든 읽기 요청이 Master로 전달되므로 Master 노드에 부하가 집중될 수 있습니다.
두 옵션은 둘 다 읽기 작업의 탄력성을 높이려는 목적으로 사용되지만, 어느 노드에 부하를 더 주고 싶은지에 따라 선택할 수 있습니다.
REPLICA_PREFERRED는 읽기 부하를 Master에서 분산시키려 할 때 유용하며, MASTER_PREFERRED는 데이터의 일관성과 정확성을 높이려 할 때 유용합니다.
근데 제 상황에서는 MASTER 에게 부하를 주는 부담을 주더라도 ReadFrom.MASTER_PREFERRED 옵션을 선택해야 할 것 같습니다.
쓰기에 대한 옵션을 제가 지정하고 싶다는 생각을 했습니다. ( MASTER 에게만 쓰라 그러면 READ_ONLY 에러는 발생하지 않을테니 ;; ) 왜 slave 에게 쓰라는 이벤트를 해서 에러를 내는지 이해가 가지는 않습니다만 ..
sentinel 을 사용하는 경우에는 spring boot 설정에서 아래와 같은 설정이 필요하다고 합니다.
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
.master("mymaster")
.sentinel("redis-node-2.redis-headless.lucy3.svc.cluster.local", 26379)
.sentinel("redis-node-1.redis-headless.lucy3.svc.cluster.local", 26379);
sentinelConfig.setPassword(RedisPassword.of(password));
// 마스터에서 우선적으로 읽지만, 마스터가 응답하지 않을 경우 슬레이브에서 읽음
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.MASTER_PREFERRED)
.build();
return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}
고성능을 위한 Redis (High Availability, HA)와 Kubernetes 구축 마스터 가이드 (Spring Session storage 설정까지)
'개발중 > Spring Boot & Redis' 카테고리의 다른 글
클래스 변경과 직렬화: Spring Redis 환경에서의 serialVersionUID 관리 (0) | 2023.11.17 |
---|---|
로컬에서는 Spring Session, 배포에서는 Redis: 세션 저장소 분리 전략 (0) | 2023.09.20 |
Spring Security에서 Redis를 사용하면서 겪은 SessionRegistry 문제 (0) | 2023.09.20 |
[Spring - Redis] READONLY You can't write against a read only replica. (0) | 2023.09.15 |
Spring Session storage 를 Redis 로 설정하기 ! (0) | 2023.09.13 |