Lettuce Library based Redis Cluster Development vector
Lettuce Library based Redis Cluster Development vector
Overview
Redis Cluster distributes data based on sharding, and replication is performed using a Master‑Slave structure for each shard. Therefore, it differs from the traditional Redis (DBaaS) architecture of Redis Sentinel. Client development of this type is required. The Lettuce library is widely used for Redis Cluster client development. The Lettuce library provides synchronous and asynchronous APIs, supports operations for each Redis data type, and offers various features needed for connecting to Redis Cluster, such as integration with Spring Data.
Lettuce library
Lettuce
Lettuce is based on the Netty library, utilizing TCP channels and event-driven processing for Redis. Cluster and connection are created and managed. Therefore, as a dependency, Netty and Reactor-related libraries are needed. The Lettuce library provides various features, including StandAlone as well as Cluster. Provides a connection class for Sentinel and supports ID/P assword method and SSL-based authentication. After connecting to a Redis Cluster, if a failure occurs on a Master or Slave node or when adding a node and When deleting, the Lettuce library can reflect the topology changes in real time.
[ Lettuce Maven ]
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce core</artifactId>
<version>${version}</version>
</dependency>
Spring-data-redis
RDBMS provides a driver based on the JDBC interface, and Spring Framework provides the corresponding. Provides an interface that easily registers drivers as Spring Beans. Lettuce is based on the Spring Data Redis library and integrates easily with the Spring Framework. It can be registered and used. Calls the Connection Pool and Redis Operation objects as 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
Manage node IP addresses, port values, and authentication information within the cluster as a Redis Cluster connection information management object. Set. It is recommended to enter all Master/Slave information within the cluster and launch the client for the first time. Refer to the documentation. If authentication is not applied to the Redis Cluster, the authentication information will not be set.
[ RedisClusterConfiguration code example ]
List<String> host;
RedisClusterConfiguration configuration = new RedisClusterConfiguration(this.host);
configuration.setUsername("username");
configuration.setPassword("password");
Cluster Topology Refresh Options
Manage the Redis Cluster state, and when a Master/Slave failure or Cluster change occurs, handle it. You can set options for actions that are reflected dynamically. Lettuce does so periodically. You can check the cluster status, or set it to check the cluster status when a trigger occurs. It is available, and you can also define the check interval.
[ ClusterTopologyRefreshOptions Settings List ]
| Option | Explanation | default |
|---|---|---|
| enableAllAdaptiveRefreshTriggers | Reconstruct the topology when a trigger occurs in Redis Cluster | False |
| refreshTriggersReconnectAttempts | Perform a topology refresh when a Reconnect Attempt occurs the number of times set by RefreshTrigger.PERSISTENT_RECONNECTS. | 5 |
| adaptiveRefreshTriggersTimeout | Because triggers cause topology refreshes, frequent trigger emissions and frequent refreshes load Redis, so we avoid repeated refreshes within that time interval. | 30 |
| enablePeriodicRefresh | Whether to perform topology refresh | False |
| refreshPeriod | enablePeriodicRefresh execution interval | 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 configured seed node. | True |
[ ClusterTopologyRefreshOptions code example ]
ClusterTopologyRefreshOptions topologyRefreshOptions =
ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(60))
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
.refreshTriggersReconnectAttempts(3)
.build()
Lettuce Client Configuration
A class that manages overall client-related options, such as connection and command You can configure timeout and TCP-related settings. Timeout-related settings are Application Since it is related to outage and failover time, consider the Redis Cluster and topology settings when setting the value. Define
[ ClusterClientOptions configuration list ]
| option | Explanation | default |
|---|---|---|
| autoReconnect | Whether to retry a connection that was terminated without a close event | True |
| timeoutOptions | Timeout for GET and PUT operations, same as commandTimeout | X |
| socketOptions | The default timeout for the connection is 10 seconds. | 10 |
| disconnectedBehavior | When the connection is lost, configure handling of ongoing operations: if auto reconnect is enabled, store the operation in a queue; if auto reconnect is disabled, treat it as a failure. | DEFAULT |
Lettuce Connection Factory
Providing a connection to the client based on the configuration classes mentioned earlier This is a class. You can set the read policy and add Netty-related options that manage TCP. It can be done. Typically, the generated LettuceConnectionFactory object is Spring Register it as a Bean so that it can be easily used in Redis-related clients.
[ LettuceConnectionFactory code example ]
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>
A class that provides the client Operation API for businesses using Redis Cluster. The class must use RedisTemplate. RedisTemplate is Takes a LettuceConnectionFactory as a parameter, obtains a Connection, and creates RedisTemplate is registered as a Spring Bean and used.
[ 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 configuration
Netty Epoll options
Generally, for TCP settings, the client configures only a few parameters while most are handled by the application. It is applied based on kernel parameters in the OS. The Netty Epoll library provides detailed TCP options. It provides a way to solve problems that cannot be addressed with the existing client TCP options. With the existing Lettuce library, failover does not work when there is a network outage on the read replica. There is an issue, which we resolve by applying TCP_USER_TIMEOUT in the Epoll options.
[Frequently used Epoll options]
| Option | Explanation |
|---|---|
| EpollChannelOption.TCP_KEEPIDLE | If there is no load during KEEP IDLE, transition to the Idle state and perform a status check. |
| EpollChannelOption.TCP_KEEPCNT | After transitioning to the idle state, perform status checks for KEEPCNT iterations. |
| EpollChannelOption.TCP_KEEPINTVL | Interval time when performing a status check after switching to Idle state |
| EpollChannelOption.TCP_USER_TIMEOUT | If there is no response for USER_TIMEOUT after a TCP request, it is considered a failure and the TCP connection is terminated; if set to 0, this option is not used. |
Netty Epoll library
The Epoll library includes compiled files for each OS. Therefore, the Classifier It must be specified, and the Linux environment library includes an SO file that is used whenever an Epoll option is invoked.
netty epoll pom.xml example for Linux
<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, you need the NettyCustomizer interface. Implement it and set the Epoll option in the internal method. Whether Epoll is applied You can check 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.opt ion(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, configure LettuceConnectionFactory and RedisTemplate Initialize the configuration and register it as a Spring Bean. ClientResource is Netty Apply the Epoll option and set the Cluster state information option in ClusterTopology, In ClientConfiguration, detailed client options are defined.
[ Lettuce Config uration 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
You can retrieve a RedisTemplate bean and execute GET and PUT operations against a Redis Cluster. Redis Cluster state changes or failovers are performed by the Lettuce library based on the options configured 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);