Lettuce Cluster Failover and Load Balancing

레디스 개발자 교육 신청 레디스 정기점검/기술지원
Redis Technical Support
레디스 엔터프라이즈 서버
Redis Enterprise Server

Cluster Failover and Load Balancing

Redis Cluster에 접속하는 방법을 설명한다.
입력/조회 명령 실행 중 마스터 서버가 다운되었을 때 처리 방법에 대선 설정
조회 명령을 복제본에서 실행해서 부하 분산(load balancing)하는 방법을 설명한다.
Lettuce-6.1.7과 Redis-7.0.3에서 실행했다.


Redis Cluster에 접속하는 방법

이클립스(eclipse 2022-03) 설정

Main.java

Util.java

Config.java


Redis Cluster 구성

Redis Cluster 생성

  • 서버 준비: 7000, 7001, 7002, 8000, 8001, 8002 6대를 준비한다.
    redis.conf 변경
    • bind 192.168.56.102 127.0.0.1 -::1
    • port 7000
    • daemonize yes
    • pidfile "/var/run/redis_7000.pid"
    • logfile "redis.log"
    • save ""
    • dir "/app/redis/redis-7.0.3/7000"
    • masterauth "password"
    • requirepass "password"
    • appendonly yes
    • cluster-enabled yes
    • cluster-config-file nodes.conf
    • cluster-node-timeout 5000
    • cluster-require-full-coverage no
  • 7000~7002는 마스터로, 8000~8002는 복제본으로 사용할 것이다.
  • 클러스터 생성: $ src/redis-cli --cluster create 192.168.56.102:7000 192.168.56.102:7001 192.168.56.102:7002 192.168.56.102:8000 192.168.56.102:8001 192.168.56.102:8002 --cluster-replicas 1 -a password

Lettuce Cluster 설정

레디스 서버 리스트 파일

  • 파일 명: cluster.cfg
  • 위치: 이클립스(eclipse) project 폴더 아래에 작성
  • 필드 구성:
    • server-name: 구분자, 프로그램 내에서 사용되지는 않는다.
    • ip: 서버 ip
    • port: 서버 port
    • password: password, 첫 번째 줄에만 입력한다.
      redis.conf requirepass, masterauth에 설정했을 경우 사용한다.
  • Config.java loadConfig()에서 읽어온다. 빈 라인 또는 #으로 시작하는 라인은 처리하지 않는다.

Lettuce - Redis Cluster 연결

Connection timeout은 default 60sec이다. 여기서는 10sec로 설정했다.
Failover 시 connection timeout이 복구 시간에 영향을 미치기 때문에 주의할 필요가 있다.

몇 가지 연결 방법

  • redis://[password@]host[:port]
  • redis://[username:password@]host[:port]
  • redis://[password@]host1:port,host2:port,host3:port/0?timeout=60s

장애 발생 시 대비 Redis Cluster 정보를 새로 가져오는 방법

Redis Cluster 정보를 새로 가져오는(새로고침) 데는 ClusterTopologyRefreshOptions를 사용하고, 2가지 방법으로 설정한다.
기본적으로 새로고침을 활성화하지 않는다. 명시적으로 활성화(enable) 시켜야한다.
Redis Cluster 정보를 새로 가져오는 데는 cluster nodes 명령을 실행한다.

Redis Cluster 정보를 새로 가져오는 2가지 방법

  • 주기적으로 클러스터 정보를 가져오는 방법 -> enablePeriodicRefresh()
  • 이벤트 발생 시 클러스터 정보를 가져오는 방법 -> enableAdaptiveRefreshTrigger()

2종류의 시간은 기본적으로 아래와 같이 설정된다. 이는 변경할 수 있다.
Source code: ClusterTopologyRefreshOptions.java
    refreshPeriod = 60sec DEFAULT_REFRESH_PERIOD
    adaptiveRefreshTimeout = 30sec DEFAULT_ADAPTIVE_REFRESH_TIMEOUT

Lettuce의 설명을 참조하려면 아래 링크를 클릭하세요.
    7.2.1. Cluster-specific options

EnablePeriodicRefresh()

