1. 概要

このチュートリアルでは、ApacheKafkaを使用したイベント駆動型アーキテクチャのデータモデリングの領域に挑戦します。

2. 設定

Kafkaクラスターは、Zookeeperクラスターに登録されている複数のKafkaブローカーで構成されています。 簡単にするために、Confluentによって公開された既製のDockerイメージとdocker-compose構成を使用します。

まず、3ノードKafkaクラスター用のdocker-compose.ymlをダウンロードしましょう。

$ BASE_URL="https://raw.githubusercontent.com/confluentinc/cp-docker-images/5.3.3-post/examples/kafka-cluster"
$ curl -Os "$BASE_URL"/docker-compose.yml

次に、ZookeeperノードとKafkaブローカーノードを起動してみましょう。

$ docker-compose up -d

最後に、すべてのKafkaブローカーが稼働していることを確認できます。

$ docker-compose logs kafka-1 kafka-2 kafka-3 | grep started
kafka-1_1      | [2020-12-27 10:15:03,783] INFO [KafkaServer id=1] started (kafka.server.KafkaServer)
kafka-2_1      | [2020-12-27 10:15:04,134] INFO [KafkaServer id=2] started (kafka.server.KafkaServer)
kafka-3_1      | [2020-12-27 10:15:03,853] INFO [KafkaServer id=3] started (kafka.server.KafkaServer)

3. イベントの基本

イベント駆動型システムのデータモデリングのタスクを開始する前に、イベント、イベントストリーム、プロデューサー-コンシューマー、トピックなどのいくつかの概念を理解する必要があります。

3.1. イベント

Kafka-worldのイベントは、domain-worldで発生した何かの情報ログです。 これは、タイムスタンプ、メタ情報、ヘッダーなどの他のいくつかの属性とともに、情報をキーと値のペアメッセージとして記録することによって行われます。

チェスのゲームをモデル化していると仮定しましょう。 その場合、イベントは移動である可能性があります。

イベントは、アクター、アクション、およびその発生時刻の重要な情報を保持していることがわかります。 この場合、 Player1 がアクターであり、アクションは12/2020/2500でセルa1からa5にルークを移動します。 08:30

3.2. メッセージストリーム

Apache Kafkaは、イベントをメッセージストリームとしてキャプチャするストリーム処理システムです。 私たちのチェスのゲームでは、イベントストリームは、プレーヤーがプレイした動きのログと考えることができます。

各イベントの発生時に、ボードのスナップショットがその状態を表します。 通常、従来のテーブルスキーマを使用して、オブジェクトの最新の静的状態を格納するのが一般的です。

一方、イベントストリームは、2つの連続する状態間の動的な変化をイベントの形式でキャプチャするのに役立ちます。 これらの不変の一連のイベントを再生すると、ある状態から別の状態に遷移できます。これは、イベントストリームと、ストリームテーブルの二重性としてよく知られている従来のテーブルとの関係です。 。

2つの連続したイベントだけでチェス盤のイベントストリームを視覚化してみましょう。

4. トピック

このセクションでは、ApacheKafkaを介してルーティングされるメッセージを分類する方法を学習します。

4.1. 分類

Apache Kafkaなどのメッセージングシステムでは、イベントを生成するものはすべて、一般にプロデューサーと呼ばれます。 それらのメッセージを読んで消費するものは消費者と呼ばれます。

実際のシナリオでは、各プロデューサーはさまざまなタイプのイベントを生成できるため、消費者が関連するメッセージをフィルター処理して残りを無視することを期待する場合、消費者は多大な労力を費やすことになります。

この基本的な問題を解決するために、 Apache Kafkaは、本質的に一緒に属するメッセージのグループであるトピックを使用します。 その結果、消費者はイベントメッセージを消費しながら生産性を高めることができます。

チェス盤の例では、トピックを使用して、chess-movesトピックの下にあるすべての動きをグループ化できます。

$ docker run \
  --net=host --rm confluentinc/cp-kafka:5.0.0 \
  kafka-topics --create --topic chess-moves \
  --if-not-exists \
  --partitions 1 --replication-factor 1 \
  --zookeeper localhost:32181
Created topic "chess-moves".

4.2. 生産者/消費者

それでは、プロデューサーとコンシューマーがメッセージ処理にKafkaのトピックをどのように使用するかを見てみましょう。 これを示すために、Kafkaディストリビューションに付属の kafka-console-producerおよびkafka-console-consumerユーティリティを使用します。

kafkaという名前のコンテナをスピンアップしましょう-プロデューサーここで、プロデューサーユーティリティを呼び出します。

