Redis Extension Reference Guide

Redis 是一种用作数据库、缓存、流引擎和消息代理的内存数据存储。Quarkus Redis 扩展允许将 Quarkus 应用与 Redis 集成。

Redis is an in-memory data store used as a database, cache, streaming engine, and message broker. The Quarkus Redis extension allows integrating Quarkus applications with Redis.

若要使用此扩展,用户必须熟悉 Redis,尤其是了解命令机制及其组织方式。通常,我们建议:

To use this extension, the user must be familiar with Redis, especially understanding the mechanism of commands and how they are organized. Typically, we recommend:

  1. The interactive tutorial introducing Redis.

  2. The command references explains Redis commands and contains links to reference documentation.

此扩展提供了命令性和响应式 API 以及低级和高级(类型安全)客户端。

This extension provides imperative and reactive APIs and low-level and high-level (type-safe) clients.

Use the Redis Client

如果你想使用此扩展,则需要首先添加 io.quarkus:quarkus-redis 扩展。在你的 pom.xml 文件中,添加:

If you want to use this extension, you need to add the io.quarkus:quarkus-redis extension first. In your pom.xml file, add:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-redis-client</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-redis-client")

使用此依赖项,你之后可以注入 Redis 客户端或 datasource(高级、类型安全 API),例如:

With this dependency, you can then inject Redis clients or datasource (high-level, type-safe API), such as:

import io.quarkus.redis.datasource.RedisDataSource;

// ...
@Inject RedisAPI lowLevelClient;
@Inject RedisDataSource highLevelApi;

One extension, multiple APIs 部分提供了有关 quarkus-redis 扩展提供的各种 API 的更多详细信息。

More details about the various APIs offered by the quarkus-redis extension are available in the One extension, multiple APIs section.

如需将 Redis 用作缓存后端,请参阅 Redis Cache Backend reference

To use Redis as a cache backend, refer to the Redis Cache Backend reference.

One extension, multiple APIs

此扩展提供了多种与 Redis 交互的方式:

This extension provides multiple ways to interact with Redis:

  • the low-level Vert.x client: it’s a fully reactive, non-blocking, and asynchronous client. More details on the Vert.x Redis Client documentation. Two APIs are exposed: io.vertx.redis.client.Redis, and io.vertx.redis.client.RedisAPI. You will generally use the latter, except if you need to manage connections yourself.

  • the low-level Mutiny variant of the Vert.x API: Unlike the previous one, it exposes a Mutiny API and provides both reactive and imperative methods (suffixed with andAwait()). Two APIs are exposed: io.vertx.mutiny.redis.client.Redis and io.vertx.mutiny.redis.client.RedisAPI. You will generally use the latter, except if you need to manage connections yourself.

  • a high-level reactive data source: A type-safe, high-level API to interact with Redis. This API is fully reactive and asynchronous. It exposes a Mutiny API. It exposes the io.quarkus.redis.datasource.ReactiveRedisDataSource interface.

  • a high-level imperative data source: A type-safe, high-level API to interact with Redis. It is the imperative variant of the reactive data source. It exposes the io.quarkus.redis.datasource.RedisDataSource interface.

为了帮助你选择适合自己的 API,这里有几点建议:

To help you select the suitable API for you, here are some recommendations:

  • If you are building an imperative (classic) Quarkus application integrating with Redis: use io.quarkus.redis.datasource.RedisDataSource.

  • If you are building a reactive Quarkus application integrating with Redis: use io.quarkus.redis.datasource.ReactiveRedisDataSource.

  • If you need fine-grain control, or execute commands in a generic way: use io.vertx.mutiny.redis.client.RedisAPI

  • If you have existing Vert.x code, use io.vertx.redis.client.RedisAPI

  • If you need to emit custom commands, you can either use the data sources (reactive or imperative) or the io.vertx.mutiny.redis.client.Redis.

[id="inject-the-default-and-named-clients"] Inject the default and named clients

此扩展允许你配置 default Redis 客户端/数据源或 named 客户端/数据源。当你需要连接到多个 Redis 实例时,后者至关重要。

This extension lets you configure a default Redis client/data sources or named ones. The latter is essential when you need to connect to multiple Redis instances.

使用 quarkus.redis.* 属性配置默认连接。例如,要配置默认 Redis 客户端,请使用:

The default connection is configured using the quarkus.redis.* properties. For example, to configure the default Redis client, use:

quarkus.redis.hosts=redis://localhost/

使用默认连接时,你可以使用 plain @Inject 注入各种 API:

When using the default connection, you can inject the various APIS using a plain @Inject:

@ApplicationScoped
public class RedisExample {
    @Inject ReactiveRedisDataSource reactiveDataSource;
    @Inject RedisDataSource redisDataSource;
    @Inject RedisAPI redisAPI;
    // ...
}

通常,你注入一个;前面的代码段只是一个示例。

In general, you inject a single one; the previous snippet is just an example.

Named 客户端使用 quarkus.redis.<name>.* 属性配置:

Named clients are configured using the quarkus.redis.<name>.* properties:

quarkus.redis.my-redis-1.hosts=redis://localhost/
quarkus.redis.my-redis-2.hosts=redis://my-other-redis:6379

要访问 API,你需要使用 @RedisClientName 限定符:

To access the APIs, you need to use the @RedisClientName qualifier:

@ApplicationScoped
public class RedisExample {
    @Inject @RedisClientName("my-redis-1") ReactiveRedisDataSource reactiveDataSource;
    @Inject @RedisClientName("my-redis-2") RedisDataSource redisDataSource;
    // ...
}

使用 @RedisClientName 时,您可以省略 @Inject 注释。

