lasciva blog

開発して得た知見やwebビジネスのストック

「Apache Kafka 分散メッセージングシステムの構築と活用」を読んだ

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

目的、モチベーション

本業のプロダクトでKafkaを使っていて、理解を深めたいと思ったので読んだ。
読む前の状態としては、Kafkaは触れてるが大規模のデータを捌けるPub/Subメッセージを扱うログ型DBみたいなものぐらいのイメージだった。

全体の感想

生まれた歴史から、どのような仕組みで何を解決するものなのか、実際の例まで一通り網羅されていて、入門書として良かった。
実用例のパターンも何種類も紹介されていて、本番環境レベルではないがイメージを掴むには良かった。
インストール方法やコード例(Javaがほとんど)もあって、手も動かせる。
コードで動かさなかったが、半日ぐらいで読めた。

Kafkaは便利だと思っていたが、完全にメッセージを厳密に一度だけ処理するには、アプリケーション側でも冪等性を保つなどの工夫が必要で単純なものではないんだなと思った。

目次

概要

印象に残ったところを記載してます。少し公式ドキュメントを読んで補足している箇所もあります。

第1部 導入Apache Kafka

1 Apache Kafkaの概要

1.3 Kafka誕生の背景

元々は、LinkedInの下記のような技術的課題からオープンソースとして生まれた。

  1. スループットでリアルタイムに処理したい
  2. 任意のタイミングでデータを読み出したい
  3. 各種プロダクトやシステムとの接続を用意にしたい
  4. メッセージをロストしたくない
1.4 Kafkaによる要求仕様の実現

下記の3要素からなる。

  • Producer: メッセージの送信元
  • Broker: メッセージの収集/配信役
    • 挟むことで、ProducerとConsumerの接続先を一つにでき、増減などの構成変更に影響されないようにできる
  • Consumer: メッセージの配信先

https://kafka.apache.org/images/producer_consumer.png

https://kafka.apache.org/images/producer_consumer.png

送達保証
Exactly Onceレベル(Consumerが処理を実行するのを一回にする)の送達保証をするために、トランザクションの概念を導入。

ProducerとBroker間では、BrokerがProducerにAckの返送に失敗した場合、ProducerがRetryしてBrokerがメッセージを受け取り、2度目以降は記録せずに排除することで実現する。
ConsumerとBroker間では、どこまでメッセージを受け取ったかのオフセットを管理されており、トランザクションでオフセットのコミットを行うことで実現する。オフセットコミットに失敗した場合、もう一度メッセージが処理されるため、Consumer側でも冪等性が必要。

2 Kafkaの基本

2.3 システム構成

Brokerは耐久性向上のために複数台で構成されており、ZooKeeperで分散処理の管理が行われている。

2.4 分散メッセージングのための仕組み

f:id:hacking15dog:20191014221434p:plain

Partition: Topicに対する負荷を分散させるために、Partition単位で分割されている。負荷分散のために、Brokerクラスタ中に分散に配置される。

Messageの送受信
ProducerのMessage送信: メッセージがあるデータ量まで蓄積されたか、指定した時間毎に送ることでパフォーマンス向上できる。
ConsumerのMessage取得: 同様にまとめて送信することができる。

スループットの向上が期待できる一方で、レイテンシーが発生するのでトレードオフ

Consumerのロールバック
ロールバックによって、メッセージのAt Least Once(最低でも一回は受信される)ことは保証できるが、Exactly Onceはアプリケーション側の対応が必要。

2.5 データの堅牢性を高めるレプリケーションの仕組み

ISR: In-Sync Replica, 最新の状態を保っているReplicaのこと。
最小のISR数を min.insync.replicaで指定できる。

High Watermark
レプリケーションが完了しているOffsetのことを指す。これより新しいOffsetのものはConsumerが取得できない。

ProducerのMessage到達保証レベルの調整
Messageが送信されたことを示すAckを、BrokerがProducerへ返す設定によって、性能と耐障害性に大きく影響する。

説明
0 BrokerのAckを待たない。Brokerに保存されたことは一切保証されず、リトライも行われない。
1 Leaderのレプリカに書き込まれたらAckを返す。Followerのレプリカに書き込まれる前に、Leaderが落ちるとデータロストが発生する。
all すべてのISRの数までレプリケーションされたらAckを返す。但し、万一レプリケーションされてないノードのみ生き残った場合、データロストが発生する。