주기적으로 클러스터 정보를 가져온다.

  • enablePeriodicRefresh(Duration) 기간을 설정하고 활성화 시킨다.
    시간 설정 방법: enablePeriodicRefresh(Duration.ofSeconds(30)), enablePeriodicRefresh(Duration.ofMinutes(2))
  • enablePeriodicRefresh() -> true로 설정, default 60sec로 설정된다.
  • enablePeriodicRefresh(true/false)
    -> refreshPeriod(Duration).enablePeriodicRefresh();
  • refreshPeriod(Duration) -> 기간만 설정하는 방법, default 60sec

이 기능을 활성화하면 cluster nodes를 포함해서 다음 4가지 명령이 주기적으로 실행된다.
    cluster nodes, info, client setname, hello
이 명령들은 lettuce에서 redis로 연결(connection)해서 실행하고 바로 연결을 끊는다. 그래서 client list로 볼 수 없다. info stats 명령으로 connection이 증가하는 것을 확인할 수 있다.
# Stats total_connections_received:32
code: cluster/topology/DefaultClusterTopologyRefresh.java
    CompletionStage()
        requestTopology() -> CLUSTER NODES
        requestClients() -> INFO CLIENTS
    openConnections()
        connection.async().clientSetname("lettuce#ClusterTopologyRefresh");
        connection.close();

EnableAdaptiveRefreshTrigger()

이벤트 발생 시 클러스터 정보를 가져온다.

  • enableAdaptiveRefreshTrigger(): RefreshTrigger 5가지 중 하나 이상을 설정한다.
    code: enableAdaptiveRefreshTrigger(RefreshTrigger.ASK_REDIRECT, RefreshTrigger.MOVED_REDIRECT);
  • enableAllAdaptiveRefreshTriggers(): 5가지 모두 설정한다.

RefreshTrigger 5가지

  • RefreshTrigger.ASK_REDIRECT: 레디스 명령에 ASK로 응답할 때,
    cluster setslot importing/migrating 후 cluster setslot node로 slot 이동을 확정하기 전에 원래 노드에 명령이 실행되면 ASK ip:port(대상 노드)가 리턴된다.
    code: onAskRedirection()
  • RefreshTrigger.MOVED_REDIRECT: 레디스 명령에 MOVED로 응답할 때,
    cluster setslot importing/migrating/node로 slot를 이동했을 경우 원래 노드에 명령이 실행되면 MOVED ip:port(대상 노드)가 리턴된다.
    code: onAskRedirection()
  • RefreshTrigger.UNCOVERED_SLOT: 알려진 노드에 포함되지 않은 슬롯을 사용하려고 할 때
    code: onUncoveredSlot(int slot)
  • RefreshTrigger.UNKNOWN_NODE: 알 수 없는 노드에 연결을 시도할 때
    code: onUnknownNode()
  • RefreshTrigger.PERSISTENT_RECONNECTS: 특정 노드에 2번 이상 연경을 시도할 때
    code: onReconnectAttempt(int attempt)

테스트 방법

아래 번호를 선택해서 테스트한다.
6번부터 12번 까지는 조회 테스트이다. 조회 테스트 결과는 Sentinel과 같다.

  1. SET Sync: SET 명령 실행, Failover 테스트에 사용된다.
  2. SET Async: SET 명령을 비동기로 실행한다.
  3. MSET: MSET 명령 실행, Redis Cluster에서는 multi-key 명령을 실행할 수 없지만 Lettuce에서는 MSET을 키 개수만큼 SET 명령으로 나누어서 실행한다.
  4. MGET: MGET 명령 실행, Lettuce에서는 MGET을 키 개수만큼 GET 명령으로 나누어서 실행한다.
  5. SUNION: SUNION 명령 실행 -> SUNION 같은 multi-key 명령은 실행할 수 없다.
  6. ANY: 마스터, 복제 노드 고르게 조회 분배
  7. ANY_REPLICA: 복제 노드에서만 조회. 복제 노드가 여러개 있으면 고르게 분배, 복제 노드가 다운되면 마스터가 살아있어도 실행 불가
  8. MASTER=UPSTREAM: 마스터에서만 조회
  9. MASTER_PREFERRED=UPSTREAM_PREFERRED: 마스터 위주로 조회
  10. REPLICA: 복제 노드에서만 조회, 복제 노드 중 한 곳에 집중되는 경향이 있다.
  11. REPLICA_PREFERRED: 기본적으로 복제 노드에서 조회하고, 복제 노드가 다운되었을 때 마스터에서 조회. 복제 노드 중 한 곳에 집중되는 경향이 있다.
  12. LOWEST_LATENCY: 대기 시간이 가장 짧은 노드에서 읽도록 설정한다. 복제 노드 중 한 곳에 집중되는 경향이 있다.