You can omit the @Inject annotation when using @RedisClientName.

Connect to the Redis server

Redis 扩展程序可以使用 4 种不同的模式:

The Redis extension can operate in 4 distinct modes:

  • Simple client (probably what most users need).

  • Sentinel (when working with Redis in High Availability mode).

  • Cluster (when working with Redis in Clustered mode).

  • Replication (single shard, one node write, multiple read).

连接 URL 使用 quarkus.redis.hosts (或 quarkus.redis.<name>.hosts)配置如下:

The connection url is configured with the quarkus.redis.hosts (or quarkus.redis.<name>.hosts) as follows:

quarkus.redis.hosts=redis://[:password@]host[:port][/db-number]

Use Unix Socket

使用 Unix 套接字时,您需要:

When using unix-socket, you need:

quarkus.redis.hosts=unix://[:password@]/domain/docker.sock[?select=db-number]

Use the Sentinel Mode

使用 Sentinel 时,您需要传递多个 host urls 并将客户端类型配置为 sentinel

When using Sentinel, you need to pass multiple host urls and configure the client type to sentinel:

quarkus.redis.hosts=redis://localhost:5000,redis://localhost:5001,redis://localhost:5002
quarkus.redis.client-type=sentinel

# Optional
quarkus.redis.master-name=my-sentinel # Default is mymaster
quarkus.redis.role=master # master is the default

此处的主机 URL 必须是哨兵服务器。客户端将从其中一个哨兵获取实际 Redis 服务器(主服务器或副本,取决于 role)的 URL,使用 master-name 作为“主服务器集”的标识符。

The host URLs here must be the sentinel servers. The client will obtain the URLs of actual Redis servers (master or replicas, depending on role) from one of the sentinels, using the master-name as an identifier of the "master set".

请注意,您实际上永远不想配置 quarkus.redis.role=sentinel。此设置意味着 Redis 客户端将在哨兵服务器之一上直接执行命令,而不是在哨兵守护的实际 Redis 服务器上。

Note that you practically never want to configure quarkus.redis.role=sentinel. This setting means that the Redis client will execute commands directly on one of the sentinel servers, instead of an actual Redis server guarded by the sentinels.

Use the Cluster Mode

在集群模式下使用 Redis 时,您需要传递多个 host urls,将客户端类型配置为 cluster 并配置 replicas 模式:

When using Redis in cluster mode, you need to pass multiple host urls, configure the client type to cluster and configure the replicas mode:

quarkus.redis.hosts=redis://localhost:7000,redis://localhost:7001,redis://localhost:7002
quarkus.redis.client-type=cluster

# Optional
quarkus.redis.replicas=share # defaults to "never"

此处的主机 URL 必须是集群成员中的部分成员。并非所有集群成员都需要进行配置,因为客户端将从已知服务器之一获取完整的集群拓扑。但是,建议配置至少 2 个或 3 个节点,而不仅仅是 1 个。

The host URLs here must be some of the cluster members. Not all cluster members need to be configured, as the client will obtain a full cluster topology from one of the known servers. However, it is advisable to configure at least 2 or 3 nodes, not just 1.

默认情况下,所有命令都将发送到主节点(如果命令有密钥,则它是拥有该密钥的分片的主节点,否则是随机主节点)。可以将 Redis 客户端配置为向副本节点发送只读命令(查询)。将 quarkus.redis.replicas 配置属性设置为:

By default, all commands are sent to a master node (if the command has a key, it is the master node of the shard that owns the key, otherwise it is a random master node). It is possible to configure the Redis client to send read-only commands (queries) to replica nodes. Set the quarkus.redis.replicas configuration property to:

  • never: never use replica nodes, always send queries to master nodes (this is the default)

  • always: always use replica nodes (if there’s more than one replica in the shard, it is selected randomly), never send queries to master nodes

  • share: use master and replica nodes to execute queries (the specific node for each query is selected randomly)

请注意,Redis 中的复制是异步的,因此副本节点可能落后于其主节点。

Note that replication in Redis is asynchronous, so replica nodes may be lagging behind their master nodes.

Use the Replication Mode

在使用复制模式时,您需要传递单个主机 URL 并将类型配置为 replication

When using the replication mode, you need to pass a single host url and configure the type to be replication:

quarkus.redis.hosts=redis://localhost:7000
quarkus.redis.client-type=replication

# Optional
quarkus.redis.replicas=share # defaults to "never"

默认情况下,所有命令都发送至主节点。可以将 Redis 客户端配置为向副本节点发送只读命令(查询)。将 quarkus.redis.replicas 配置属性设置为:

By default, all commands are sent to the master node. It is possible to configure the Redis client to send read-only commands (queries) to replica nodes. Set the quarkus.redis.replicas configuration property to:

  • never: never use replica nodes, always send queries to the master node (this is the default)

  • always: always use replica nodes (if there’s more than one replica, it is selected randomly), never send queries to the master node

  • share: use master and replica nodes to execute queries (the specific node for each query is selected randomly)

请注意,Redis 中的复制是异步的,因此副本节点可能落后于主节点。

Note that replication in Redis is asynchronous, so replica nodes may be lagging behind the master node.

Connect to Redis Cloud

要连接到 Redis Cloud,你需要以下属性:

To connect to redis cloud, you need the following properties:

quarkus.redis.hosts=<the redis cloud url such as redis://redis-12436.c14.us-east-1-3.ec2.cloud.redislabs.com:12436>
quarkus.redis.password=<the password>

Use TLS

要使用 TLS,你需要:

