Apache Kafkaを使用したQuarkusメッセージング入門
このガイドでは、QuarkusアプリケーションがQuarkus Messagingを利用してApache Kafkaとやり取りする方法を示します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 17+がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.9
-
Docker と Docker Compose、または Podman 、および Docker Compose
-
使用したい場合は、 Quarkus CLI
-
ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること
アーキテクチャ
このガイドでは、Kafka と通信する 2 つのアプリケーションを開発します。最初のアプリケーションは 見積りリクエスト を Kafka に送信し、見積り トピックからの Kafka メッセージを消費します。2 つ目のアプリケーションは 見積りリクエスト を受信し、見積り を送り返します。
1つ目のアプリケーションである プロデューサー は、ユーザーが HTTP エンドポイントを介していくつかの見積をリクエストできるようにします。見積リクエストごとにランダムな識別子が生成されてユーザーに返され、見積リクエストを 保留 としてマークします。同時に、生成されたリクエスト ID は Kafka トピック quote-requests
を介して送信されます。
2 つ目のアプリケーションである processor は、quote-requests
トピックから読み取り、見積にランダムな価格を設定し、quotes
という名前の Kafka トピックに送信します。
最後に、プロデューサー は見積を読み取り、サーバーから送信されたイベントを使用してブラウザーに送信します。したがって、ユーザーには、見積価格が 保留 から受信した価格にリアルタイムで更新されていることがわかります。
ソリューション
次の章で紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。
Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.git
、 アーカイブ をダウンロードします。
ソリューションは kafka-quickstart
ディレクトリ にあります。
Mavenプロジェクトの作成
まず、プロデューサー と プロセッサー の 2 つのプロジェクトを作成する必要があります。
ターミナルで プロデューサー プロジェクトを作成するには、次のコマンドを実行します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=kafka-quickstart-producer"
このコマンドは、プロジェクト構造を作成し、使用する 2 つの Quarkus エクステンションを選択します。
-
Quarkus REST (formerly RESTEasy Reactive) and its Jackson support (to handle JSON) to serve the HTTP endpoint.
-
リアクティブメッセージング用の Kafka コネクター
同じディレクトリーから processor プロジェクトを作成するには、次のコマンドを実行します。
Windowsユーザーの場合:
-
cmdを使用する場合、(バックスラッシュ
\
を使用せず、すべてを同じ行に書かないでください)。 -
Powershellを使用する場合は、
-D
パラメータを二重引用符で囲んでください。例:"-DprojectArtifactId=kafka-quickstart-processor"
その時点で、次の構造になっているはずです。
.
├── kafka-quickstart-processor
│ ├── README.md
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ └── src
│ └── main
│ ├── docker
│ ├── java
│ └── resources
│ └── application.properties
└── kafka-quickstart-producer
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── docker
├── java
└── resources
└── application.properties
お気に入りの IDE で 2 つのプロジェクトを開きます。
Dev Services
開発モードを使用するとき、またはテストのために、Kafka ブローカーを起動する必要はありません。Quarkus が自動的にブローカーを開始します。詳細については、Dev Services for Kafka を参照してください。 |
見積オブジェクト
Quote
クラスは、プロデューサー プロジェクトと プロセッサー プロジェクトの両方で使用されます。簡単にするために、ここではクラスを複製します。どちらのプロジェクトでも、次の内容の src/main/java/org/acme/kafka/model/Quote.java
ファイルを作成します。
package org.acme.kafka.model;
public class Quote {
public String id;
public int price;
/**
* Default constructor required for Jackson serializer
*/
public Quote() { }
public Quote(String id, int price) {
this.id = id;
this.price = price;
}
@Override
public String toString() {
return "Quote{" +
"id='" + id + '\'' +
", price=" + price +
'}';
}
}
Quote
オブジェクトの JSON 表現は、Kafka トピックに送信されるメッセージ、および Web ブラウザーに送信されるサーバー送信イベントで使用されます。
Quarkus には、JSON Kafka メッセージを処理する機能が組み込まれています。次のセクションでは、Jackson のシリアライザー/デシリアライザークラスを作成します。
見積リクエストの送信
プロデューサー プロジェクト内に、src/main/java/org/acme/kafka/producer/QuotesResource.java
ファイルを作成し、次のコンテンツを追加します。
package org.acme.kafka.producer;
import java.util.UUID;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.acme.kafka.model.Quote;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;
@Path("/quotes")
public class QuotesResource {
@Channel("quote-requests")
Emitter<String> quoteRequestEmitter; (1)
/**
* Endpoint to generate a new quote request id and send it to "quote-requests" Kafka topic using the emitter.
*/
@POST
@Path("/request")
@Produces(MediaType.TEXT_PLAIN)
public String createRequest() {
UUID uuid = UUID.randomUUID();
quoteRequestEmitter.send(uuid.toString()); (2)
return uuid.toString(); (3)
}
}
1 | リアクティブメッセージングの Emitter を挿入して、quote-requests チャネルにメッセージを送信します。 |
2 | ポストリクエストで、ランダムな UUID を生成し、エミッターを使用してそれを Kafka トピックに送信します。 |
3 | 同じ UUID をクライアントに返します。 |
quote-requests
チャネルは、クラスパス上の唯一のコネクターであるため、Kafka トピックとして管理されます。特に明記されていない限り、この例のように、Quarkus はチャネル名をトピック名として使用します。したがって、この例では、アプリケーションは quote-requests
トピックに書き込みます。Quarkus は、Emitter
が String
値を生成することを検出するため、シリアライザーも自動的に設定します。
複数のコネクターがある場合は、アプリケーション設定で使用するコネクターを指定する必要があります。 |
見積リクエストの処理
ここでは、見積りリクエストを使用して価格を提示します。プロセッサー プロジェクト内に、src/main/java/org/acme/kafka/Processor/QuotesProcessor.java
ファイルを作成し、次のコンテンツを追加します。
package org.acme.kafka.processor;
import java.util.Random;
import jakarta.enterprise.context.ApplicationScoped;
import org.acme.kafka.model.Quote;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import org.eclipse.microprofile.reactive.messaging.Outgoing;
import io.smallrye.reactive.messaging.annotations.Blocking;
/**
* A bean consuming data from the "quote-requests" Kafka topic (mapped to "requests" channel) and giving out a random quote.
* The result is pushed to the "quotes" Kafka topic.
*/
@ApplicationScoped
public class QuotesProcessor {
private Random random = new Random();
@Incoming("requests") (1)
@Outgoing("quotes") (2)
@Blocking (3)
public Quote process(String quoteRequest) throws InterruptedException {
// simulate some hard working task
Thread.sleep(200);
return new Quote(quoteRequest, random.nextInt(100));
}
}
1 | メソッドが requests チャネルからのアイテムを消費することを示します。 |
2 | メソッドによって返されるオブジェクトが quotes チャネルに送信されることを示します。 |
3 | 処理が blocking であり、呼び出し元のスレッドで実行できないことを示します。 |
quote-requests
トピックからのすべての Kafka record について、Reactive Messaging は`process` メソッドを呼び出し、返された Quote
オブジェクトを quotes
チャネルに送信します。この場合、application.properties
ファイルでチャネルを設定して、requests
および quotes
チャネルを設定する必要があります。
%dev.quarkus.http.port=8081
# Configure the incoming `quote-requests` Kafka topic
mp.messaging.incoming.requests.topic=quote-requests
mp.messaging.incoming.requests.auto.offset.reset=earliest
この場合、着信側と発信側のコネクタ設定が1つずつあり、それぞれに明確な名前が付けられていることに注意してください。 設定プロパティは以下のような構造になっています:
mp.messaging.[outgoing|incoming].{channel-name}.property=value
channel-name
セグメントは、 @Incoming
および @Outgoing
アノテーションで設定された値と一致する必要があります。
-
quote-requests
→ この Kafka トピックから見積リクエストを読みます -
quotes
→ この Kafka トピックに見積を書き込みます
この設定の詳細については、Kafka ドキュメントの プロデューサー設定 and コンシューマー設定 セクションを参照してください。これらのプロパティーは、 |
mp.messaging.incoming.requests.auto.offset.reset=earliest
は、コンシューマーグループにコミットされたオフセットがない場合に、最初のオフセットからトピックの読み取りを開始するようにアプリケーションに指示します。つまり、プロセッサーアプリケーションを起動する前に送信されたメッセージも処理します。
シリアライザーまたはデシリアライザーを設定する必要はありません。Quarkus はそれらを検出し、何も見つからない場合は、JSON シリアル化を使用してそれらを生成します。
見積の受信
プロデューサー プロジェクトに戻ります。QuotesResource
を変更して、Kafka からの見積りを消費し、サーバー送信イベントを介してクライアントに送り返します。
import io.smallrye.mutiny.Multi;
...
@Channel("quotes")
Multi<Quote> quotes; (1)
/**
* Endpoint retrieving the "quotes" Kafka topic and sending the items to a server sent event.
*/
@GET
@Produces(MediaType.SERVER_SENT_EVENTS) (2)
public Multi<Quote> stream() {
return quotes; (3)
}
1 | @Channel 修飾子を使用して quotes チャネルを挿入します。 |
2 | Server Sent Events を使用してコンテンツが送信されたことを示します。 |
3 | ストリーム (Reactive Stream) を返します。 |
Quarkus は quotes
チャネルを quotes
Kafka トピックに自動的に関連付けるため、何も設定する必要はありません。また、Quote
クラスのデシリアライザーも生成します。
Kafka でのメッセージのシリアライズ
この例では、Jackson を使用して Kafka メッセージをシリアライズ/デシリアライズしました。メッセージのシリアルライズに関するその他のオプションについては、Kafka リファレンスガイド - シリアル化 を参照してください。 We strongly suggest adopting a contract-first approach using a schema registry. To learn more about how to use Apache Kafka with the schema registry and Avro, follow the Using Apache Kafka with Schema Registry and Avro guide for Avro or you can follow the Using Apache Kafka with Schema Registry and JSON Schema guide.. |
HTML ページ
見積をリクエストし、SSE で取得した価格を表示する HTML ページの最終調整を行います。
プロデューサー プロジェクト内に、次の内容で src/main/resources/META-INF/resources/quotes.html
ファイルを作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Prices</title>
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly.min.css">
<link rel="stylesheet" type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/patternfly/3.24.0/css/patternfly-additions.min.css">
</head>
<body>
<div class="container">
<div class="card">
<div class="card-body">
<h2 class="card-title">Quotes</h2>
<button class="btn btn-info" id="request-quote">Request Quote</button>
<div class="quotes"></div>
</div>
</div>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$("#request-quote").click((event) => {
fetch("/quotes/request", {method: "POST"})
.then(res => res.text())
.then(qid => {
var row = $(`<h4 class='col-md-12' id='${qid}'>Quote # <i>${qid}</i> | <strong>Pending</strong></h4>`);
$(".quotes").prepend(row);
});
});
var source = new EventSource("/quotes");
source.onmessage = (event) => {
var json = JSON.parse(event.data);
$(`#${json.id}`).html((index, html) => {
return html.replace("Pending", `\$\xA0${json.price}`);
});
};
</script>
</html>
ここでは何も特別なことはありません。ユーザーがボタンをクリックすると、見積をリクエストするための HTTP リクエストが作成され、保留中の見積がリストに追加されます。SSE を介して受け取った見積もりごとに、リスト内の対応するアイテムが更新されます。
起動
両方のアプリケーションを実行する必要があります。1 つの端末で、次を実行します。
mvn -f producer quarkus:dev
別の端末で、次を実行します。
mvn -f processor quarkus:dev
Quarkus は、Kafka ブローカーを自動的に起動し、アプリケーションを設定して、異なるアプリケーション間で Kafka ブローカーインスタンスを共有します。詳細については、Dev Services for Kafka を参照してください。
ブラウザーで http://localhost:8080/quotes.html
を開き、ボタンをクリックして見積をリクエストします。
JVM またはネイティブモードでの実行
開発モードまたはテストモードで実行していない場合は、Kafka ブローカーを起動する必要があります。 Apache Kafka Web サイト に記載された手順に従うか、次の内容で docker-compose.yaml
ファイルを作成できます。
version: '3.5'
services:
zookeeper:
image: quay.io/strimzi/kafka:0.41.0-kafka-3.7.0
command: [
"sh", "-c",
"bin/zookeeper-server-start.sh config/zookeeper.properties"
]
ports:
- "2181:2181"
environment:
LOG_DIR: /tmp/logs
networks:
- kafka-quickstart-network
kafka:
image: quay.io/strimzi/kafka:0.41.0-kafka-3.7.0
command: [
"sh", "-c",
"bin/kafka-server-start.sh config/server.properties --override listeners=$${KAFKA_LISTENERS} --override advertised.listeners=$${KAFKA_ADVERTISED_LISTENERS} --override zookeeper.connect=$${KAFKA_ZOOKEEPER_CONNECT}"
]
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
LOG_DIR: "/tmp/logs"
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
networks:
- kafka-quickstart-network
producer:
image: quarkus-quickstarts/kafka-quickstart-producer:1.0-${QUARKUS_MODE:-jvm}
build:
context: producer
dockerfile: src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
depends_on:
- kafka
environment:
KAFKA_BOOTSTRAP_SERVERS: kafka:9092
ports:
- "8080:8080"
networks:
- kafka-quickstart-network
processor:
image: quarkus-quickstarts/kafka-quickstart-processor:1.0-${QUARKUS_MODE:-jvm}
build:
context: processor
dockerfile: src/main/docker/Dockerfile.${QUARKUS_MODE:-jvm}
depends_on:
- kafka
environment:
KAFKA_BOOTSTRAP_SERVERS: kafka:9092
networks:
- kafka-quickstart-network
networks:
kafka-quickstart-network:
name: kafkaquickstart
最初に、次のコマンドを使用して両方のアプリケーションを JVM モードでビルドします。
mvn -f producer package
mvn -f processor package
パッケージ化したら、docker-compose up
を実行します。
これは開発クラスターであり、本番では使用しないでください。 |
アプリケーションをネイティブ実行可能ファイルとしてビルドし、実行することもできます。まず、両方のアプリケーションをネイティブとしてコンパイルします。
mvn -f producer package -Dnative -Dquarkus.native.container-build=true
mvn -f processor package -Dnative -Dquarkus.native.container-build=true
次のコマンドでシステムを実行します。
export QUARKUS_MODE=native
docker-compose up --build
さらに詳しく
このガイドでは、Quarkus を使用して Kafka とやりとりする方法を示しました。 SmallRye Reactive Messaging を利用して、データストリーミングアプリケーションを構築します。
機能と設定オプションの完全なリストについては、Apache Kafka エクステンションのリファレンスガイド を確認してください。
In this guide we explore how we can interact with Apache Kafka using the Quarkus Messaging extensions. Quarkus extension for Kafka also allows using Kafka clients directly. |