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

使用Redis客户端

本指南演示了如何将你的Quarkus应用通过Redis客户端扩展连接到Redis服务器。

这项技术被认为是preview。

preview(预览) 中,不保证向后兼容和在生态系统中的存在。具体的改进可能需要改变配置或API,并且正在计划变得 稳定 。欢迎在我们的 邮件列表 中提供反馈,或在我们的 GitHub问题列表 中提出问题。

For a full list of possible statuses, check our FAQ entry.

准备工作

要完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • 安装JDK 11以上版本并正确配置了 JAVA_HOME

  • Apache Maven 3.8.1+

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

  • A working Docker environment

架构

In this guide, we are going to expose a simple Rest API to increment numbers by using the INCRBY command. Along the way, we’ll see how to use other Redis commands like GET, SET (from the string group), DEL and KEYS (from the key group).

We’ll be using the Quarkus Redis extension to connect to interact with 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,resteasy-reactive-jackson \
    --no-code
cd redis-quickstart

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

关于如何安装并使用Quarkus CLI的更多信息,请参考Quarkus CLI指南

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.2.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=redis-quickstart \
    -Dextensions="redis-client,resteasy-reactive-jackson" \
    -DnoCode
cd redis-quickstart

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

该命令可生成一个新项目,并导入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的Key
2 Redis的Key所持有的值

创建Increment服务

We are going to create an IncrementService class which will play the role of a Redis client. With this class, we’ll be able to perform the SET, GET , DEL, KEYS and INCRBY Redis commands.

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

package org.acme.redis;

import javax.enterprise.context.ApplicationScoped;
import javax.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 StringCommands<String, Long> countCommands; (2)

    public IncrementService(RedisDataSource ds, ReactiveRedisDataSource reactive) { (3)
        countCommands = ds.string(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 The field use to manipulate keys
2 The field use to manipulate the counter
3 Inject both the imperative and reactive data sources
4 Retrieve the commands to manipulate the counters
5 Retrieve the commands to manipulate the keys
6 Retrieve the value associated with the given key. It null, returns 0.
7 Set the value associated with the given key
8 Increment the value associated with the given key
9 Delete a key (and its associated value)
10 List all the keys

创建Increment资源

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

package org.acme.redis;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.POST;
import javax.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);
    }
}

创建测试类

Edit the pom.xml file to add the following dependency:

<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 http://localhost:8080/increments 命令。

与应用程序进行交互

从上文可以看到,该API服务暴露了五个Restful接口。在这一节中,我们将看到如何初始化一个增量,查看当前的增量列表,给指定键的值递增,查询一个增量的当前值,以及删除一个键。

创建一个新的增量

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

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

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

查看当前增量键的列表

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

curl http://localhost:8080/increments

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

查询增量

要使用指定的键查询增量,我们须运行以下命令:

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

递增指定键的值

要增加键的值,请运行以下命令:

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

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

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

删除指定的键

使用下面的命令,删除一个键的增量:

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

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

Configuring for production

At this point, Quarkus uses the Redis Dev Service to run a Redis server and configure the application. However, in production, you will run your own Redis (or used a Cloud offering).

Let’s start a Redis server on the port 6379 using:

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

Then, open the src/main/resources/application.properties file and add:

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

打包,并在JVM模式下运行

你可以将应用打包为一个传统的jar文件,然后运行。

首先,需要打包:

CLI
quarkus build
Maven
./mvnw clean package
Gradle
./gradlew build
This command will start a Redis instance to execute the tests.

然后运行:

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

以本地(native)模式运行

无需对源代码做任何修改,就可以将应用程序构建为一个本地可执行文件。本地可执行文件消除了对JVM的依赖,它包含了在目标平台上运行应用程序所需的一切,使应用程序能够以最小的资源开销运行。

编译一个本地可执行文件需要很长的时间,因为GraalVM会执行额外的步骤来删除不必要的代码路径。可使用 native profile来编译一个本地可执行文件:

CLI
quarkus build --native
Maven
./mvnw package -Dnative
Gradle
./gradlew build -Dquarkus.package.type=native

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

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

进一步探索

To learn more about the Quarkus Redis extension, check the redis extension reference guide.