To use TLS, you need to:

  1. Set the quarkus.redis.tls.enabled=true property or use the TLS registry (recommended)

  2. Make sure that your URL starts with rediss:// (with two s)

使用 TLS 注册表时,需要使用具名配置以避免与其他 TLS 配置发生冲突:

When using the TLS registry, you need to use a named configuration to avoid conflicts with other TLS configurations:

quarkus.tls.redis.trust-store.p12.path=client.p12
quarkus.tls.redis.trust-store.p12.password=secret

quarkus.redis.tls-configuration-name=redis # Reference the named configuration

默认主机名验证器设置为 NONE,这意味着它不会验证主机名。你可以通过设置 quarkus.redis.tls.hostname-verification-algorithm 属性(如 HTTPS)来更改此行为。

The default hostname verifier is set to NONE, meaning it does not verify the host name. You can change this behavior by setting the quarkus.redis.tls.hostname-verification-algorithm property, to HTTPS for example.

Configure the authentication

Redis 密码可以设置在 redis:// URL 中或使用 quarkus.redis.password 属性设置。建议使用后者,如果可能,使用机密或环境变量来配置密码。

The Redis password can be set in the redis:// URL or with the quarkus.redis.password property. We recommend the latter, and if possible, using secrets or an environment variable to configure the password.

关联的环境变量是 QUARKUS_REDIS_PASSWORD,对于具名客户端则是 QUARKUS_REDIS_<NAME>_PASSWORD

The associated environment variable is QUARKUS_REDIS_PASSWORD, or QUARKUS_REDIS_<NAME>_PASSWORD for named clients.

Connection pooling

与 Redis 的连接始终是池化的。默认情况下,池中最大连接数为 6。这可以通过 quarkus.redis.max-pool-size 配置。

Connections to Redis are always pooled. By default, maximum number of connections in the pool is 6. This can be configured using quarkus.redis.max-pool-size.

当连接池耗尽时,获取连接的尝试会被放入队列。默认情况下,等待在队列中获取 Redis 连接的最大尝试次数为 24。这可以通过 quarkus.redis.max-pool-waiting 配置。

When the connection pool is depleted, attempts to obtain a connection are put into a queue. By default, maximum number of attempts waiting in the queue to obtain a Redis connection is 24. This can be configured using quarkus.redis.max-pool-waiting.

执行某些命令会修改服务器端状态和连接行为。这样的连接不能被重用,关闭时,它们不会被放回连接池;而是会彻底关闭。以下命令会导致此行为:

Executing certain commands modifies the server-side state and the behavior of the connection. Such connections cannot be reused and when closed, they are not put back into the pool; instead, they are truly closed. The commands that cause this behavior are:

  • subscription commands (SUBSCRIBE, UNSUBSCRIBE etc.)

  • SELECT

  • AUTH

Use Redis data sources

Quarkus 公开了一个位于 Redis 上的高级 API。此 API 类型安全,围绕 group 的概念构建,继承自 Redis command organization。此 API 可以更方便、安全地执行 Redis 命令。

Quarkus exposes a high-level API on top of Redis. This API is type-safe and structured around the notion of group, inherited from the Redis command organization. This API lets you execute Redis commands more conveniently and safely.

Inject data sources

对于每个已配置的 Redis 客户端,会公开两个 Redis 数据源:

For each configured Redis client, two Redis data sources are exposed:

  • io.quarkus.redis.datasource.RedisDataSource - an imperative (blocking) Redis data source. Each operation blocks until a response is received or a timeout is reached

  • io.quarkus.redis.datasource.ReactiveRedisDataSource - a reactive Redis data source returning Uni<X> or Multi<X>.

如果您配置了 default Redis 客户端,则可以使用以下内容注入数据源:

If you configured the default Redis client, you could inject the data sources using:

@Inject RedisDataSource defaultRedisDataSource;
@Inject ReactiveRedisDataSource defaultReactiveRedisDataSource;

如果您配置了 named Redis 客户端,则需要使用 `io.quarkus.redis.RedisClientName`限定符来选择正确的客户端:

If you configured a named Redis client, you need to use the io.quarkus.redis.RedisClientName qualifier to select the right client:

@RedisClientName("my-redis") RedisDataSource myRedisDataSource;
@RedisClientName("my-redis") ReactiveRedisDataSource myReactiveRedisDataSource;

当使用 blocking 变体时,您可以使用以下内容配置默认超时:

When using the blocking variant, you can configure the default timeout with:

quarkus.redis.timeout=5s
quarkus.redis.my-redis.timeout=5s

默认超时配置为 10s。

The default timeout is configured to 10s.

All about delegation

阻塞数据源 (io.quarkus.redis.datasource.RedisDataSource) 是在反应式数据源 (io.quarkus.redis.datasource.ReactiveRedisDataSource) 的基础上实现的。ReactiveRedisDataSource 是在 io.vertx.mutiny.redis.Redis API 的基础上实现的。

The blocking data source (io.quarkus.redis.datasource.RedisDataSource) is implemented on top of the reactive one (io.quarkus.redis.datasource.ReactiveRedisDataSource). The ReactiveRedisDataSource is implemented on top of the io.vertx.mutiny.redis.Redis API.

Data Source groups

如上所述,API 分为以下几组:

As mentioned above, the API is divided into groups:

  • bitmap - .bitmap()

  • key (generic) - .key()

  • geo - .geo(memberType)

  • hash - .hash(`valueType)

  • hyperloglog - .hyperloglog(memberType)

  • list - .list(memberType)

  • pubsub - pubsub()

  • set - .set(memberType)

  • sorted-set - .sortedSet(memberType)

  • string - .value(valueType)

  • stream - .stream(`valueType)

  • transactions - withTransaction

  • json - .json() (requires the RedisJSON module on the server side)

  • bloom - .bloom() (requires the RedisBloom module on the server side)

  • cuckoo - .cuckoo() (requires the rRedisBloom module on the server side, which also provides the cuckoo filter commands)

  • count-min - .countmin() (requires the RedisBloom module on the server side, which also provides the count-min filter commands)

  • top-k - .topk() (requires the RedisBloom module on the server side, which also provides the top-k filter commands)

  • graph - .graph() (requires the RedisGraph module on the server side). These commands are marked as experimental. Also the module has been declared end of life by Redis.

  • search - .search() (requires the RedisSearch module on the server side).

  • auto-suggest - .autosuggest() (requires the RedisSearch module on the server side).

  • time-series - .timeseries() (requires the Redis Time Series module on the server side).

这些命令被标记为实验性的,因为我们需要在使命令稳定之前获得反馈。

These commands are marked as experimental, as we would need feedback before making them stable.

这些方法中的每一个都返回一个允许您执行与该组相关的命令的对象。以下代码摘录演示了如何使用 hash 组:

Each of these methods returns an object that lets you execute the commands related to the group. The following snippet demonstrates how to use the hash group:

@ApplicationScoped
public class MyRedisService {

    private static final String MY_KEY = "my-key";

    private final HashCommands<String, String, Person> commands;

    public MyRedisService(RedisDataSource ds) { (1)
        commands = ds.hash(Person.class); (2)
    }

    public void set(String field, Person value) {
        commands.hset(MY_KEY, field, value);  (3)
    }

    public Person get(String field) {
        return commands.hget(MY_KEY, field);  (4)
    }
}
1 Inject the RedisDataSource in the constructor
2 Creates the HashCommands object. This object has three type parameters: the type of the key, the type of the field, and the type of the member
3 Use the created commands to associate the field field with the value value
4 Use the created commands to retrieve the field field value.

Serializing and Deserializing data

数据源 API 会自动处理序列化和反序列化。默认情况下,非标准类型会被序列化为 JSON 并从 JSON 中反序列化。在这种情况下,将使用 quarkus-jackson

The data source APIs handle the serialization and deserialization automatically. By default, non-standard types are serialized into JSON and deserialized from JSON. In this case, quarkus-jackson is used.

Binary

要存储或检索二进制数据,请使用 byte[]

To store or retrieve binary data, use byte[].

Custom codec

你可以通过提供实现 `io.quarkus.redis.datasource.codecs.Codec`接口的 CDI _bean_来注册自定义编解码器:

You can register custom codec by providing a CDI bean implementing the io.quarkus.redis.datasource.codecs.Codec interface:

import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

import jakarta.enterprise.context.ApplicationScoped;

import io.quarkus.redis.datasource.codecs.Codec;

@ApplicationScoped
public class PersonCodec implements Codec {
    @Override
    public boolean canHandle(Type clazz) {
        return clazz.equals(Person.class);
    }

    @Override
    public byte[] encode(Object item) {
        var p = (Person) item;
        return (p.firstName + ";" + p.lastName.toUpperCase()).getBytes(StandardCharsets.UTF_8);
    }

    @Override
    public Object decode(byte[] item) {
        var value = new String(item, StandardCharsets.UTF_8);
        var segments = value.split(";");
        return new Person(segments[0], segments[1]);
    }
}

方法 `canHandle`会被调用以检查该编解码器是否可以处理特定的类型。以 `encode`方法接收的参数与该类型匹配。以方法 `decode`返回的对象也必须与该类型匹配。

The canHandle method is called to check if the codec can handle a specific type. The parameter received in the encode method matches that type. The object returned by the decode method must also match that type.

Use type reference

每个组都可以使用 Class,也可以使用 `TypeReference`对象进行配置。在处理 Java 泛型时,`TypeReference`非常有用:

Each group can be configured with Class, or with TypeReference objects. TypeReference are useful when dealing with Java generics:

@ApplicationScoped
public class MyRedisService {

    private static final String MY_KEY = "my-key";

    private final HashCommands<String, String, List<Person>> commands;

    public MyRedisService(RedisDataSource ds) {
        commands = ds.hash(new TypeReference<List<Person>>(){});
    }

    public void set(String field, List<Person> value) {
        commands.hset(MY_KEY, field, value);
    }

    public List<Person> get(String field) {
        return commands.hget(MY_KEY, field);
    }
}

使用事务时不能使用类型引用。这是一个已知限制。

You cannot use type references when using transaction. This is a known limitation.

Manipulate cached and binary data with the value group

组 `value`用于处理 Redis Strings。因此,该组不仅限于 Java 字符串,还可以用于整数(如计数器)或二进制内容(如图像)。

The value group is used to manipulate Redis Strings. Thus, this group is not limited to Java Strings but can be used for integers (like a counter) or binary content (like images).

Work with cached values

يمكنك استخدام setex كذاكرة تخزين مؤقتة باستخدام أمر setex الذي يخزن قيمة معينة لمفتاح معين لمدة زمنية معينة. يُوضح الجزء التالي كيف يمكن استخدام هذا الأمر لتخزين BusinessObject لمدة ثانية واحدة.

You can use Redis as a cache using the setex command, which stores a given value to a given key for a given duration. The following snippet shows how such a command can be used to store BusinessObject for 1 second.

@ApplicationScoped
public static class MyRedisCache {

    private final ValueCommands<String, BusinessObject> commands;

    public MyRedisCache(RedisDataSource ds) {
        commands = ds.value(BusinessObject.class);
    }

    public BusinessObject get(String key) {
        return commands.get(key);
    }

