编辑此页面

使用 Redis 客户端

本指南演示了 Quarkus 应用程序如何使用 Redis Client 扩展连接到 Redis 服务器。

此技术被认为是稳定的。

由于是稳定的,因此非常重视向后兼容性和在生态系统中的存在性。

有关可能的完整状态列表,请查看我们的常见问题解答条目

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

  • 一个有效的 Docker 环境

架构

在本指南中,我们将公开一个简单的 Rest API,使用 INCRBY 命令来增加数字。在此过程中,我们将了解如何使用其他 Redis 命令,例如 GETSET(来自字符串组)、DELKEYS(来自键组)。

我们将使用 Quarkus Redis 扩展来连接和与 Redis 交互。

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 存储库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载一个存档

解决方案位于 redis-quickstart 目录中。

创建 Maven 项目

首先,我们需要一个新项目。使用以下命令创建一个新项目

CLI
quarkus create app org.acme:redis-quickstart \
    --extension='redis-client,rest-jackson' \
    --no-code
cd redis-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=redis-quickstart \
    -Dextensions='redis-client,rest-jackson' \
    -DnoCode
cd redis-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数括在双引号中,例如 "-DprojectArtifactId=redis-quickstart"

此命令会生成一个新项目,导入 Redis 扩展。

如果您已经配置了 Quarkus 项目,可以通过在项目根目录中运行以下命令将 redis-client 扩展添加到您的项目中

CLI
quarkus extension add redis-client
Maven
./mvnw quarkus:add-extension -Dextensions='redis-client'
Gradle
./gradlew addExtension --extensions='redis-client'

这会将以下内容添加到您的构建文件中

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

创建 Increment POJO

我们将使用 Increment POJO 来模拟我们的增量。创建 src/main/java/org/acme/redis/Increment.java 文件,内容如下

package org.acme.redis;

public class Increment {
    public String key; (1)
    public long value; (2)

    public Increment(String key, long value) {
        this.key = key;
        this.value = value;
    }

    public Increment() {
    }
}
1 将用作 Redis 键的键
2 Redis 键持有的值

创建 Increment Service

我们将创建一个 IncrementService 类,它将扮演 Redis 客户端的角色。通过此类,我们将能够执行 SETGETDELKEYSINCRBY Redis 命令。

创建 src/main/java/org/acme/redis/IncrementService.java 文件,内容如下

package org.acme.redis;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import io.quarkus.redis.datasource.ReactiveRedisDataSource;
import io.quarkus.redis.datasource.RedisDataSource;
import io.quarkus.redis.datasource.keys.KeyCommands;
import io.quarkus.redis.datasource.keys.ReactiveKeyCommands;
import io.quarkus.redis.datasource.string.StringCommands;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class IncrementService {

    // This quickstart demonstrates both the imperative
    // and reactive Redis data sources
    // Regular applications will pick one of them.

    private ReactiveKeyCommands<String> keyCommands; (1)
    private ValueCommands<String, Long> countCommands; (2)

    public IncrementService(RedisDataSource ds, ReactiveRedisDataSource reactive) { (3)
        countCommands = ds.value(Long.class); (4)
        keyCommands = reactive.key();  (5)

    }


    long get(String key) {
        Long value = countCommands.get(key); (6)
        if (value == null) {
            return 0L;
        }
        return value;
    }

    void set(String key, Long value) {
        countCommands.set(key, value); (7)
    }

    void increment(String key, Long incrementBy) {
        countCommands.incrby(key, incrementBy); (8)
    }

    Uni<Void> del(String key) {
        return keyCommands.del(key) (9)
            .replaceWithVoid();
    }

    Uni<List<String>> keys() {
        return keyCommands.keys("*"); (10)
    }
}
1 用于操作键的字段
2 用于操作计数器的字段
3 注入命令式和响应式数据源
4 检索用于操作计数器的命令
5 检索用于操作键的命令
6 检索与给定键关联的值。如果为 null,则返回 0。
7 设置与给定键关联的值
8 递增与给定键关联的值
9 删除一个键(及其关联的值)
10 列出所有键

创建 Increment Resource

创建 src/main/java/org/acme/redis/IncrementResource.java 文件,内容如下

package org.acme.redis;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import java.util.List;

import io.smallrye.mutiny.Uni;

@Path("/increments")
public class IncrementResource {

    @Inject
    IncrementService service;

    @GET
    public Uni<List<String>> keys() {
        return service.keys();
    }

    @POST
    public Increment create(Increment increment) {
        service.set(increment.key, increment.value);
        return increment;
    }

    @GET
    @Path("/{key}")
    public Increment get(String key) {
        return new Increment(key, service.get(key));
    }

    @PUT
    @Path("/{key}")
    public void increment(String key, long value) {
        service.increment(key, value);
    }

    @DELETE
    @Path("/{key}")
    public Uni<Void> delete(String key) {
        return service.del(key);
    }
}

创建测试类

编辑 pom.xml 文件以添加以下依赖项

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <scope>test</scope>
</dependency>

创建 src/test/java/org/acme/redis/IncrementResourceTest.java 文件,内容如下

package org.acme.redis;

import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

import static io.restassured.RestAssured.given;

import io.restassured.http.ContentType;

@QuarkusTest
public class IncrementResourceTest {

