The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Redis Extension Reference Guide

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.

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.

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

1. 安装

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.quarkiverse.redis</groupId>
    <artifactId>quarkus-redis</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-redis")

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;

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

2. One extension, multiple APIs

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.

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.RedisReactiveDataSource.

  • 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.

3. Default and named clients

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.

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

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

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

@ApplicationScoped
public class RedisExample {
    @Inject RedisReactiveDataSource reactiveDataSource;
    @Inject RedisDataSource redisDataSource;
    @Inject RedisAPI redisAPI;
    // ...
}
In general, you inject a single one; the previous snippet is just an example.

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

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

@ApplicationScoped
public class RedisExample {
    @Inject @RedisClientName("my-redis-1") RedisReactiveDataSource reactiveDataSource;
    @Inject @RedisClientName("my-redis-2") RedisDataSource redisDataSource;
    // ...
}
When using @RedisClientName, you can omit the @Inject annotation.

4. Connecting to Redis

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).

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]

4.1. Using Unix Socket

When using unix-socket, you need:

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

4.2. Sentinel Mode

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 my-master
quarkus.redis.role=master # master is the default

4.3. Cluster Mode

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
quarkus.redis.replicas=share

4.4. Replication Mode

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

4.5. Connecting to 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>

4.6. Authentication

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.

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

5. Using the high-level clients (data sources)

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.

5.1. Injecting data sources

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>.

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

@Inject RedisDataSource defaultRedisDataSource;
@Inject ReactiveRedisDataSource defaultReactiveRedisDataSource;

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;

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

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

The default timeout is configured to 10s.

All about delegation

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.

5.1.1. Data Source groups

As mentioned above, the API is divided into groups:

  • bitmap - .bitmap()

  • 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)

  • stream (not available yet)

  • string - .string(valueType)

  • transactions - withTransaction

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) {
        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.

5.2. Serialization and Deserialization

The data source APIs handle the serialization and deserialization automatically. When a non-standard type is used, the object is serialized into JSON and deserialized from JSON. In this case, quarkus-jackson is used.

To store binary data, use byte[].

5.3. The string group

The string 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).

5.3.1. Caching values

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 StringCommands<String, BusinessObject> commands;

    public MyRedisCache(RedisDataSource ds) {
        commands = ds.string(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
    }
}

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

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

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.

5.3.2. Storing binary data

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 StringCommands<String, byte[]> commands;

    public MyBinaryRepository(RedisDataSource ds) {
        commands = ds.string(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);
    }
}

5.3.3. Storing a counter

You can store counters in Redis as demonstrated below:

@ApplicationScoped
public static class MyRedisCounter {

    private final StringCommands<String, Long> commands;

    public MyRedisCounter(RedisDataSource ds) {
        commands = ds.string(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

5.3.4. Using pub/sub

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

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 StringCommands<String, BusinessObject> commands;
    private final PubSubCommands<Notification> pub;

    public MyCache(RedisDataSource ds) {
        commands = ds.string(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));
    }
}

5.3.5. Using transactions

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

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.

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).

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.

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");
    });

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.

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"));
});

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.

5.3.6. Executing custom commands

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);

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.

The reactive variant returns a Uni<Response>.

You can also execute custom command in a transaction.

6. Vert.x Redis Client

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.

7. Redis Health Check

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 端点时,可获得有关于连接验证状态的信息。

可以通过在 application.properties 中设置 quarkus.redis.health.enabled 属性为 false 来禁用这个连接健康检查。

8. 以编程方式设定Redis主机

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

这样可以避免在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));
    }
}

RedisHostsProvider 可用于配置Redis客户端,如下图所示:

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

8.1. 开发服务

9. 配置参考

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认

Whether a health check is published in case the smallrye-health extension is present.

Environment variable: QUARKUS_REDIS_HEALTH_ENABLED

boolean

true

If DevServices has been explicitly enabled or disabled. DevServices is generally enabled by default, unless there is an existing configuration present. When DevServices is enabled Quarkus will attempt to automatically configure and start a database when running in Dev or Test mode and when Docker is running.

Environment variable: QUARKUS_REDIS_DEVSERVICES_ENABLED

boolean

true

The container image name to use, for container based DevServices providers.

Environment variable: QUARKUS_REDIS_DEVSERVICES_IMAGE_NAME

string

Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.

Environment variable: QUARKUS_REDIS_DEVSERVICES_PORT

int

Indicates if the Redis server managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. If a matching container is found, it is used, and so a second one is not started. Otherwise, Dev Services for Redis starts a new container. The discovery uses the quarkus-dev-service-redis label. The value is configured using the service-name property. Container sharing is only used in dev mode.

Environment variable: QUARKUS_REDIS_DEVSERVICES_SHARED

boolean

true

The value of the quarkus-dev-service-redis label attached to the started container. This property is used when shared is set to true. In this case, before starting a container, Dev Services for Redis looks for a container with the quarkus-dev-service-redis label set to the configured value. If found, it will use this container instead of starting a new one. Otherwise, it starts a new container with the quarkus-dev-service-redis label set to the specified value. This property is used when you need multiple shared Redis servers.

Environment variable: QUARKUS_REDIS_DEVSERVICES_SERVICE_NAME

string

redis

If DevServices has been explicitly enabled or disabled. DevServices is generally enabled by default, unless there is an existing configuration present. When DevServices is enabled Quarkus will attempt to automatically configure and start a database when running in Dev or Test mode and when Docker is running.

Environment variable: QUARKUS_REDIS__ADDITIONAL_REDIS_CLIENTS__DEVSERVICES_ENABLED

boolean

true

The container image name to use, for container based DevServices providers.

Environment variable: QUARKUS_REDIS__ADDITIONAL_REDIS_CLIENTS__DEVSERVICES_IMAGE_NAME

string

Optional fixed port the dev service will listen to. If not defined, the port will be chosen randomly.

Environment variable: QUARKUS_REDIS__ADDITIONAL_REDIS_CLIENTS__DEVSERVICES_PORT

int

Indicates if the Redis server managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. If a matching container is found, it is used, and so a second one is not started. Otherwise, Dev Services for Redis starts a new container. The discovery uses the quarkus-dev-service-redis label. The value is configured using the service-name property. Container sharing is only used in dev mode.

Environment variable: QUARKUS_REDIS__ADDITIONAL_REDIS_CLIENTS__DEVSERVICES_SHARED

boolean

true

The value of the quarkus-dev-service-redis label attached to the started container. This property is used when shared is set to true. In this case, before starting a container, Dev Services for Redis looks for a container with the quarkus-dev-service-redis label set to the configured value. If found, it will use this container instead of starting a new one. Otherwise, it starts a new container with the quarkus-dev-service-redis label set to the specified value. This property is used when you need multiple shared Redis servers.

Environment variable: QUARKUS_REDIS__ADDITIONAL_REDIS_CLIENTS__DEVSERVICES_SERVICE_NAME

string

redis