    public void set(String key, BusinessObject bo) {
        commands.setex(key, 1, bo); // Expires after 1 second
    }
}

يمكنك استخدام طريقة setnx فقط لتعيين القيمة إذا لم يتم تخزين أي قيمة للمفتاح المعطى.

You can use the setnx method only to set the value if no value has been stored for the given key.

توفر المجموعة key مزيدًا من التحكم الدقيق في انتهاء الصلاحية ووقت البقاء لكل مفتاح.

The key group provides more fine-grain control on expiration and ttl of each key.

يمكن أن تتلقى طريقة set أيضًا وسيطة SetArgs التي تعدل السلوك:

The set method can also receive a SetArgs argument that modify the behavior:

  • ex(seconds) - Set the specified expire time, in seconds.

  • px(milliseconds) - Set the specified expire time, in milliseconds.

  • exat(timestamp-seconds) - Set the specified Unix time at which the key will expire, in seconds.

  • pxat(timestamp-milliseconds) - Set the specified Unix time at which the key will expire, in milliseconds.

  • nx() - Only set the key if it does not already exist.

  • xx() - Only set the key if it already exists.

  • keepttl() - Retain the time to live associated with the key.

Store binary data

Redis strings 可用于存储二进制数据,例如图像。在这种情况下,我们将 byte[] 用作值类型:

Redis strings can be used to store binary data, such as images. In this case, we will use byte[] as value type:

@ApplicationScoped
public static class MyBinaryRepository {

    private final ValueCommands<String, byte[]> commands;

    public MyBinaryRepository(RedisDataSource ds) {
        commands = ds.value(byte[].class);
    }

    public byte[] get(String key) {
        byte[] bytes = commands.get(key);
        if (bytes == null) {
            throw new NoSuchElementException("`" + key + "` not found");
        }
        return bytes;
    }

    public void add(String key, byte[] bytes) {
        commands.set(key, bytes);
    }

    public void addIfAbsent(String key, byte[] bytes) {
        commands.setnx(key, bytes);
    }
}

Store a counter

您可以在 Redis 中存储计数器,如下所示:

You can store counters in Redis as demonstrated below:

@ApplicationScoped
public static class MyRedisCounter {

    private final ValueCommands<String, Long> commands;

    public MyRedisCounter(RedisDataSource ds) {
        commands = ds.value(Long.class); (1)
    }

    public long get(String key) {
        Long l = commands.get(key);  (2)
        if (l == null) {
            return 0L;
        }
        return l;
    }

    public void incr(String key) {
        commands.incr(key);  (3)
    }

}
1 Retrieve the commands. This time we will manipulate Long values
2 Retrieve the counter associated with the given key. Return 0L when no counter is stored.
3 Increment the value. If there are no counter stored for the key, the incr command considers 0 as value (so incr sets the value to 1).

还有其他方法可用于处理计数器,例如:

There are other methods that can be useful to manipulate counters, such as:

  • incrby - allows setting the increment value (positive or negative)

  • incrbyfloat - allows setting the increment value as a float/ double (the stored value will be a double)

  • set - to set an initial value if needed

  • decr and decrby - allows decrementing the stored value

Communicate with pub/sub

Redis 允许向频道发送 messages 并监听这些消息。这些功能可在 pubsub 组中获得。

Redis allows sending messages to channels and listening for these messages. These features are available from the pubsub group.

以下代码段显示了 cache 如何在每次 set 后发出 Notification,以及订阅者如何接收通知。

The following snippets shows how a cache can emit a Notification after every set, and how a subscriber can receive the notification.

public static final class Notification {
    public String key;
    public BusinessObject bo;

    public Notification() {

    }

    public Notification(String key, BusinessObject bo) {
        this.key = key;
        this.bo = bo;
    }
}

@ApplicationScoped
@Startup // We want to create the bean instance on startup to subscribe to the channel.
public static class MySubscriber implements Consumer<Notification> {
    private final PubSubCommands<Notification> pub;
    private final PubSubCommands.RedisSubscriber subscriber;

    public MySubscriber(RedisDataSource ds) {
        pub = ds.pubsub(Notification.class);
        subscriber = pub.subscribe("notifications", this);
    }

    @Override
    public void accept(Notification notification) {
        // Receive the notification
    }

    @PreDestroy
    public void terminate() {
        subscriber.unsubscribe(); // Unsubscribe from all subscribed channels
    }
}

@ApplicationScoped
public static class MyCache {

    private final ValueCommands<String, BusinessObject> commands;
    private final PubSubCommands<Notification> pub;

    public MyCache(RedisDataSource ds) {
        commands = ds.value(BusinessObject.class);
        pub = ds.pubsub(Notification.class);
    }

    public BusinessObject get(String key) {
        return commands.get(key);
    }

    public void set(String key, BusinessObject bo) {
        commands.set(key, bo);
        pub.publish("notifications", new Notification(key, bo));
    }
}

Use Redis transactions

Redis 事务与关系数据库事务略有不同。Redis 事务是一批同时执行的命令。

Redis transactions are slightly different from relational database transactions. Redis transactions are a batch of commands executed altogether.

Redis 事务可以 watch 一组键,如果在事务执行期间更新了这些键中的一个,则 discard 事务。

A Redis transaction can watch a set of keys, which would discard the transaction is one of these keys are updated during the transaction execution.

在事务中排队的命令在整个事务执行之前不会执行。这意味着您无法在事务期间检索结果。结果累积在一个 TransactionResult 对象中,您将在事务完成后访问该对象。此对象包含事务是否成功或被丢弃,在前一种情况下还包含每条命令的结果(按命令顺序编制索引)。

