Lettuce Library-Based Redis Cluster Client Development Guide
Lettuce Library-Based Redis Cluster Client Development Guide
Overview
Redis Cluster distributes data based on sharding and replicates data in a Master-Slave structure for each shard. Therefore, client development is required in a different way than the existing Redis (DBaaS) architecture, Redis Sentinel. For Redis Cluster client development, the Lettuce library is widely used. The Lettuce library provides various features necessary for Redis Cluster connection, including synchronous and asynchronous APIs, Redis data type-specific operations, and integration with Spring Data.
Lettuce Library
Lettuce
Lettuce creates and manages connections to Redis Cluster based on TCP Channel and event handling using the Netty library. Therefore, dependencies on Netty and Reactor-related libraries are required. The Lettuce library provides various features, including connection classes for Cluster, StandAlone, and Sentinel, as well as support for ID/Password and SSL-based authentication. After connecting to Redis Cluster, if a failure occurs on the Master or Slave node, or if a node is added or deleted, the Lettuce library can reflect the topology change in real-time.
[ Lettuce Maven ]
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce core</artifactId>
<version>${version}</version>
</dependency>
Spring Data Redis
RDBMS provides a JDBC interface-based driver, and the Spring Framework provides an interface that easily registers the driver as a Spring Bean. Lettuce is based on the Spring Data Redis library and can be easily registered and used in the Spring Framework. It calls the Connection Pool and Redis Operation objects in the form of Spring Beans.
[ Spring Data Redis Maven ]
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${version}</version>
</dependency>
Redis Cluster Connection Settings
Redis Cluster Configuration
The IP address and port value of the node in the cluster, as well as the authentication information, are set as a Redis Cluster connection information management object. It is recommended to enter all Master/Slave information in the cluster, and it is referenced when the client starts for the first time. If authentication is not applied to the Redis Cluster, the authentication information is not set.
[ Redis Cluster Configuration Code Example ]
List<String> host;
RedisClusterConfiguration configuration = new RedisClusterConfiguration(this.host);
configuration.setUsername("username");
configuration.setPassword("password");
Cluster Topology Refresh Options
Redis Cluster status management and dynamic reflection of Master/Slave failures or Cluster changes can be set. Lettuce can be set to periodically check the Cluster status or check the Cluster status when a Trigger occurs, and the check interval can be defined.
[ Cluster Topology Refresh Options Setting List ]
| Option | Description | Default |
|---|---|---|
| enableAllAdaptiveRefreshTriggers | Redis Cluster Trigger occurrence topology reconstruction | False |
| refreshTriggersReconnectAttempts | RefreshTrigger.PERSISTENT_RECONNECTS set number of Reconnect Attempt occurrence topology refresh | 5 |
| adaptiveRefreshTriggersTimeout | Trigger-induced topology refresh time interval; if the trigger is frequently issued and the refresh occurs frequently, it does not repeatedly refresh within the specified time to prevent load on Redis | 30 |
| enablePeriodicRefresh | Topology refresh execution | False |
| refreshPeriod | enablePeriodicRefresh execution cycle | 60 |
| dynamicRefreshSources | If set to true, topology information is obtained from all discovered nodes. If set to false, topology information is obtained only from the initially set seed node | True |
[ Cluster Topology Refresh Options Code Example ]
ClusterTopologyRefreshOptions topologyRefreshOptions =
ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(60))
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
.refreshTriggersReconnectAttempts(3)
.build()
Lettuce Client Configuration
The class that manages overall client-related options, allowing you to configure connection and command timeouts, as well as TCP-related settings. Timeout settings are related to application failures and failover times, so you should define values considering Redis Cluster and topology settings.
[ ClusterClientOptions Setting List ]
| Option | Description | Default |
|---|---|---|
| autoReconnect | Whether to retry a connection that was closed without a close event | True |
| timeoutOptions | Command timeout for GET and PUT operations | X |
| socketOptions | Connection timeout with a default value of 10 seconds | 10 |
| disconnectedBehavior | Setting for handling ongoing operations when a connection is disconnected, which saves operations to a queue when auto-reconnect is enabled and fails when auto-reconnect is disabled | DEFAULT |
Lettuce Connection Factory
A class that provides connections to clients based on the configuration classes mentioned earlier. You can set read policies and add Netty-related options to manage TCP. You can do this. Generally, the created LettuceConnectionFactory object is registered as a Spring Bean, making it easily accessible for Redis-related clients.
[ LettuceConnectionFactory code example ]
LettuceConnectionFactory connectionFactory=new
LettuceConnectionFactory(configuration
,LettuceClientConfiguration.builder()
.readFrom(ReadFrom.REPLICA_PREFERRED)
.commandTimeout(Duration.ofMillis(${user}))
.clientOptions(
ClusterClientOptions.builder()
.autoReconnect(true)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.DEFAULT)
.topologyRefreshOptions(topologyRefreshOptions)
.build())
.clientResources(clientResource)
.build());
RedisTemplate<K,V>
A class that provides client operation API, business classes using Redis Cluster must use RedisTemplate. RedisTemplate receives LettuceConnectionFactory as a parameter to obtain a connection, and the created RedisTemplate is registered as a Spring Bean for use.
[ RedisTemplate code example ]
//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 Option Settings
Netty Epoll Options
Generally, TCP settings are partially set by the client and mostly applied based on kernel parameters in the OS where the Application is running. The Netty Epoll library provides detailed TCP options to solve problems that cannot be solved with existing client TCP options. There was an issue with the existing Lettuce library where Failover did not work in case of Network failure in Read Replica, but it can be solved by applying TCP_USER_TIMEOUT in Epoll options.
[Commonly Used Epoll Options]
| Option | Description |
|---|---|
| EpollChannelOption.TCP_KEEPIDLE | If there is no load during KEEPIDLE, it transitions to Idle state and performs status checking |
| EpollChannelOption.TCP_KEEPCNT | After transitioning to Idle state, it performs status checking for KEEPCNT times |
| EpollChannelOption.TCP_KEEPINTVL | After transitioning to Idle state, it performs status checking at an interval of time |
| EpollChannelOption.TCP_USER_TIMEOUT | If there is no response to a TCP request for USER_TIMEOUT, it is considered a failure and the TCP connection is released. If it is 0, this option is not used. |
Netty Epoll Library
The Epoll library contains compiled files for each OS. Therefore, Classifier is It must be specified, and the Linux environment library has an SO file inside, which is used every time the Epoll option is called.
[Linux netty epoll pom.xml example]
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version> 1.85.Final</version>
<classifier>linux-x86_64</classifier>
</dependency>
nettyCustomizer
To apply the Netty Epoll option in Lettuce, the NettyCustomizer interface must be implemented, and the Epoll option must be set in the internal method. The availability of Epoll can be checked with the EpollProvider.isAvailable method.
[NettyCustomizer interface implementation example]
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.option(EpollChannelOption.TCP_KEEPCNT, ${user});
bootstrap.option(EpollChannelOption.TCP_USER_TIMEOUT, ${user});
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
}).build();
Lettuce-based Operation Code Example
Lettuce Configuration Class Example
Based on Redis Cluster connection information, the settings for LettuceConnectionFactory and RedisTemplate are initialized and registered as a Spring Bean. ClientResource applies Netty Epoll options, ClusterTopology sets Cluster status information options, and ClientConfiguration defines detailed client options.
[ Lettuce Configuration Class Example ]
@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 Example
RedisTemplate Bean can be used to perform GET and PUT operations on Redis Cluster targets. Redis Cluster status changes or Failover are performed by the Lettuce library based on the options set in RedisConfig.
[ Redis Operation Example ]
@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);