Skip to content

Commit 02d8fcd

Browse files
dwdoughertyuglidesazzad16mich-elle-luna
authored
Add Lettuce quick start guide (#2695)
* DOC-3406: add Lettuce quick start guide * Adhere to async and reactive APIs * Apply suggestions from code review Co-authored-by: M Sazzadul Hoque <[email protected]> * Apply suggestions from code review Thank you, Michelle! Co-authored-by: mich-elle-luna <[email protected]> --------- Co-authored-by: Igor Malinovskiy <[email protected]> Co-authored-by: M Sazzadul Hoque <[email protected]> Co-authored-by: mich-elle-luna <[email protected]>
1 parent 24cb0f3 commit 02d8fcd

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed

Diff for: docs/connect/clients/lettuce.md

+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
---
2+
title: "Lettuce guide"
3+
linkTitle: "Lettuce"
4+
description: Connect your Lettuce application to a Redis database
5+
weight: 3
6+
---
7+
8+
Install Redis and the Redis client, then connect your Lettuce application to a Redis database.
9+
10+
## Lettuce
11+
12+
Lettuce offers a powerful and efficient way to interact with Redis through its asynchronous and reactive APIs. By leveraging these capabilities, you can build high-performance, scalable Java applications that make optimal use of Redis's capabilities.
13+
14+
## Install
15+
16+
To include Lettuce as a dependency in your application, edit the appropriate dependency file as shown below.
17+
18+
If you use Maven, add the following dependency to your `pom.xml`:
19+
20+
```xml
21+
<dependency>
22+
<groupId>io.lettuce</groupId>
23+
<artifactId>lettuce-core</artifactId>
24+
<version>6.3.2.RELEASE</version> <!-- Check for the latest version on Maven Central -->
25+
</dependency>
26+
```
27+
28+
If you use Gradle, include this line in your `build.gradle` file:
29+
30+
```
31+
dependencies {
32+
compile 'io.lettuce:lettuce-core:6.3.2.RELEASE
33+
}
34+
```
35+
36+
If you wish to use the JAR files directly, download the latest Lettuce and, optionally, Apache Commons Pool2 JAR files from Maven Central or any other Maven repository.
37+
38+
To build from source, see the instructions on the [Lettuce source code GitHub repo](https://github.com/lettuce-io/lettuce-core).
39+
40+
## Connect
41+
42+
Start by creating a connection to your Redis server. There are many ways to achieve this using Lettuce. Here are a few.
43+
44+
### Asynchronous connection
45+
46+
```java
47+
package org.example;
48+
import java.util.*;
49+
import java.util.concurrent.ExecutionException;
50+
51+
import io.lettuce.core.*;
52+
import io.lettuce.core.api.async.RedisAsyncCommands;
53+
import io.lettuce.core.api.StatefulRedisConnection;
54+
55+
public class Async {
56+
public static void main(String[] args) {
57+
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
58+
59+
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
60+
RedisAsyncCommands<String, String> asyncCommands = connection.async();
61+
62+
// Asynchronously store & retrieve a simple string
63+
asyncCommands.set("foo", "bar").get();
64+
System.out.println(asyncCommands.get("foo").get()); // prints bar
65+
66+
// Asynchronously store key-value pairs in a hash directly
67+
Map<String, String> hash = new HashMap<>();
68+
hash.put("name", "John");
69+
hash.put("surname", "Smith");
70+
hash.put("company", "Redis");
71+
hash.put("age", "29");
72+
asyncCommands.hset("user-session:123", hash).get();
73+
74+
System.out.println(asyncCommands.hgetall("user-session:123").get());
75+
// Prints: {name=John, surname=Smith, company=Redis, age=29}
76+
} catch (ExecutionException | InterruptedException e) {
77+
throw new RuntimeException(e);
78+
} finally {
79+
redisClient.shutdown();
80+
}
81+
}
82+
}
83+
```
84+
85+
Learn more about asynchronous Lettuce API in [the reference guide](https://lettuce.io/core/release/reference/index.html#asynchronous-api).
86+
87+
### Reactive connection
88+
89+
```java
90+
package org.example;
91+
import java.util.*;
92+
import io.lettuce.core.*;
93+
import io.lettuce.core.api.reactive.RedisReactiveCommands;
94+
import io.lettuce.core.api.StatefulRedisConnection;
95+
96+
public class Main {
97+
public static void main(String[] args) {
98+
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
99+
100+
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
101+
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
102+
103+
// Reactively store & retrieve a simple string
104+
reactiveCommands.set("foo", "bar").block();
105+
reactiveCommands.get("foo").doOnNext(System.out::println).block(); // prints bar
106+
107+
// Reactively store key-value pairs in a hash directly
108+
Map<String, String> hash = new HashMap<>();
109+
hash.put("name", "John");
110+
hash.put("surname", "Smith");
111+
hash.put("company", "Redis");
112+
hash.put("age", "29");
113+
114+
reactiveCommands.hset("user-session:124", hash).then(
115+
reactiveCommands.hgetall("user-session:124")
116+
.collectMap(KeyValue::getKey, KeyValue::getValue).doOnNext(System.out::println))
117+
.block();
118+
// Prints: {surname=Smith, name=John, company=Redis, age=29}
119+
120+
} finally {
121+
redisClient.shutdown();
122+
}
123+
}
124+
}
125+
```
126+
127+
Learn more about reactive Lettuce API in [the reference guide](https://lettuce.io/core/release/reference/index.html#reactive-api).
128+
129+
### Redis Cluster connection
130+
131+
```java
132+
import io.lettuce.core.RedisURI;
133+
import io.lettuce.core.cluster.RedisClusterClient;
134+
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
135+
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
136+
137+
// ...
138+
139+
RedisURI redisUri = RedisURI.Builder.redis("localhost").withPassword("authentication").build();
140+
141+
RedisClusterClient clusterClient = RedisClusterClient.create(redisUri);
142+
StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();
143+
RedisAdvancedClusterAsyncCommands<String, String> commands = connection.async();
144+
145+
// ...
146+
147+
connection.close();
148+
clusterClient.shutdown();
149+
```
150+
151+
### TLS connection
152+
153+
When you deploy your application, use TLS and follow the [Redis security guidelines](/docs/management/security/).
154+
155+
```java
156+
RedisURI redisUri = RedisURI.Builder.redis("localhost")
157+
.withSsl(true)
158+
.withPassword("secret!") // use your Redis password
159+
.build();
160+
161+
RedisClient client = RedisClient.create(redisUri);
162+
```
163+
164+
165+
166+
## Connection Management in Lettuce
167+
168+
Lettuce uses `ClientResources` for efficient management of shared resources like event loop groups and thread pools.
169+
For connection pooling, Lettuce leverages `RedisClient` or `RedisClusterClient`, which can handle multiple concurrent connections efficiently.
170+
171+
A typical approach with Lettuce is to create a single `RedisClient` instance and reuse it to establish connections to your Redis server(s).
172+
These connections are multiplexed; that is, multiple commands can be run concurrently over a single or a small set of connections, making explicit pooling less critical.
173+
174+
Lettuce provides pool config to be used with Lettuce asynchronous connection methods.
175+
176+
177+
```java
178+
package org.example;
179+
import io.lettuce.core.RedisClient;
180+
import io.lettuce.core.RedisURI;
181+
import io.lettuce.core.TransactionResult;
182+
import io.lettuce.core.api.StatefulRedisConnection;
183+
import io.lettuce.core.api.async.RedisAsyncCommands;
184+
import io.lettuce.core.codec.StringCodec;
185+
import io.lettuce.core.support.*;
186+
187+
import java.util.concurrent.CompletableFuture;
188+
import java.util.concurrent.CompletionStage;
189+
190+
public class Pool {
191+
public static void main(String[] args) {
192+
RedisClient client = RedisClient.create();
193+
194+
String host = "localhost";
195+
int port = 6379;
196+
197+
CompletionStage<BoundedAsyncPool<StatefulRedisConnection<String, String>>> poolFuture
198+
= AsyncConnectionPoolSupport.createBoundedObjectPoolAsync(
199+
() -> client.connectAsync(StringCodec.UTF8, RedisURI.create(host, port)),
200+
BoundedPoolConfig.create());
201+
202+
// await poolFuture initialization to avoid NoSuchElementException: Pool exhausted when starting your application
203+
AsyncPool<StatefulRedisConnection<String, String>> pool = poolFuture.toCompletableFuture()
204+
.join();
205+
206+
// execute work
207+
CompletableFuture<TransactionResult> transactionResult = pool.acquire()
208+
.thenCompose(connection -> {
209+
210+
RedisAsyncCommands<String, String> async = connection.async();
211+
212+
async.multi();
213+
async.set("key", "value");
214+
async.set("key2", "value2");
215+
System.out.println("Executed commands in pipeline");
216+
return async.exec().whenComplete((s, throwable) -> pool.release(connection));
217+
});
218+
transactionResult.join();
219+
220+
// terminating
221+
pool.closeAsync();
222+
223+
// after pool completion
224+
client.shutdownAsync();
225+
}
226+
}
227+
```
228+
229+
In this setup, `LettuceConnectionFactory` is a custom class you would need to implement, adhering to Apache Commons Pool's `PooledObjectFactory` interface, to manage lifecycle events of pooled `StatefulRedisConnection` objects.
230+
231+
## DNS cache and Redis
232+
233+
When you connect to a Redis database with multiple endpoints, such as Redis Enterprise Active-Active, it's recommended to disable the JVM's DNS cache to load-balance requests across multiple endpoints.
234+
235+
You can do this in your application's code with the following snippet:
236+
237+
```java
238+
java.security.Security.setProperty("networkaddress.cache.ttl","0");
239+
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "0");
240+
```
241+
242+
## Learn more
243+
244+
- [Lettuce reference documentation](https://lettuce.io/docs/)
245+
- [Redis commands](https://redis.io/commands)
246+
- [Project Reactor](https://projectreactor.io/)

0 commit comments

Comments
 (0)