Commands enqueued in a transaction are not executed before the whole transaction is executed. It means that you cannot retrieve a result during the transaction. Results are accumulated in a TransactionResult object you will access after the completion of the transaction. This object contains whether the transaction succeeded or was discarded, and in the former case the result of each command (indexed by the command order).

要启动事务,您使用 withTransaction 方法。此方法接收一个 Consumer<TransactionalRedisDataSource>,该 Consumer<TransactionalRedisDataSource> 遵循与常规 RedisDataSource 相同的 API,不同之处在于这些命令返回 void(对于反应式变体,返回 Uni<Void>)。当消费者返回时,事务 executed

To start a transaction, you use the withTransaction method. This method receives a Consumer<TransactionalRedisDataSource>, which follows the same API as the regular RedisDataSource except that the commands return void (Uni<Void> for the reactive variant). When that consumer returns, the transaction is executed.

以下代码段展示了如何创建执行两个相关_writes_的事务:

The following snippet shows how to create a transaction executing two related writes:

@Inject RedisDataSource ds;

// ...

TransactionResult result = ds.withTransaction(tx -> {
        TransactionalHashCommands<String, String, String> hash = tx.hash(String.class);
        hash.hset(KEY, "field-1", "hello");
        hash.hset(KEY, "field-2", "hello");
    });

接收到的`tx`对象也可以用于_discard_事务,方法为:tx.discard();。返回的`TransactionResult`让你检索每个命令的结果。

The received tx object can also be used to discard the transaction, using: tx.discard();. The returned TransactionResult lets you retrieve the result of each command.

当使用数据源的响应型变量时,传递的回调是一个`Function<ReactiveTransactionalRedisDataSource, Uni<Void>>`:

When using the reactive variant of the data source, the passed callback is a Function<ReactiveTransactionalRedisDataSource, Uni<Void>>:

@Inject ReactiveRedisDataSource ds;

// ...

Uni<TransactionResult> result = ds.withTransaction(tx -> {
        ReactiveTransactionalHashCommands<String, String, String> hash = tx.hash(String.class);
        return hash.hset(KEY, "field-1", "hello")
            .chain(() -> hash.hset(KEY, "field-2", "hello"));
});

事务执行可能受到_keys_的制约。当一个已传递的键在事务执行期间被修改时,该事务会被舍弃。这些键传递为`String`,作为`withTransaction`方法的第二个参数:

Transaction execution can be conditioned by keys. When a passed key gets modified during the execution of a transaction, the transaction is discarded. The keys are passed as String as a second parameter to the withTransaction method:

TransactionResult result = ds.withTransaction(tx -> {
    TransactionalHashCommands<String, String, String> hash = tx.hash(String.class);
    hash.hset(KEY, "field-1", "hello");
    hash.hset(KEY, "field-2", "hello");
}, KEY);

你不能在事务中使用发布/订阅功能。

You cannot use the pub/sub feature from within a transaction.

Implement the optimistic locking pattern

要使用乐观锁,你需要使用`withTransaction`方法的变种,允许在事务开始前执行代码。换句话说,它将如下执行:

To use optimistic locking, you need to use a variant of the withTransaction method, allowing the execution of code before the transaction starts. In other words, it will be executed as follows:

WATCH key

// Pre-transaction block
// ....
// Produce a result

MULTI
  // In transaction code, receive the result produced by the pre-transaction block.
EXEC

例如,如果你需要仅在字段存在时更新哈希表中的一个值,你将使用以下 API:

For example, if you need to update a value in a hash only if the field exists, you will use the following API:

OptimisticLockingTransactionResult<Boolean> result = blocking.withTransaction(ds -> {
    // The pre-transaction block:
    HashCommands<String, String, String> hashCommands = ds.hash(String.class);
    return hashCommands.hexists(key, "field"); // Produce a result (boolean in this case)
},
 (exists, tx) -> { // The transactional block, receives the result and the transactional data source
        if (exists) {
            tx.hash(String.class).hset(key, "field", "new value");
        } else {
            tx.discard();
        }
 },
  key); // The watched key

如果在事务前或事务块执行期间触及了一个监视的键,那么将中止该事务。事务前块产生一个结果,事务块可以使用这个结果。这个构造是必要的,因为在事务中,这些命令不会产生结果。结果只能在执行事务后检索。

If one of the watched keys is touched before or during the execution of the pre-transaction or transactional blocks, the transaction is aborted. The pre-transactional block produces a result that the transactional block can use. This construct is necessary because, within a transaction, the commands do not produce a result. Results can only be retrieved after the execution of the transaction.

事务前和事务块在同一 Redis 连接中调用。因此,事务前块必须使用已传递的数据源来执行命令。因此,命令从该连接发出。这些命令不得修改监视的键。

The pre-transaction and transactional blocks are invoked on the same Redis connection. Consequently, the pre-transaction block must use the passed data source to execute commands. Thus, the commands are emitted from that connection. These commands must not modify the watched keys.

如果事务前块抛出一个异常(或者在使用响应型 API 时产生失败),那么该事务将中止。

The transaction is aborted if the pre-transaction block throws an exception (or produces a failure when using the reactive API).

Execute custom commands

要执行自定义命令,或者 API 不支持的命令,请使用以下方法:

To execute a custom command, or a command not supported by the API, use the following approach:

@Inject ReactiveRedisDataSource ds;

// ...

Response response = ds.execute("my-command", param1, param2, param3);

execute`方法将命令发送给 Redis 并检索`Response。命令名称以第一个参数的形式传递。你可以在你的命令中添加任意数量的*String*参数。结果将封装到一个`Response`对象中。

