Lettuce 라이브러리 기반 Redis Cluster 클라이언트 개발 가이드
Lettuce 라이브러리 기반 Redis Cluster 클라이언트 개발 가이드
개요
Redis Cluster는 샤딩 기반으로 데이터를 분산하며 , 복제는 샤딩별 Master, Slave 구조로 진행합니다 . 따라서 Redis(DBaaS) 의 기존 아키텍처인 Redis Sentinel과는 다른 방식의 클라이언트 개발이 필요합니다. Redis Cluster 용 클라이언트 개발에는 Lettuce 라이브러리가 많이 쓰이고 있습니다. Lettuce 라이브러리는 동기 및 비동기 API 와 Redis 자료형 별 Operation 을 이용할 수 있고 , Spring data 와 연계등의 Redis Cluster 접속에 필요한 다양한 기능을 제공합니다.
Lettuce 라이브러리
Lettuce
Lettuce는 Netty 라이브러리 기반으로 TCP Channel 및 event 처리 기반으로 Redis Cluster 와 connection 을 생성 하고 관리합니다 . 따라서 De p endency 로 Netty 및 Reactor 관련 라이브러리들이 필요합니다 Lettuce 라이브러리는 다양한 기능을 제공하는데 Cluster 외에도 StandAlone 및 Sentinel 용 접속 클래스를 제공하며 , ID/P assword 방식과 SSL 기반 인증을 지원합니다. Redis Cluster 와 연결 후 Master 또는 Slave 노드에 장애가 발생하거나 노드를 추가 및 삭제하는 경우 Lettuce 라이브러리는 실시간으로 해당 Topology 변경을 반영할 수 있습니다
[ Lettuce Maven ]
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce core</artifactId>
<version>${version}</version>
</dependency>
Spring-data-redis
RDBMS는 JDBC 인터페이스 기반 드라이버를 제공하며 , Spring Framework 는 해당 드라이버 를 Spring Bean 으로 쉽게 등록하는 인터페이스를 제공합니다 Lettuce는 Spring data redis 라이브러리 기반으로 하며 Spring Framework 에 쉽게 등록해서 사용할 수 있습니다. Connection Pool 및 Redis Operation 객체를 Spring Bean 형태로 호출합니다
[ Spring data redis Maven ]
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring data redis</artifactId>
<version>${version}</version>
</dependency>
Redis Cluster 접속 설정
Redis Cluster Configuration
Cluster내 노드 IP 주소 및 Port 값과 인증정보를 Redis Cluster 접속 정보 관리 객체로 설정합니다 Cluster 내 모든 Master/Slave 정보 입력을 권장하며 최초 클라이언트 기동 시 참고합니다. Redis Cluster에 인증이 적용되지 않으면 인증정보가 설정되지 않습니다
[ RedisClusterConfiguration 코드 예제 ]
List<String> host;
RedisClusterConfiguration configuration = new RedisClusterConfiguration(this.host);
configuration.setUsername("username");
configuration.setPassword("password");
Cluster Topology Refresh Options
Redis Cluster상태를 관리하고 Master/Slave 장애나 Cluster 변경이 생기면 이를 동적으로 반영하는 동작에 대한 옵션을 설정할 수 있습니다. Lettuce는 주기적으로 Cluster상태를 체크하거나, Trigger 발생시에 Cluster 상태를 체크하도록 설정할 수 있으며, 체크 주기도 정의할 수 있습니다
[ ClusterTopologyRefreshOptions 설정 목록 ]
| 옵션 | 설명 | default |
|---|---|---|
| enableAllAdaptiveRefreshTriggers | Redis Cluster에서 Trigger 발생시 topology 재구성 | False |
| refreshTriggersReconnectAttempts | RefreshTrigger.PERSISTENT_RECONNECTS 에 의해 설정된 숫자만큼 Reconnect Attempt 발생시 topology refresh 수행 | 5 |
| adaptiveRefreshTriggersTimeout | trigger로 인한 topology refresh 시간 간격 trigger가 자주 발행해서 refresh 가 자주 발생하면 , redis 에 부하를 줌으로 해당 시간 내에는 반복적으로 refresh 하지 않음 | 30 |
| enablePeriodicRefresh | topology refresh 수행 여부 | False |
| refreshPeriod | enablePeriodicRefresh 수행 주기 | 60 |
| dynamicRefreshSources | true로 설정하면 , 발견된 모든 노드로부터 topology 정보를 얻어온다 . false로 설정하면, 처음 설정한 seed 노드로부터 만 topology정보를 얻어온다 | True |
[ ClusterTopologyRefreshOptions 코드 예제 ]
ClusterTopologyRefreshOptions topologyRefreshOptions =
ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(60))
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
.refreshTriggersReconnectAttempts(3)
.build()
Lettuce Client Configuration
클라이언트에 관련된 전반적인 옵션을 관리하는 클래스로 connection 및 command 타임아웃 및 TCP 관련된 설정을 할 수 있습니다. 타임아웃 관련된 설정은 Application 장애 및 Failover 시간과 연관되므로 Redis Cluster 및 topology 설정을 고려하여 값을 정의합니다
[ ClusterClientOptions 설정 목록 ]
| 옵션 | 설명 | default |
|---|---|---|
| autoReconnect | close 이벤트없이 종료된 connection 에 대해 retry 여부 | True |
| timeoutOptions | commandTimeout과 동일하게 GET PUT operation에 대한 timeout | X |
| socketOptions | connection에 대한 timeout 으로 default 값은 10 초 | 10 |
| disconnectedBehavior | connection이 끊어졌을 때 진행중인 operation 에 대한 처리 설정으로 auto reconnect 활성화시 operation을 queue에 저장하고 auto reconnect 비활성화시에는 fail 처리한다 | DEFAULT |
Lettuce Connection Factory
앞에서 언급한 설정 클래스들을 기반으로 클라이언트에 connection을 제공하는 클래스입니다 . Read정책을 설정할 수 있고 TCP 를 관리하는 Netty 관련된 옵션을 추가 할 수 있습니다. 일반적으로 생성 된 LettuceConnectionFactory 객체는 Spring Bean 으로 등록하여, Redis 관련 클라이언트에서 쉽게 사용하도록 합니다
[ LettuceConnectionFactory 코드 예제 ]
LettuceConnectionFactory connectionFactory=new
LettuceConnectionFactory(configuration
,LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.commandTimeout(Duration.ofMillis(${user}))
.clientOptinos(
ClusterClientOptions.builder()
.autoReconnect(true)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.DEFAULT)
.topologyRefreshOptions(topologyRefreshOptions)
.build())
.clientResources(clientResource)
.build());
RedisTemplate<K,V>
클라이언트 Operation API 를 제공하는 클래스로, Redis Cluster를 이용하는 비즈니스 클래스는 RedisTemplate을 사용해야 합니다. RedisTemplate은 LettuceConnectionFactory를 파라미터로 받아 Connection을 가져오며, 생성한 RedisTemplate은 Spring Bean으로 등록해서 사용합니다
[ RedisTemplate 코드 예제 ]
//Create lettuce template for String type key and value
RedisTemplate<String, String>
template = new RedisTemplate<>();
//Set template based on lettuce ConnectionFactory
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
Netty Epoll 옵션 설정
Netty Epoll 옵션
일반적으로 TCP 설정은 클라이언트는 일부만 설정하고 대부분은 Application을 실행하는 OS내 커널 파라미터 기반으로 적용됩니다. Netty Epoll 라이브러리는 TCP 상세 옵션을 제공하여 기존 클라이언트 TCP 옵션으로는 진행할 수 없는 문제들을 해결할 수 있습니다. 기존 Lettuce 라이브러리로는 Read Replica에 Network장애시 Failover가 안되는 문제가 있는데, Epoll옵션내 TCP_USER_TIMEOUT을 적용하여 해결 합니다
[자주 사용하는 Epoll 옵션]
| 옵션 | 설명 |
|---|---|
| EpollChannelOption.TCP_KEEPIDLE | KEEPIDLE 동안 부하가 없으면 Idle 상태로 전환하여 상태체크 수행 |
| EpollChannelOption.TCP_KEEPCNT | Idle상태 전환 후 KEEPCNT 횟수 만큼 상태 체크 수행 |
| EpollChannelOption.TCP_KEEPINTVL | Idle상태 전환 후 상태 체크 수행 시 간격 시간 |
| EpollChannelOption.TCP_USER_TIMEOUT | TCP 요청에 대해 USER_TIMEOUT 동안 응답이 없으면 장애로 판단 후 TCP연결 해제, 0이면 해당 옵션을 사용하지 않습니다 |
Netty Epoll 라이브러리
Epoll라이브러리는 OS별로 컴파일 된 파일을 내장하고 있습니다. 따라서 Classifier를 명시해야 하며, Linux 환경 라이브러리는 내부에 SO 파일이 있어 Epoll 옵션 호출할 때마다 사용됩니다
[Linux 용 netty epoll pom.xml 예제]
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty transport native epoll</artifactId>
<version> 1.85.Final</version>
<classifier>linux x86_64</classifier>
</dependency>
nettyCustomizer
Lettuce에서 Netty Epoll 옵션을 적용하기 위해선 NettyCustomizer 인터페이스를 구현하고 내부 메소드에 Epoll 옵션을 설정해야 됩니다. Epoll 적용 여부는 EpollProvider.isAvailable 메소드로 확인할 수 있습니다
[ NettyCustomizer 인터페이스 구현 예제 ]
ClientResources clientResource = ClientResources.builder().nettyCustomizer(new
NettyCustomizer() {
@Override
public void afterBootstrapInitialized(Bootstrap bootstrap) {
bootstrap.option(EpollChannelOption.TCP_KEEPIDLE, ${user});
bootstrap.option(EpollChannelOption.TCP_KEEPINTVL, ${user});
bootstrap.opt ion(EpollChannelOption.TCP_KEEPCNT, ${user});
bootstrap.option(EpollChannelOption.TCP_USER_TIMEOUT, ${user});
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
}).build();
Lettuce 기반 Operation 코드 예제
Lettuce Configuration 클래스 예제
Redis Cluster 접속 정보를 기반으로 LettuceConnectionFactory 및 RedisTemplate에 대한 설정을 초기화 하고 Spring Bean 으로 등록합니다. ClientResource는 Netty Epoll 옵션을 적용하고 ClusterTopology는 Cluster 상태 정보 옵션을 설정하며, ClientConfiguration에서는 클라이언트 상세 옵션을 정의합니다
[ Lettuce Config uration 클래스 예제 ]
@Configuration
public class RedisConfig{
// Method to create ConnectionFactory bean
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// Define cluster status related settings
ClusterTopologyRefreshOptions topolo gyRefreshOptions =
ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(60))
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
.refres hTriggersReconnectAttempts(3)
.build();
// Define cluster connection information, Host information is entered in List
RedisClusterConfiguration configuration =
new RedisClusterConfiguration(this.host);
configuration.setUsern ame("user");
configuration.setPassword("password");
// Define Netty Epoll options
ClientResources clientResource =
ClientResources.builder().nettyCustomizer(new NettyCustomizer() {
@Override
public void afterBootstrapInitialized(Bootstrap bootstrap) {
bootstrap.option(EpollChannelOption.TCP_KEEPIDLE, 15);
bootstrap.option(EpollChannelOption.TCP_KEEPINTVL, 5);
bootstrap.option(EpollChannelOption.T CP_KEEPCNT, 1);
bootstrap.option(EpollChannelOption.TCP_USER_TIMEOUT, 30000);
bootstrap.option(ChannelOption.SO_KEEPALIVE,true);
}).build();
}
// Define LettuceFactory with Topology, Cluster Info, Netty option
LettuceConnectionFactory connectionFactory=new
LettuceConnectionFactory(configuration
,LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.clientResources(clientResource)
.commandTimeout(Duration.ofMillis(30000))
.clientOptions(
ClusterClientOptions.builder()
.topologyRefreshOptions(topologyRefreshOptions).build()).build());
return connectionFactory;
}
// Create RedisTemplate Bean in String type
@Bean
RedisTemplate<String, String> redisTemplate(
@Qualifier("redisConnectionFactory") RedisConnectionFactory
redisConnectionFactory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(redisConne ctionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
Redis Operation 예제
RedisTemplate Bean을 받아와서 Redis Cluster 대상으로 GET PUT 을 수행할 수 있습니다. Redis Cluster 상태 변경이나 Failover 는 RedisConfig 에서 설정한 옵션 기반으로 Lettuce 라이브러리가 수행합니다
[ Redis Operation 예제 ]
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate<String, String> redisTemplate;
//String
type PUT operation
this.redisTemplate.opsForValue().set(key, value);
//String
type GET operation
String v = this.redisTemplate.opsForValue().get(key);