Apach Kafka 超入門 2

## 始めに
前回に引き続きApache Kafkaについて勉強したことをまとめてみました。

おさらいとして、前回の記事に書いた内容も含んではいますが、基礎的な知識に関しては以下の記事を読んでいただくようお願いします。

https://qiita.com/ist-a-ku/items/2d8d5ea448b38224dd99

今回は特に、「Kakfaをよりうまく使いこなすために」という観点で、いくつかの知識をピックアップして紹介していきたいと思います。
### この記事の対象者
・Apache Kafkaの勉強を始めた方
・Apache Kakfaに関する基礎的な知識を得たい方

### この記事で伝えること
・Apache Kafkaを支える仕組み
・Apache Kafkaを使いこなすために必要な知識

## データのロストと処理継続性のトレードオフ
:::note info
#### おさらい〜Replicaについて〜
Kafkaでは、異なるBrokerに同一TopicのPartitionを分散して配置します。
その中で一つのPartitionがReader Replicaとして実際にProducer・Consumerとやりとりをします。他のPartitionはFollower ReplicaとしてLeader Replicaからメッセージを取得して複製を保ち、障害発生時に備えています。

![分散処理1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/50fc988f-6591-b3f1-86be-a2fdcba3ed98.png)
:::

### レプリカの同期状態
Leader Replicaの複製を保っているFollower Replicaは __In-Sync Replica (ISR)__ と呼ばれます。
逆に、Leader Replicaの複製状態になっていないFollower Replicaを __Under Replicated Partions__ と呼びます。

各Topicでは、 __最小ISR数(min.insync.replica)__  (= 複製状態を保つべきレプリカの最小数)の設定が可能です。

これにより、Leader ReplicaからFollower Replicaへの複製に遅延や停止が発生しても、これを許容し __最低限の複製を保っていればProducer・Consumerとのやりとりを続けることが可能__ です。
あるPartitionのISR数が最小数を下回ると、そのPartitionのLeader ReplicaはProducerから書き込み出来なくなります。

（例）Replica数が3・min.insync.replicaが2
　→　最低2つのReplicaが複製を保っていればやりとりを継続する

![kafka_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/8ee4938d-948c-8abe-0645-99f2929556e8.png)

### Messageの到達保証レベル

:::note info
#### おさらい〜Ackについて〜

Brokerがメッセージを受け取った際に、Producerに対して受け取ったことを返答するAckを送信します。ProducerがAckを受け取らなかった場合、Producerはメッセージを再送するべきだと判断することができます。

![kafka_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/e5162398-4835-fac5-0bdb-ef4f0c16e552.png)

:::

BrokerがどのタイミングでAckを送信するかについて、以下3種類の到達保証レベルを設定することができます。

|  設定  |  説明  |
| ---- | ---- |
|  0 |  Producerは __Ackを待たずに__ 次のメッセージを送信する |
|  1 |  Leader Replicaへの書き込みが完了したらAckを返す |
|  all | __最小ISR数まで書き込みが完了したら__ Ackを返す |

0に設定した場合、何らかの障害でメッセージの書き込みが失敗しても、Producerはメッセージの送信を続けることができます。

一方でallに設定した場合、複製状態のレプリカができて初めて受取済となるため、 __データをロストする可能性が低く__ なります。
しかし、最小ISR数の設定によっては一つのBrokerが故障した場合、 __メッセージの送信が止まってしまう__ 可能性があります。

ここからは最小ISR数とAck設定の関係性についてみていきましょう。
### 最小ISR数とAck設定の関係性
Ack設定をallにした場合、最小ISR数の設定によってメッセージの書き込みを継続できるかどうかが決まります。
以下2つのケースを比較していきましょう。


- ケース1：最小ISR数がレプリカ数と同じである場合
Brokerが一台故障した場合、該当のレプリカが障害解消後にISRとなるまで __書き込みができなくなる__ 。

![kafka_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/c4daf454-e14c-f757-a617-6b5f12883883.png)


- ケース2：最小ISR数がレプリカ数よりも少ない場合
Brokerが一台故障した場合でも、ISR数が2であればAckを返すため、 __書き込みを継続できる__ 。

![kafka_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/59ecf8da-5b66-40e0-2ff0-c3880a46a709.png)


処理を継続できるという点においては、ケース1よりもケース2の方が優れています。
その一方で、ケース2において故障したBrokerの復旧前にさらに別のBrokerで障害が発生した場合、 __処理中のメッセージをロストするリスクが高く__ なります。

このように、最小ISRの設定とAckの設定は、
- データをロストしないこと
- 処理を継続すること
の __トレードオフを調整する__ 役割を担っていると言えます。