The execute method sends the command to Redis and retrieves the Response. The command name is passed as first parameters. You can add an arbitrary number of String parameters to your command. The result is wrapped into a Response object.

响应型变量将返回一个`Uni<Response>`。

The reactive variant returns a Uni<Response>.

你也可以在一个事务中执行自定义命令。

You can also execute custom command in a transaction.

Preload data into Redis

在启动时,你可以配置 Redis 客户端将数据预加载到 Redis 数据库中。

On startup, you can configure the Redis client to preload data into the Redis database.

Configure the load scripts

使用以下命令指定要加载的_load script_:

Specify the load script you want to load using:

quarkus.redis.load-script=import.redis # import.redis is the default in dev mode, no-file is the default in production mode
quarkus.redis.my-redis.load-script=actors.redis, movies.redis

`load-script`是一个编译时属性,在运行时不能被覆盖。

load-script is a build time property than cannot be overridden at runtime.

请注意,每个客户端可能具有不同的脚本,甚至是脚本列表。如果是列表,数据会以列表顺序导入(例如,首先是`actors.redis`,然后是`my-redis`客户端的`movies.redis`)。

Note that each client can have a different script, even a list of scripts. In the case of a list, the data is imported in the list order (for example, first actors.redis, then movies.redis for the my-redis client).

Write load scripts

`.redis`文件遵循_one command per line_格式:

The .redis file follows a one command per line format:

# Line starting with # and -- are ignored, as well as empty lines

-- One command per line:
HSET foo field1 abc field2 123

-- Parameters with spaces must be wrapped into single or double quotes
HSET bar field1 "abc def" field2 '123 456 '

-- Parameters with double quotes must be wrapped into single quotes and the opposite
SET key1 'A value using "double-quotes"'
SET key2 "A value using 'single-quotes'"

Quarkus 会将所有命令从单个文件批处理,然后发送所有命令。如果有任何错误,加载过程将会失败,但是之前的指令也许已经执行过了。为避免这种情况,你可以将命令封装在 Redis transaction 中:

Quarkus batches all the commands from a single file and sends all the commands. The loading process fails if there is any error, but the previous instructions may have been executed. To avoid that, you can wrap your command into a Redis transaction:

-- Run inside a transaction
MULTI
SET key value
SET space:key 'another value'
INCR counter
EXEC

Configure the pre-loading

数据会在应用启动时加载。默认情况下,它会在导入前丢弃整个数据库。你可以使用 quarkus.redis.flush-before-load=false 来阻止这种情况发生。

The data is loaded when the application starts. By default, it drops the whole database before importing. You can prevent this using quarkus.redis.flush-before-load=false.

另请注意,只有在数据库为空(没有键)时才会运行导入过程。你可以使用 quarkus.redis.load-only-if-empty=false 来强制导入,即使存在数据。

Also, the import process only runs if the database is empty (no key). You can force to import even if there is data using the quarkus.redis.load-only-if-empty=false

Distinguish dev/test vs. prod when pre-loading

如上所述,在开发和测试模式中,Quarkus 会尝试通过寻找 src/main/resources/import.redis 来导入数据。此行为在 prod 模式中处于禁用状态,如果你想导入即使是在生产中,添加:

As mentioned above, in dev and test modes, Quarkus tries to import data by looking for the src/main/resources/import.redis. This behavior is disabled in prod mode, and if you want to import even in production, add:

%prod.quarkus.redis.load-script=import.redis

prod 模式中导入之前,确保你相应地配置了 quarkus.redis.flush-before-load

Before importing in prod mode, make sure you configured quarkus.redis.flush-before-load accordingly.

在开发模式中,为了重新加载 .redis 加载脚本的内容,你需要添加: %dev.quarkus.vertx.caching=false

In dev mode, to reload the content of the .redis load scripts, you need to add: %dev.quarkus.vertx.caching=false

Use the Vert.x redis client

除了高级 API 之外,你还可以直接在代码中使用 Vertx Redis 客户端。Vert.x Redis Client 的文档可在 Vert.x Web Site 中找到。

In addition to the high-level API, you can use the Vertx Redis clients directly in your code. The documentation of the Vert.x Redis Client is available on the Vert.x Web Site.

Configure Redis hosts programmatically

RedisHostsProvider 以编程的方式提供 redis 主机。这允许从其他来源配置诸如 redis 连接密码之类的属性。

The RedisHostsProvider programmatically provides redis hosts. This allows for configuration of properties like redis connection password coming from other sources.

这很有用,因为它消除了在 application.properties 中存储敏感数据的需求。

This is useful as it removes the need to store sensitive data in application.properties.

@ApplicationScoped
@Identifier("hosts-provider") // the name of the host provider
public class ExampleRedisHostProvider implements RedisHostsProvider {
    @Override
    public Set<URI> getHosts() {
        // do stuff to get the host
        String host = "redis://localhost:6379/3";
        return Collections.singleton(URI.create(host));
    }
}

主机提供程序可用于配置 redis 客户端,如下所示:

The host provider can be used to configure the redis client like shown below

quarkus.redis.hosts-provider-name=hosts-provider

Customize the Redis options programmatically

你可以公开一个实现了 io.quarkus.redis.client.RedisOptionsCustomizer 接口的 bean,以自定义 Redis 客户端选项。该 bean 会对每个已配置的 Redis 客户端进行调用:

You can expose a bean implementing the io.quarkus.redis.client.RedisOptionsCustomizer interface to customize the Redis client options. The bean is called for each configured Redis client:

@ApplicationScoped
public static class MyExampleCustomizer implements RedisOptionsCustomizer {

