Lettuce
GitHub Overview
redis/lettuce
Advanced Java Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.
Topics
Star History
Cache Library
Lettuce
Overview
Lettuce is an advanced Redis client library for Java applications.
Details
Lettuce is an officially supported Java Redis client that joined Redis' official client family in 2024, provided as open source under the MIT license. It features thread-safe design allowing multiple threads to safely share a single connection, supporting four programming models: synchronous, asynchronous, reactive, and Kotlin coroutines. Built on Netty's non-blocking I/O architecture, it delivers high performance and low latency while providing rich features including Redis Sentinel, Redis Cluster, pipelining, automatic reconnection, SSL/TLS, and Pub/Sub. Leveraging Java 8's CompletionStage interface, it provides fine-grained control over how applications receive data. The library supports all Redis commands and deployment models, making it suitable for various use cases from simple caching to complex distributed systems. Its scalable and thread-safe design eliminates the need for connection pooling in most scenarios, while its reactive API integrates seamlessly with Project Reactor for reactive programming paradigms.
Pros and Cons
Pros
- Thread-Safe: Single connection can be safely shared across multiple threads
- High Performance: Netty-based non-blocking I/O for high throughput
- Rich API Models: Supports sync, async, reactive, and coroutine paradigms
- Robust Connection Management: Automatic reconnection and command buffering
- Complete Redis Support: Full command set and deployment model coverage
- Official Support: Officially supported by Redis with continuous development
- Spring Integration: Excellent integration with Spring Data Redis
Cons
- Learning Curve: Multiple API models and advanced features require study
- Blocking Operation Constraints: BLPOP and MULTI/EXEC need dedicated connections
- Memory Overhead: Rich feature set may consume slightly more memory
- Configuration Complexity: Advanced features may require complex configuration
Key Links
- Lettuce Official Site
- GitHub Repository
- Official Documentation
- API Reference
- Redis Official Blog
- Maven Central
Code Examples
Basic Connection and Operations
import io.lettuce.core.*;
// Create Redis client
RedisClient client = RedisClient.create("redis://localhost:6379");
// Establish connection
StatefulRedisConnection<String, String> connection = client.connect();
// Get synchronous commands
RedisCommands<String, String> commands = connection.sync();
// Basic operations
commands.set("key", "Hello, Lettuce!");
String value = commands.get("key");
System.out.println(value); // "Hello, Lettuce!"
// Clean up resources
connection.close();
client.shutdown();
Asynchronous API
import io.lettuce.core.api.async.RedisAsyncCommands;
import java.util.concurrent.CompletableFuture;
RedisClient client = RedisClient.create("redis://localhost:6379");
StatefulRedisConnection<String, String> connection = client.connect();
RedisAsyncCommands<String, String> async = connection.async();
// Asynchronous SET operation
RedisFuture<String> setResult = async.set("async-key", "async-value");
// Asynchronous GET operation
RedisFuture<String> getValue = async.get("async-key");
// Handle results
setResult.thenAccept(result -> System.out.println("SET result: " + result));
getValue.thenAccept(value -> System.out.println("GET result: " + value));
// Execute multiple operations concurrently
List<RedisFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
futures.add(async.set("key-" + i, "value-" + i));
}
// Wait for all operations to complete
LettuceFutures.awaitAll(1, TimeUnit.MINUTES,
futures.toArray(new RedisFuture[0]));
Reactive API
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import reactor.core.publisher.Mono;
RedisClient client = RedisClient.create("redis://localhost:6379");
StatefulRedisConnection<String, String> connection = client.connect();
RedisReactiveCommands<String, String> reactive = connection.reactive();
// Reactive streams
Mono<String> setMono = reactive.set("reactive-key", "reactive-value");
Mono<String> getMono = reactive.get("reactive-key");
// Chain operations
setMono
.then(getMono)
.doOnNext(value -> System.out.println("Value: " + value))
.subscribe();
// Process multiple values
reactive.mget("key1", "key2", "key3")
.flatMapIterable(values -> values)
.filter(value -> value.hasValue())
.map(value -> value.getValue())
.doOnNext(System.out::println)
.subscribe();
Redis Cluster Connection
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
// Configure cluster nodes
RedisURI node1 = RedisURI.create("redis://localhost:7000");
RedisURI node2 = RedisURI.create("redis://localhost:7001");
RedisURI node3 = RedisURI.create("redis://localhost:7002");
// Create cluster client
RedisClusterClient clusterClient = RedisClusterClient
.create(Arrays.asList(node1, node2, node3));
// Connect to cluster
StatefulRedisClusterConnection<String, String> connection =
clusterClient.connect();
RedisAdvancedClusterCommands<String, String> commands =
connection.sync();
// Cluster operations
commands.set("cluster-key", "cluster-value");
String value = commands.get("cluster-key");
// Configure read preference (read from replica)
connection.setReadFrom(ReadFrom.REPLICA);
connection.close();
clusterClient.shutdown();
SSL/TLS Connection
// SSL/TLS connection configuration
RedisURI redisUri = RedisURI.Builder.redis("secure.redis.com")
.withPort(6380)
.withSsl(true)
.withPassword("secure-password")
.withDatabase(0)
.build();
RedisClient client = RedisClient.create(redisUri);
StatefulRedisConnection<String, String> connection = client.connect();
// Execute commands over SSL
RedisCommands<String, String> commands = connection.sync();
commands.set("secure-key", "secure-value");
Pub/Sub Functionality
import io.lettuce.core.pubsub.RedisPubSubListener;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
// Pub/Sub connection
StatefulRedisPubSubConnection<String, String> pubsubConnection =
client.connectPubSub();
// Configure listener
pubsubConnection.addListener(new RedisPubSubListener<String, String>() {
@Override
public void message(String channel, String message) {
System.out.println("Received: " + message + " from " + channel);
}
@Override
public void subscribed(String channel, long count) {
System.out.println("Subscribed to " + channel);
}
@Override
public void unsubscribed(String channel, long count) {
System.out.println("Unsubscribed from " + channel);
}
});
// Subscribe to channels
RedisPubSubCommands<String, String> pubsub = pubsubConnection.sync();
pubsub.subscribe("news", "updates");
// Publish message (from separate connection)
RedisCommands<String, String> publisher = client.connect().sync();
publisher.publish("news", "Breaking news!");