Apache Kafkaの性能検証(5): システム全体のレイテンシについて

初版: 2018/11/16
著者: 伊藤 雅博, 株式会社日立製作所

# はじめに

この投稿では[オープンソースカンファレンス2017.Enterprise](https://www.ospn.jp/osc2017.enterprise/)で発表した「[めざせ！Kafkaマスター　～Apache Kafkaで最高の性能を出すには～](https://www.ospn.jp/osc2017.enterprise/pdf/OSC2017.enterprise_Hitachi_Kafka.pdf)」の検証時に調査した内容を紹介します（全8回の予定）。本投稿の内容は2017年6月にリリースされたKafka 0.11.0 時点のものです。

最終回となる今回は、システム全体のレイテンシについて紹介します。前回まではデータ処理のスループットに着目してチューニングを行ってきました。一般的に、スループットが増加するとレイテンシも増加しますが、これまでの検証で行ったチューニングではレイテンシを考慮していませんでした。そこで今回はレイテンシの測定結果について紹介します。

過去の投稿：
1. [Apache Kafkaの概要とアーキテクチャ](https://qiita.com/sigmalist/items/5a26ab519cbdf1e07af3)
2. [Apache KafkaのProducer/Broker/Consumerのしくみと設定一覧](https://qiita.com/sigmalist/items/3b512e2ab49b07271665)
3. [Apache Kafkaの推奨システム構成と性能の見積もり方法](https://qiita.com/sigmalist/items/b3d25e914dc30539ece4)
4. [Apache Kafkaの性能検証（１）：検証内容と検証環境について](https://qiita.com/sigmalist/items/730c7c82e02e5a837b9a)
5. [Apache Kafkaの性能検証（２）：Producerのチューニング](https://qiita.com/sigmalist/private/0cd76d7edb055e89d1e6)
6. [Apache Kafkaの性能検証（３）：Brokerのチューニング結果](https://qiita.com/sigmalist/private/0795bde01bcbd78b1c47)
7. [Apache Kafkaの性能検証（４）：Producerの再チューニングおよびConsumerのチューニング結果](https://qiita.com/sigmalist/private/b0bc222f449a9f3cc273)

# システム全体のレイテンシ

システム全体のレイテンシは、ProducerのSend APIでRecordを追加する時点から、ConsumerのPoll APIでRecordを取得するまでの時間です。このレイテンシは以下に示す5つの処理時間の合計値となります。
 
![kafka08_01.png](https://qiita-image-store.s3.amazonaws.com/0/285851/3ef922ef-f265-60a6-140c-f537f04b1d3b.png)

前回までのチューニングを行った状態で、上記の各処理のレイテンシを測定した結果を以下に示します。これらのレイテンシは、[これまで測定してきたスループット](https://qiita.com/sigmalist/private/730c7c82e02e5a837b9a#%E6%80%A7%E8%83%BD%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9%E3%81%AE%E5%8F%8E%E9%9B%86%E6%96%B9%E6%B3%95)と同様に、KafkaがJMXで公開しているメトリックを収集したものです。

**① Send APIのブロック時間**

ユーザスレッドがSend APIでRecordを追加するスループットが、Producerのリクエスト送信スループットよりも高い場合は、Producerのバッファがいずれ満杯となります。Producerのバッファが満杯になると、ユーザスレッドはバッファに空きが出るまでSend APIでブロックされます。

今回の検証で使用した40個のProducerについて、Producerごとの空きバッファサイズの推移を以下に示します。測定開始直後から空きバッファサイズは減少し続け、2分ほど経過した時点でバッファの空き容量がほぼ0になることが分かります。そのため、2分ほど経過した時点からSend APIでブロックが発生していたと考えられます。

![kafka08_02.png](https://qiita-image-store.s3.amazonaws.com/0/285851/ac2d2f67-1aeb-4797-dedd-92d6a5d4ca96.png)

Producerの秒間リクエスト送信数の推移を以下に示します。測定開始から10秒ほど経過した後は、1Producerの秒間送信リクエスト数は3程度で推移していることが分かります。
 
![kafka08_03.png](https://qiita-image-store.s3.amazonaws.com/0/285851/f87e81e6-e3ca-703f-f267-eade6b8ce550.png)

今回の検証ではSend APIのブロック時間を測定していませんが、リクエストが送信されればバッファが空くため、ブロック時間は1秒÷3リクエスト≒**333ミリ秒**程度と推定できます。

**② Producerのバッファリング時間**

Send APIでRecord Batchに追加されたRecordは、ネットワークスレッドがリクエストとして送信するまで、Producerのバッファに留まり続けます。Recordのバッファリング時間の推移を以下に示します。測定開始直後からバッファリング時間は徐々に増加していき、2-3分経過した後は**50秒**程度で推移していることが分かります。

![kafka08_04.png](https://qiita-image-store.s3.amazonaws.com/0/285851/d8f0b04c-0e75-ab93-128d-c1055506a1ec.png)

今回の検証では、40個のProducerによる送信スループットの合計が803MB/sだったので、1 Producerあたりの送信スループットは約20MB/sとなります。今回の検証ではProducerの buffer.memory を 1GB に設定したため、バッファが満杯になるまでRecordが溜まると、Recordは送信されるまでに 1GB ÷ 20MB/s ≒ 50秒 かかることになります。

**③ Produceリクエストのレイテンシ**

今回の検証はProduceリクエストの acks を all 、Topicの min.insync.replicas を 2 に設定したため、ProduceしたRecordが2個以上のReplicaに複製された時点でレスポンスが返されます。この複製が完了したRecordは、Consumerから取得できるようになります。Produceリクエストのレイテンシの推移を以下に示します。スパイク時はレイテンシが10秒を超えることもありますが、平均すると**2.5秒**程度で推移していることが分かります。

![kafka08_05.png](https://qiita-image-store.s3.amazonaws.com/0/285851/adf17488-7dc3-eeb1-d0ec-758b83dd1829.png)

**④ Fetchリクエストのレイテンシ**

今回の検証では6個のConsumerでConsumer Groupを構成して、TopicのデータをFetchし続けました。各Consumerが送信したFetchリクエストのレイテンシの推移を以下に示します。レイテンシは 100ミリ秒から 300ミリ秒の間でばらつきがありますが、平均すると**150ミリ秒**程度で推移していることが分かります。

![kafka08_06.png](https://qiita-image-store.s3.amazonaws.com/0/285851/dea09d78-58df-2827-f0d5-556faa3d13b8.png)

**⑤ Poll APIのレイテンシ**

Consumerを使用するユーザアプリケーションは、Poll APIを呼び出してRecordを取得します。このときConsumerがRecordを保持していればRecordが即時返却されますが、保持していない場合は新たなRecordが得られるまでブロックされます。

今回の検証ではPoll APIのレイテンシを測定していませんが、Consumeスループットは803MB/s程度で推移していました。そのためRecordは即時返却され続けたと考えられ、レイテンシは**数ミリ秒**程度と推定できます。

# レイテンシについての考察

上記の①から⑤のレイテンシを合計すると**53秒**程度でした。今回は検証ではスループットを最大化するために、Producerの送信能力を超える速さでRecordを追加したので、Producerのバッファが詰まって送信まで50秒もかかっていました。Producerの buffer.memory を増やすと、ユーザアプリケーション側から流れてくるRecord流量の一時的な増加には耐えやすくなります。しかしバッファリング時間を監視していないと、Recordの送信が遅れていることに気付けないというリスクもあります。

Kafkaの最大スループット（803MB/s）を超えない範囲のRecord流量であれば、Producerで詰まることはなくなる（①、②の待ち時間ほぼ無くなる）ため、レイテンシは**3秒**程度になると推定できます。ただしRecord Batchの蓄積が遅くなるため、linger.msを増やす必要があるかもしれません。

# おわりに

ここまで、Kafkaの調査内容と性能検証結果を8回に分けて紹介してきました。Kafkaはスケーラビリティに優れた分散メッセージキューです。しかし、データを保護するために同期レプリケーションを行いつつ性能を引き出すには、内部構造を理解した上で適切なチューニングが必要となります。