    @Override
    public void customize(String clientName, RedisOptions options) {
        if (clientName.equalsIgnoreCase("my-redis")
                || clientName.equalsIgnoreCase(RedisConfig.DEFAULT_CLIENT_NAME)) {
            // modify the given options
        } else {
            throw new IllegalStateException("Unknown client name: " + clientName);
        }
    }
}

Use the Redis Dev Services

参见 Redis Dev Service

Configure Redis observability

Enable the health checks

如果你使用 quarkus-smallrye-health 扩展,quarkus-redis 会自动添加一个准备就绪运行状况检查以验证与 Redis 服务器的连接。

If you are using the quarkus-smallrye-health extension, quarkus-redis will automatically add a readiness health check to validate the connection to the Redis server.

因此,当您访问应用程序的 /q/health/ready 端点时,您将获得有关连接验证状态的信息。

So when you access the /q/health/ready endpoint of your application you will have information about the connection validation status.

此行为可通过在 application.properties 中将 quarkus.redis.health.enabled 属性设为 false 来禁用。

This behavior can be disabled by setting the quarkus.redis.health.enabled property to false in your application.properties.

Enable metrics

当应用程序也使用 quarkus-micrometer 扩展时,Redis 客户端指标会自动启用。Micrometer 会收集应用程序实现的所有 Redis 客户端的指标。

Redis client metrics are automatically enabled when the application also uses the quarkus-micrometer extension. Micrometer collects the metrics of all the Redis clients implemented by the application.

比如,如果你将指标导出到 Prometheus,你将得到:

As an example, if you export the metrics to Prometheus, you will get:

# HELP redis_commands_duration_seconds The duration of the operations (commands of batches
# TYPE redis_commands_duration_seconds summary
redis_commands_duration_seconds_count{client_name="<default>",} 3.0
redis_commands_duration_seconds_sum{client_name="<default>",} 0.047500042
# HELP redis_commands_duration_seconds_max The duration of the operations (commands of batches
# TYPE redis_commands_duration_seconds_max gauge
redis_commands_duration_seconds_max{client_name="<default>",} 0.033273167
# HELP redis_pool_active The number of resources from the pool currently used
# TYPE redis_pool_active gauge
redis_pool_active{pool_name="<default>",pool_type="redis",} 0.0
# HELP redis_pool_ratio Pool usage ratio
# TYPE redis_pool_ratio gauge
redis_pool_ratio{pool_name="<default>",pool_type="redis",} 0.0
# HELP redis_pool_queue_size Number of pending elements in the waiting queue
# TYPE redis_pool_queue_size gauge
redis_pool_queue_size{pool_name="<default>",pool_type="redis",} 0.0
# HELP redis_commands_failure_total The number of operations (commands or batches) that have been failed
# TYPE redis_commands_failure_total counter
redis_commands_failure_total{client_name="<default>",} 0.0
# HELP redis_commands_success_total The number of operations (commands or batches) that have been executed successfully
# TYPE redis_commands_success_total counter
redis_commands_success_total{client_name="<default>",} 3.0
# HELP redis_pool_idle The number of resources from the pool currently used
# TYPE redis_pool_idle gauge
redis_pool_idle{pool_name="<default>",pool_type="redis",} 6.0
# HELP redis_pool_completed_total Number of times resources from the pool have been acquired
# TYPE redis_pool_completed_total counter
redis_pool_completed_total{pool_name="<default>",pool_type="redis",} 3.0
# HELP redis_commands_count_total The number of operations (commands or batches) executed
# TYPE redis_commands_count_total counter
redis_commands_count_total{client_name="<default>",} 3.0
# HELP redis_pool_usage_seconds Time spent using resources from the pool
# TYPE redis_pool_usage_seconds summary
redis_pool_usage_seconds_count{pool_name="<default>",pool_type="redis",} 3.0
redis_pool_usage_seconds_sum{pool_name="<default>",pool_type="redis",} 0.024381375
# HELP redis_pool_usage_seconds_max Time spent using resources from the pool
# TYPE redis_pool_usage_seconds_max gauge
redis_pool_usage_seconds_max{pool_name="<default>",pool_type="redis",} 0.010671542
# HELP redis_pool_queue_delay_seconds Time spent in the waiting queue before being processed
# TYPE redis_pool_queue_delay_seconds summary
redis_pool_queue_delay_seconds_count{pool_name="<default>",pool_type="redis",} 3.0
redis_pool_queue_delay_seconds_sum{pool_name="<default>",pool_type="redis",} 0.022341249
# HELP redis_pool_queue_delay_seconds_max Time spent in the waiting queue before being processed
# TYPE redis_pool_queue_delay_seconds_max gauge
redis_pool_queue_delay_seconds_max{pool_name="<default>",pool_type="redis",} 0.021926083

Redis 客户端名称可在 tags 中找到。

The Redis client name can be found in the tags.

该指标包含 Redis 连接池指标 (redis_pool_*) 和关于命令执行的指标 (redis_commands_*),例如命令数、成功、失败和持续时间。

The metrics contain both the Redis connection pool metrics (redis_pool_*) and the metrics about the command execution (redis_commands_*) such as the number of command, successes, failures, and durations.

Disable metrics

要停用 Redis 客户端指标(在使用 quarkus-micrometer 的时候),可以将以下属性添加到应用程序配置中:

To disable the Redis client metrics when quarkus-micrometer is used, add the following property to the application configuration:

quarkus.micrometer.binder.redis.enabled=false

Configuration reference

Unresolved directive in redis-reference.adoc - include::{generated-dir}/config/quarkus-redis-client.adoc[]