よってケース1とケース2のどちらかが優れているというわけではなく、システムの要件やKafkaの利用目的によって設定を決めるべきだということになります。

## Offset Commitのタイミング
::: note info
##### おさらい〜Offset Commitについて〜
ConsumerがBrokerに対してどのメッセージまで処理が完了したかという記録を残すことをOffset Commitといいます。
この情報により、障害復旧後Brokerからのメッセージ受信を再開する際、Consumerはどのメッセージから処理を再開すれば良いかを判断することができます。

![kafka_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/6bd4bb80-38df-43b0-c009-ea0e2cb860c6.png)


:::

Offset Commitには以下二つの方法があります。
それぞれの特徴とメリット・デメリットをみていきましょう。
- __Auto Offset Commit__
- __Manual Offset Commit__

### Auto Offset Commit
Autoとあるように、 __一定の間隔ごとに自動で__ Offset Commitを行う方式のことです。
Offset Commitを行う間隔はオプションで指定することができ、デフォルトでは5秒となっています。

![kafka_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/327fd01b-1a37-0845-9ce4-944ac06bdd7a.png)


#### メリット
Consumer側でOffset Commitを明示的に行う必要がないため、Consumer側の処理が簡潔になります。

#### デメリット
Consumer側で障害が発生した際、メッセージが失われたり、メッセージの重複（同じメッセージを再度処理する）が発生したりする可能性があります。

- __メッセージのロストが発生するケース__
Offset Commitされたメッセージの処理が完了する前に障害が発生した場合、処理中のメッセージまで「処理が完了した」と記録されてしまいます。そのため障害解消後、処理途中であったメッセージは処理されなくなってしまいます。

![kafka_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/8f946e88-f942-e300-591c-5788a678a9cd.png)



- __メッセージの重複が発生するケース__
複数のメッセージの処理が完了しているものの、Offset Commitが行われる前に障害が発生した場合、処理が完了しているはずのメッセージも「処理が完了していない」と記録されてしまいます。そのため障害解消後、複数のメッセージを再度処理することになってしまいます。

![kafka_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/21b606cd-f5f4-fa8a-c229-8edbc812ecbf.png)


###  Manual Offset Commit
Consumer側にOffset Commitの処理を記述し、明示的にOffset Commitを行う方式のことです。つまり、任意のタイミングでOffset Commitを行うことができます。

![kafka_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/ab01b3bc-79d4-9e28-c240-fce1ac63b3cb.png)


#### メリット
適切に利用することで、__メッセージのロストを発生させない__ ようにできます。
各メッセージを取得し処理が完了したタイミングでOffset Commitを行うようにすれば、Offset Commitされたメッセージは __必ず処理が完了している__ ことになるため、データのロストを防ぐことができます。
また、メッセージの重複も完全に防ぐことはできませんが、重複するメッセージの数は減らすことができます。

![kafka_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/a30f6ee7-a00f-b7c6-7624-0eea532732d7.png)


##### デメリット
一般的にはAuto Offset Commitの方が頻繁にOffset Commtiを行うことになるため、Brokerへの負荷が高くなります。
また、仕組みを理解して正しく使用する必要があるため、初期コスト・維持コストはAuto Offset Commitに比べて高くなります。


このように、Offset Commitを行うタイミングを適切に設定することによって、 __メッセージの重複が発生する可能性はありつつも、データのロストは防ぐ__ ことができます。

ただしOffset Commitの頻度によってはスループットに大きく影響を与えることになるため、Auto Offset CommitとManula Offset Commitどちらか利用するかは要件によって判断する必要があります。

## まとめ
- データロストの可能性を下げることと処理の継続性を担保することはトレードオフの関係にある。そのため、最小ISR数とAck設定によって、適切に調整する必要がある。

- Offset Commitを行うタイミングはAuto / Manualで設定することができる。適切にOffset Commitのタイミングを設定することで、Consumer側のデータのロストを完全に防ぐ仕組みを作ることができる。

## 最後に
今回はどうすればKafkaをより使いこなせるか？という観点で、学んだことをまとめてみました。
Kafkaの利用において、ベストプラクティスはなく、システムの要件によって設定等を考えていく必要があるというところが印象的でした。

今度こそはKafkaを使ったアプリケーションの開発にも取り組んでみようと思います。

## 参考文献
- [Apache Kafka 分散メッセージングシステムの構築と活用](https://amzn.asia/d/iUXz4Kj)
- [Kafka講座2：Apache Kafkaの分散処理の仕組み](https://youtu.be/FJZeaY3n5CY?si=KCak6ZEWoRITwnWf)