Lettuce 指南
将您的 Lettuce 应用程序连接到 Redis 数据库
Lettuce 是 Redis 的异步 Java 客户端。如果您需要同步 Java 客户端,请使用 Jedis。以下部分解释了如何安装 Lettuce
并将您的应用程序连接到 Redis 数据库。
Lettuce
需要一个正在运行的 Redis 或 Redis Stack 服务器。有关 Redis 安装说明,请参见 入门。
安装
要将 Lettuce 作为依赖项包含在您的应用程序中,请按如下所示编辑相应的依赖项文件。
如果您使用 Maven,请将以下依赖项添加到您的 pom.xml
中
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.3.2.RELEASE</version> <!-- Check for the latest version on Maven Central -->
</dependency>
如果您使用 Gradle,请将此行包含在您的 build.gradle
文件中
dependencies {
compileOnly 'io.lettuce:lettuce-core:6.3.2.RELEASE'
}
如果您希望直接使用 JAR 文件,请从 Maven Central 或任何其他 Maven 存储库下载最新的 Lettuce 和(可选)Apache Commons Pool2 JAR 文件。
要从源代码构建,请参见 Lettuce 源代码 GitHub 存储库 上的说明。
连接
首先创建到您的 Redis 服务器的连接。使用 Lettuce 可以通过多种方式实现这一点。以下是一些方法。
异步连接
package org.example;
import java.util.*;
import java.util.concurrent.ExecutionException;
import io.lettuce.core.*;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.StatefulRedisConnection;
public class Async {
public static void main(String[] args) {
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisAsyncCommands<String, String> asyncCommands = connection.async();
// Asynchronously store & retrieve a simple string
asyncCommands.set("foo", "bar").get();
System.out.println(asyncCommands.get("foo").get()); // prints bar
// Asynchronously store key-value pairs in a hash directly
Map<String, String> hash = new HashMap<>();
hash.put("name", "John");
hash.put("surname", "Smith");
hash.put("company", "Redis");
hash.put("age", "29");
asyncCommands.hset("user-session:123", hash).get();
System.out.println(asyncCommands.hgetall("user-session:123").get());
// Prints: {name=John, surname=Smith, company=Redis, age=29}
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
redisClient.shutdown();
}
}
}
在 参考指南 中了解有关异步 Lettuce API 的更多信息。
响应式连接
package org.example;
import java.util.*;
import io.lettuce.core.*;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import io.lettuce.core.api.StatefulRedisConnection;
public class Main {
public static void main(String[] args) {
RedisClient redisClient = RedisClient.create("redis://localhost:6379");
try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
RedisReactiveCommands<String, String> reactiveCommands = connection.reactive();
// Reactively store & retrieve a simple string
reactiveCommands.set("foo", "bar").block();
reactiveCommands.get("foo").doOnNext(System.out::println).block(); // prints bar
// Reactively store key-value pairs in a hash directly
Map<String, String> hash = new HashMap<>();
hash.put("name", "John");
hash.put("surname", "Smith");
hash.put("company", "Redis");
hash.put("age", "29");
reactiveCommands.hset("user-session:124", hash).then(
reactiveCommands.hgetall("user-session:124")
.collectMap(KeyValue::getKey, KeyValue::getValue).doOnNext(System.out::println))
.block();
// Prints: {surname=Smith, name=John, company=Redis, age=29}
} finally {
redisClient.shutdown();
}
}
}
在 参考指南 中了解有关响应式 Lettuce API 的更多信息。
Redis 集群连接
import io.lettuce.core.RedisURI;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
// ...
RedisURI redisUri = RedisURI.Builder.redis("localhost").withPassword("authentication").build();
RedisClusterClient clusterClient = RedisClusterClient.create(redisUri);
StatefulRedisClusterConnection<String, String> connection = clusterClient.connect();
RedisAdvancedClusterAsyncCommands<String, String> commands = connection.async();
// ...
connection.close();
clusterClient.shutdown();
TLS 连接
部署应用程序时,请使用 TLS 并遵循Redis 安全指南。
RedisURI redisUri = RedisURI.Builder.redis("localhost")
.withSsl(true)
.withPassword("secret!") // use your Redis password
.build();
RedisClient client = RedisClient.create(redisUri);
Lettuce 中的连接管理
Lettuce 使用ClientResources
来有效地管理共享资源,例如事件循环组和线程池。对于连接池,Lettuce 利用RedisClient
或RedisClusterClient
,可以有效地处理多个并发连接。
超时
Lettuce 为许多操作提供超时,例如命令执行、SSL 握手和哨兵发现。默认情况下,Lettuce 对这些操作使用 60 秒的全局超时值,但您可以使用每个操作的单独超时值来覆盖全局超时值。
以下是设置套接字级超时的示例。TCP_USER_TIMEOUT
设置适用于服务器停止响应而不确认最后一个请求的情况,而KEEPALIVE
设置适用于检测客户端和服务器之间没有流量的死连接。
RedisURI redisURI = RedisURI.Builder
.redis("localhost")
// set the global default from the default 60 seconds to 30 seconds
.withTimeout(Duration.ofSeconds(30))
.build();
try (RedisClient client = RedisClient.create(redisURI)) {
// or set specific timeouts for things such as the TCP_USER_TIMEOUT and TCP_KEEPALIVE
// A good general rule of thumb is to follow the rule
// TCP_USER_TIMEOUT = TCP_KEEP_IDLE+TCP_KEEPINTVL * TCP_KEEPCNT
// in this case, 20 = 5 + 5 * 3
SocketOptions.TcpUserTimeoutOptions tcpUserTimeout = SocketOptions.TcpUserTimeoutOptions.builder()
.tcpUserTimeout(Duration.ofSeconds(20))
.enable().build();
SocketOptions.KeepAliveOptions keepAliveOptions = SocketOptions.KeepAliveOptions.builder()
.interval(Duration.ofSeconds(5))
.idle(Duration.ofSeconds(5))
.count(3).enable().build();
SocketOptions socketOptions = SocketOptions.builder()
.tcpUserTimeout(tcpUserTimeout)
.keepAlive(keepAliveOptions)
.build();
client.setOptions(ClientOptions.builder()
.socketOptions(socketOptions)
.build());
StatefulRedisConnection<String, String> connection = client.connect();
System.out.println(connection.sync().ping());
}
连接池
使用 Lettuce 的典型方法是创建一个RedisClient
实例并重用它来建立与 Redis 服务器的连接。这些连接是多路复用的;也就是说,可以同时在单个或一组小连接上运行多个命令,这使得显式池不太实用。
Lettuce 提供了要与 Lettuce 异步连接方法一起使用的池配置。
package org.example;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.TransactionResult;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.codec.StringCodec;
import io.lettuce.core.support.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class Pool {
public static void main(String[] args) {
RedisClient client = RedisClient.create();
String host = "localhost";
int port = 6379;
CompletionStage<BoundedAsyncPool<StatefulRedisConnection<String, String>>> poolFuture
= AsyncConnectionPoolSupport.createBoundedObjectPoolAsync(
() -> client.connectAsync(StringCodec.UTF8, RedisURI.create(host, port)),
BoundedPoolConfig.create());
// await poolFuture initialization to avoid NoSuchElementException: Pool exhausted when starting your application
AsyncPool<StatefulRedisConnection<String, String>> pool = poolFuture.toCompletableFuture()
.join();
// execute work
CompletableFuture<TransactionResult> transactionResult = pool.acquire()
.thenCompose(connection -> {
RedisAsyncCommands<String, String> async = connection.async();
async.multi();
async.set("key", "value");
async.set("key2", "value2");
System.out.println("Executed commands in pipeline");
return async.exec().whenComplete((s, throwable) -> pool.release(connection));
});
transactionResult.join();
// terminating
pool.closeAsync();
// after pool completion
client.shutdownAsync();
}
}
在此设置中,LettuceConnectionFactory
是您需要实现的自定义类,它遵循 Apache Commons Pool 的PooledObjectFactory
接口,以管理池化StatefulRedisConnection
对象的生命周期事件。
DNS 缓存和 Redis
当您连接到具有多个端点(例如 Redis Enterprise Active-Active)的 Redis 数据库时,建议禁用 JVM 的 DNS 缓存以在多个端点之间负载均衡请求。
您可以在应用程序的代码中使用以下代码段来实现这一点
java.security.Security.setProperty("networkaddress.cache.ttl","0");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "0");