    @Test
    public void testRedisOperations() {
        // verify that we have nothing
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments")
                .then()
                .statusCode(200)
                .body("size()", is(0));

        // create a first increment key with an initial value of 0
        given()
                .contentType(ContentType.JSON)
                .accept(ContentType.JSON)
                .body("{\"key\":\"first-key\",\"value\":0}")
                .when()
                .post("/increments")
                .then()
                .statusCode(200)
                .body("key", is("first-key"))
                .body("value", is(0));

        // create a second increment key with an initial value of 10
        given()
                .contentType(ContentType.JSON)
                .accept(ContentType.JSON)
                .body("{\"key\":\"second-key\",\"value\":10}")
                .when()
                .post("/increments")
                .then()
                .statusCode(200)
                .body("key", is("second-key"))
                .body("value", is(10));

        // increment first key by 1
        given()
                .contentType(ContentType.JSON)
                .body("1")
                .when()
                .put("/increments/first-key")
                .then()
                .statusCode(204);

        // verify that key has been incremented
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments/first-key")
                .then()
                .statusCode(200)
                .body("key", is("first-key"))
                .body("value", is(1));

        // increment second key by 1000
        given()
                .contentType(ContentType.JSON)
                .body("1000")
                .when()
                .put("/increments/second-key")
                .then()
                .statusCode(204);

        // verify that key has been incremented
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments/second-key")
                .then()
                .statusCode(200)
                .body("key", is("second-key"))
                .body("value", is(1010));

        // verify that we have two keys in registered
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments")
                .then()
                .statusCode(200)
                .body("size()", is(2));

        // delete first key
        given()
                .accept(ContentType.JSON)
                .when()
                .delete("/increments/first-key")
                .then()
                .statusCode(204);

        // verify that we have one key left after deletion
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments")
                .then()
                .statusCode(200)
                .body("size()", is(1));

        // delete second key
        given()
                .accept(ContentType.JSON)
                .when()
                .delete("/increments/second-key")
                .then()
                .statusCode(204);

        // verify that there is no key left
        given()
                .accept(ContentType.JSON)
                .when()
                .get("/increments")
                .then()
                .statusCode(200)
                .body("size()", is(0));
    }
}

开始运行

如果您按照说明操作,应该已经运行了 Redis 服务器。然后,只需使用以下命令运行应用程序

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

打开另一个终端并运行 curl https://:8080/increments 命令。

与应用程序交互

正如我们上面所见,API 公开了五个 Rest 端点。在本节中,我们将看到如何初始化一个增量、查看当前增量的列表、根据键增量一个值、检索增量的当前值,以及最后删除一个键。

创建一个新的增量

curl -X POST -H "Content-Type: application/json" -d '{"key":"first","value":10}' https://:8080/increments (1)
1 我们创建第一个增量,键为 first,初始值为 10

运行上述命令应返回以下结果

{
  "key": "first",
  "value": 10
}

查看当前的增量键

要查看当前增量键的列表,请运行以下命令

curl https://:8080/increments

上述命令应返回 ["first"],表明到目前为止我们只有一个增量。

检索新的增量

要使用键检索增量,我们需要运行以下命令

curl https://:8080/increments/first (1)
1 运行此命令应返回以下结果
{
  "key": "first",
  "value": 10
}

根据键递增值

要递增值,请运行以下命令

curl -X PUT -H "Content-Type: application/json" -d '27' https://:8080/increments/first (1)
1 first 的值增加 27。

现在,运行命令 curl https://:8080/increments/first 应返回以下结果

{
  "key": "first",
  "value": 37 (1)
}
1 我们看到 first 键的值现在是 37,这正好是 10 + 27 的结果,快速计算。

删除键

使用以下命令,根据键删除增量。

curl -X DELETE  https://:8080/increments/first (1)
1 删除 first 增量。

现在,运行命令 curl https://:8080/increments 应返回一个空列表 []

为生产配置

此时,Quarkus 使用 Redis Dev Service 来运行 Redis 服务器并配置应用程序。但是,在生产环境中,您将运行自己的 Redis(或使用云服务)。

让我们使用以下命令在端口 6379 上启动一个 Redis 服务器

docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name redis_quarkus_test -p 6379:6379 redis:5.0.6

然后,打开 src/main/resources/application.properties 文件并添加

%prod.quarkus.redis.hosts=redis://:6379

打包并在 JVM 模式下运行

您可以将应用程序作为常规 jar 文件运行。

首先,我们需要打包它

CLI
quarkus build
Maven
./mvnw install
Gradle
./gradlew build
此命令将启动一个 Redis 实例以执行测试。

然后运行它

java -jar target/quarkus-app/quarkus-run.jar

原生运行

您也可以从此应用程序创建本地可执行文件,而无需进行任何源代码更改。本地可执行文件消除了对 JVM 的依赖:运行应用程序所需的一切都包含在可执行文件中,从而使应用程序以最少的资源开销运行。

编译本地可执行文件需要更长的时间,因为 GraalVM 执行了额外的步骤来删除不必要的代码路径。使用 native 配置文件来编译本地可执行文件

CLI
quarkus build --native
Maven
./mvnw install -Dnative
Gradle
./gradlew build -Dquarkus.native.enabled=true

构建完成后,您可以使用以下命令运行可执行文件

./target/redis-quickstart-1.0.0-SNAPSHOT-runner

更进一步

要了解有关 Quarkus Redis 扩展的更多信息,请参阅Redis 扩展参考指南

相关内容