입력 중 서버가 다운되었을 경우 장애 조치(failover) 테스트

enablePeriodicRefresh()와 enableAllAdaptiveRefreshTriggers() 두 가지 방법으로 테스트 한다.

enablePeriodicRefresh() 테스트

boolean period = true;
enablePeriodicRefresh(Duration.ofSeconds(20))

  • 1번을 선택해서 SET 명령을 실행한다.
  • kill -9 pid로 7000번 마스터 서버를 다운시킨다.
  • 36분 26초: server down
  • 36분 27초: Lettuce에서 server down 감지
  • 36분 33초: Lettuce에서 DefaultClusterTopologyRefresh 실행
  • 36분 34초: Redis Cluster Failover 완료: Failover election won: I'm the new master.
  • 36분 37초: connection timeout
  • 36분 47초: command fail
  • 36분 53초: Lettuce에서 DefaultClusterTopologyRefresh 실행
  • 36분 57초: command ok

서버 다운 후 30초 만에 복구되었다.
cluster-require-full-coverage no로 설정해도 테스트 결과는 같았다.
Lettuce에서는 이 파라미터를 이용하지 않는 것으로 보인다.

다음 테스트를 위해서 복구 진행

  • 다운시킨 7000번 서버 시작
    $ src/redis-server 7000/redis.conf
  • slave를 master로 변경
    $ src/redis-cli -p 7000 -a password
    > role -> slave
    > cluster failover
    > role -> master

enableAllAdaptiveRefreshTriggers() 테스트

boolean period = false;
enableAllAdaptiveRefreshTriggers()
adaptiveRefreshTriggersTimeout() default 30sec 적용

  • 1번을 선택해서 SET 명령을 실행한다.
  • kill -9 pid로 7000번 마스터 서버를 다운시킨다.
  • 31분 16초: server down
  • 31분 16초: Lettuce에서 server down 감지
  • 31분 23초: Redis Cluster Failover 완료: Failover election won: I'm the new master.
  • 31분 27초: connection timeout
  • 31분 27초: Lettuce에서 DefaultClusterTopologyRefresh 실행
  • 31분 37초: command ok

서버 다운 후 20초 만에 복구되었다.



Multi-key 명령, Cluster-wide 명령

Multi-key 명령

Redis Cluster에서는 multi-key 명령을 실행할 수 없으나, Lettuce에서는 일부 multi-key 명령을 single-key(단일키) 명령으로 변경해서 실행할 수 있다.

  • DEL, UNLINK: 키 개수만큼 DEL, UNLINK 명령 실행, 예) del key1 key2 key3 -> 삭제한 키 개수 리턴
  • EXISTS: 키 개수만큼 EXISTS 명령 실행, 예) exists key1 key2 key3 -> 존재하는 키 개수 리턴
  • MSET, MGET: 키 개수만큼 SET, GET 명령으로 변경해서 실행

MSET 명령 테스트

MSET 키 10개씩 100번 실행
  Sync Set: [소요시간:12sec] -> 합계 calls 1000
  7000 info commandstats -> cmdstat_mset:calls=328,usec=703,usec_per_call=2.14
  7001 info commandstats -> cmdstat_mset:calls=340,usec=773,usec_per_call=2.27
  7002 info commandstats -> cmdstat_mset:calls=332,usec=749,usec_per_call=2.26

MGET 명령 테스트

MGET 키 10개씩 100번 실행, ReadFrom.ANY
  Sync MGET: [소요시간:12sec] -> 합계 calls 1000, master, replica 고르게 실행
  7000 info commandstats -> cmdstat_mget:calls=157,usec=319,usec_per_call=2.03
  7001 info commandstats -> cmdstat_mget:calls=179,usec=405,usec_per_call=2.26
  7002 info commandstats -> cmdstat_mget:calls=155,usec=315,usec_per_call=2.03
  8000 info commandstats -> cmdstat_mget:calls=161,usec=332,usec_per_call=2.06
  8001 info commandstats -> cmdstat_mget:calls=177,usec=334,usec_per_call=1.89
  8002 info commandstats -> cmdstat_mget:calls=171,usec=358,usec_per_call=2.09