$ docker run \
--net=host \
--name=kafka-producer \
-it --rm \
confluentinc/cp-kafka:5.0.0 /bin/bash
# kafka-console-producer --broker-list localhost:19092,localhost:29092,localhost:39092 \
--topic chess-moves \
--property parse.key=true --property key.separator=:

同時に、 kafka -consumer という名前のコンテナーを起動して、コンシューマーユーティリティを呼び出すことができます。

$ docker run \
--net=host \
--name=kafka-consumer \
-it --rm \
confluentinc/cp-kafka:5.0.0 /bin/bash
# kafka-console-consumer --bootstrap-server localhost:19092,localhost:29092,localhost:39092 \
--topic chess-moves --from-beginning \
--property print.key=true --property print.value=true --property key.separator=:

それでは、プロデューサーを介したゲームの動きを記録しましょう。

>{Player1 : Rook, a1->a5}

コンシューマーはアクティブであるため、Player1としてキーを使用してこのメッセージを取得します。

{Player1 : Rook, a1->a5}

5. パーティション

次に、パーティションを使用してメッセージの分類をさらに作成し、システム全体のパフォーマンスを向上させる方法を見てみましょう。

5.1. 並行性

トピックを複数のパーティションに分割し、複数のコンシューマーを呼び出して、異なるパーティションからのメッセージを消費することができます。 このような同時実行動作を有効にすることで、システムの全体的なパフォーマンスを向上させることができます。

デフォルトでは、トピックの作成時に–bootstrap-serverオプションをサポートするKafkaバージョンは、トピックの作成時に明示的に指定されていない限り、トピックの単一パーティションを作成します。 ただし、既存のトピックについては、パーティションの数を増やすことができます。 chess-movesトピックのパーティション番号を3に設定しましょう。

$ docker run \
--net=host \
--rm confluentinc/cp-kafka:5.0.0 \
bash -c "kafka-topics --alter --zookeeper localhost:32181 --topic chess-moves --partitions 3"
WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected
Adding partitions succeeded!

5.2. パーティションキー

トピック内で、Kafkaはパーティションキーを使用して複数のパーティションにまたがるメッセージを処理します。 一方では、プロデューサーはそれを暗黙的に使用して、メッセージをパーティションの1つにルーティングします。 一方、各コンシューマーは特定のパーティションからメッセージを読み取ることができます。

デフォルトでは、プロデューサーはキーのハッシュ値を生成し、その後にパーティション数のモジュラスが続きます。 次に、計算された識別子で識別されるパーティションにメッセージを送信します。

kafka -console-producer ユーティリティを使用して新しいイベントメッセージを作成しましょう。ただし、今回は両方のプレーヤーの動きを記録します。

# kafka-console-producer --broker-list localhost:19092,localhost:29092,localhost:39092 \
--topic chess-moves \
--property parse.key=true --property key.separator=:
>{Player1: Rook, a1 -> a5}
>{Player2: Bishop, g3 -> h4}
>{Player1: Rook, a5 -> e5}
>{Player2: Bishop, h4 -> g3}

これで、2つのコンシューマーを持つことができます。1つはパーティション1から読み取り、もう1つはパーティション2から読み取ります。

# kafka-console-consumer --bootstrap-server localhost:19092,localhost:29092,localhost:39092 \
--topic chess-moves --from-beginning \
--property print.key=true --property print.value=true \
--property key.separator=: \
--partition 1
{Player2: Bishop, g3 -> h4}
{Player2: Bishop, h4 -> g3}

Player2によるすべての動きがパーティション1に記録されていることがわかります。 同様に、Player1による移動がパーティション0に記録されていることを確認できます。

6. スケーリング

トピックとパーティションをどのように概念化するかは、水平スケーリングにとって非常に重要です。 一方では、トピックは、データの事前定義された分類に近いものです。 一方、パーティションは、オンザフライで発生するデータの動的な分類です。

さらに、トピック内に構成できるパーティションの数には実際的な制限があります。 これは、各パーティションがブローカーノードのファイルシステムのディレクトリにマップされているためです。 パーティションの数を増やすと、オペレーティングシステムで開いているファイルハンドルの数も増えます。

経験則として、 Confluentの専門家は、ブローカーあたりのパーティション数を100 xbxr に制限することを推奨しています。ここで、 b はKafkaクラスター内のブローカーの数であり、 r は複製係数です。

7. 結論

この記事では、 Docker環境を使用して、メッセージ処理にApacheKafkaを使用するシステムのデータモデリングの基礎を説明しました。 イベント、トピック、およびパーティションの基本を理解したので、イベントストリーミングを概念化し、このアーキテクチャパラダイムをさらに使用する準備が整いました。