上記の「書き込まれた」というのはディスクに書き込まれたタイミングではなく、あくまでもメモリに書き込まれたタイミング。
ディスクへの書き込みは、 log.flush.interval.msを指定する。

3 Kafkaのインストール

インストール方法が紹介されてました。

4 KafkaのJava APIを用いたアプリケーションの作成

Javaの標準ライブラリでの実装例が紹介されてました。

4.4 作成したProducerアプリケーションのポイント

Producerの送信処理は非同期で行われるため、アプリケーション側で sendメソッドでメッセージをする際にはキューに入れられただけで実際の送信は別で行われる。

第2部 実践Apache Kafka

5 Kafkaのユースケース

下記のような実例の概要が紹介されてました。

  • データハブ
  • ログ収集
  • Webアクティビティ分析
  • IoT
  • イベントソーシング
5.3 データハブ

システムが成長していくと、複数のDBやログデータを収集して分析用のDBなどに加工して送りたくなるケースが出てくる。
それぞれを直接つないでしまうと、影響範囲が広くなる上に、必要となるフォーマットが異なる度に実装が必要となってしまう。
このサイロ化の解決手段の一つが、データハブアーキテクチャ。上流のシステムの接続をKafkaなどのハブを介することで、疎結合にすることができる。

6 Kafkaを用いたデータパイプライン構築時の前提知識

6.3 データパイプラインで扱うデータ

ストリーミング処理では、複数のアプリケーションが常時起動している状態で、改修などで発生するデータの変更等を行わなければならない。

Messageのデータ型は、異なる言語でも使えるフォーマット(JSON, Apache Avroなど)を用いた上で、互換性を保つべき。
しかし、どうしても互換性を担保できないケースも出てくるが、Schema Registryを用いれば解決できるケースもある。
Schema Registryでは、Apache Avro前提で、Producerの送信時にスキーマ情報を保存してConsumerがそのスキーマ情報をもとにデシリアライズする。

7 KafkaとKafka Connectによるデータハブ

8 ストリーム処理の基本

9 Structured Streamingによるストリーム処理

5章で紹介されていたものの、実装例を中心に紹介されてました。

10 Kafkaで構成するIoTデータハブ

10.2 IoTに求められるシステム特性とKafka

IoTでは、下記のような特徴からKafkaと相性が良い。

  • センサーデバイスはマシンパワーやストレージがない
  • 大量のデバイスが接続されているため、データ量が多い
  • レイテンシーが求められるケースが多い(すぐに反応しないデバイスは使いにくい)
10.3 センサーデータ向けデータハブの設計

センサー側でデータを加工するのは難しいため、サーバ側で加工することになる。
ブローカーに保存する前に、加工すればデータ量を削ることができるが、生データの情報が失われ、保存後に加工するとその逆が発生するので、トレードオフ

軽量なMQTTプロトコルを用いれば、メッセージングモデルによる非同期処理が効率的に行える。

11 さらにKafkaを使いこなすために

11.2 Consumer Group

Consumer Groupというグループを各Consumerに設定することで、複数のConsumerで分散処理を行える。

各Partitionにつき必ず1つのConsumerが対応付けられるように割り当てられる。
そのため、Consumerの数が多いと割り当てられないConsumerが発生する。逆にPartition数の方が多いと、複数のPartitionが割り当てられるConsumerが発生する。
割り当てのロジックは partition.assignment.strategyで指定できる。

11.3 Offset Commit

enable.auto.commitでOffset Commitの自動化を切り替えられる。
現時点(2019/10/14)の最新のバージョンでは、基本的にはtrueがデフォルトとなっている。
自動化するとアプリケーション側の制御が不要になる一方で、障害のタイミング次第で複数回処理されたり、一度も処理されないケースが発生するので要注意。

11.5 Partition数の考慮

Brokerの数に対して、Partition数が少ないと送受信の処理の重いLeaderがあるBrokerに負荷が集中してしまうため、要注意。

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)

Apache Kafka 分散メッセージングシステムの構築と活用 (NEXT ONE)