SUNION 명령 테스트

테스트 데이터 입력
7000> sadd myset03 DDD EEE FFF
7001> sadd myset01 AAA BBB CCC
7001> sadd myset02 CCC DDD EEE
7002> sadd myset04 BBB CCC DDD
code:
connection.sync().sunion("myset01","myset02");
-> ERROR: CROSSSLOT Keys in request don't hash to the same slot
-> 키가 저장된 슬롯이 같아야 실행됨.
7001 info commandstats -> cmdstat_sunion:calls=0,usec=0,usec_per_call=0.00,rejected_calls=1

Cluster-wide 명령

일부 명령은 여러 노드에서 실행해서 결과를 제공한다.

  • KEYS: 모든 마스터에서 실행
  • SCAN: ReadFrom 설정에 따라 마스터 또는 복제 노드에서 실행
  • DBSIZE: 모든 마스터에서 실행해서 합계 키 개수를 리턴
  • FLUSHDB, FLUSHALL: 모든 마스터에서 실행, 클러스터 내 모든 데이터가 지워짐.
  • SHUTDOWN: 클러스터 모든 노드에서 실행
  • CLIENT SETNAME: 클러스터 모든 노드에서 실행
  • SCRIPT FLUSH/LOAD/KILL: 클러스터 모든 노드에서 실행

DBSIZE 명령 테스트

Long dbsize = connection.sync().dbsize();
System.out.println("dbszie "+dbsize); -> 1000

Lettuce의 multi-key, cluster-wide 설명을 참조

Lettuce의 multi-key, cluster-wide 설명을 참조하려면 아래 링크를 클릭하세요.
5.3.2. Cross-slot command execution and cluster-wide execution for selected commands


부하 분산(load balancing)하는 방법

조회 명령을 마스터/복제 노드에서 실행해서 부하 분산(load balancing)하는 방법

조회 명령을 마스터/복제 노드에서 실행해서 부하 분산(load balancing)하는 방법을 설명한다.
다음은 Lettuce에서 제공하는 조회 명령을 분산 실행하는 방법이다.
ReadFrom에서 선택할 수 있다.

  • ANY: 마스터, 복제 노드에서 고르게 조회 명령을 실행한다.
    code: new ReadFromImpl.ReadFromAnyNode(); // @since 5.2
  • ANY_REPLICA: 복제 노드에서 조회 명령을 실행한다. 복제 노드가 여러개 있을 경우 고르게 실행된다.
    복제 노드이 없을 경우 명령을 실행할 수 없다.
    code: new ReadFromImpl.ReadFromAnyReplica(); // @since 6.0.1
  • MASTER = UPSTREAM: 마스터에서만 조회 명령을 실행한다.
    code: new ReadFromImpl.ReadFromUpstream(); // @since 6.0
  • MASTER_PREFERRED = UPSTREAM_PREFERRED: 기본적으로 마스터에서 실행하고 마스터가 사용할 수 없을 경우 복제 노드에서 실행한다.
    code: new ReadFromImpl.ReadFromUpstreamPreferred(); // @since 6.0
  • REPLICA = SLAVE: 복제 노드에서만 조회 명령을 실행한다. 복제 노드가 여러개 있을 경우 특정 노드에 집중되는 경향이 있다. 복제 노드이 없을 경우 명령을 실행할 수 없다.
    code: new ReadFromImpl.ReadFromReplica(); // @since 5.2
  • REPLICA_PREFERRED = SLAVE_PREFERRED: 기본적으로 복제 노드에서 실행하고 복제 노드을 사용할 수 없을 경우 마스터에서 실행한다. 복제 노드가 여러개 있을 경우 특정 노드에 집중되는 경향이 있다.
    code: new ReadFromImpl.ReadFromReplicaPreferred(); // @since 5.2
  • LOWEST_LATENCY = NEAREST: 대기 시간이 가장 짧은 노드에서 읽도록 설정한다. 복제 노드가 여러개 있을 경우 특정 노드에 집중되는 경향이 있다.
    new ReadFromImpl.ReadFromLowestCommandLatency(); // @since 6.1.7

테스트 결과는 Sentinel에서와 같다.


Source code

DefaultClusterTopologyRefresh.java


<< Cluster Cluster Failover for Enterprise >>

Email 답글이 올라오면 이메일로 알려드리겠습니다.