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

Using the Redis Client

このガイドでは、QuarkusアプリケーションがRedisクライアントエクステンションを使用してRedisサーバーに接続する方法を説明します。

この技術は、previewと考えられています。

preview では、下位互換性やエコシステムでの存在は保証されていません。具体的な改善には設定や API の変更が必要になるかもしれませんが、 stable になるための計画は現在進行中です。フィードバックは メーリングリストGitHub の課題管理 で受け付けています。

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

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • 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

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストール方法については、Quarkus CLIガイドをご参照ください。

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

Gradleプロジェクトを作成するには、 -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")

インクリメント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キーが保持する値

インクリメントサービスの作成

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

インクリメントリソースの作成

以下の内容の 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は5つのRestエンドポイントを公開しています。このセクションでは、インクリメントを初期化する方法、現在のインクリメントのリストを見る方法、キーを指定して値をインクリメントする方法、インクリメントの現在の値を取得する方法、そして最後にキーを削除する方法を見ていきます。

新しいインクリメントの作成

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"] を返し、これまでのところインクリメントが 1 つしかないことを示しているはずです。

新しいインクリメントの取得

キーを使ってインクリメントを取得するには、以下のコマンドを実行する必要があります。

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

ネイティブの実行

ソースコードを変更することなく、このアプリケーションからネイティブ実行可能ファイルを作成することもできます。ネイティブ実行可能ファイルは、JVMへの依存を取り除きます。ターゲットプラットフォーム上でアプリケーションを実行するために必要なすべてのものが実行ファイルに含まれているため、アプリケーションを最小限のリソースオーバーヘッドで実行することができます。

GraalVMは、不要なコードパスを削除するために追加のステップを実行するため、ネイティブ実行可能ファイルのコンパイルには少し時間がかかります。 native プロファイルを使用して、ネイティブ実行可能ファイルをコンパイルしてください。

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.