Apache Kafka 超入門

## 始めに
Apache Kafkaを一から勉強したので、学んだことをできるだけ簡単な言葉で噛み砕いて自分なりにまとめてみました。

### この記事の対象者
・Apache Kafkaとはそもそも何か知りたい方
・Apache Kafkaの勉強を始めた方

### この記事で伝えること
・Apache Kafkaとは何か
・Apache Kafkaの仕組み
・Apache Kafkaの特徴

## Apache Kafkaとは？
Apache Kafkaとは、
「__複数台のサーバーで大量のデータを処理する分散メッセージングシステム__」
のことです。

まずはメッセージングシステムについて説明していきます。
### メッセージングシステムとは？
現在の世の中では、リアルタイムで大量のデータが生成され続けています。
これらは「ストリームデータ」と呼ばれており、様々な場所でリアルタイムに収集され、活用されています。
例えば、交通状況もストリームデータの一つであり、渋滞状況の把握などに使われています。

作られたデータは利用場所に送信する必要がありますが、
直接繋げようとすると、下記の図のようにかなり複雑な構成になり、開発・運用・管理が難しくなります。

![メッセージングシステム1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/64ac5a2a-78f9-1326-418c-1ca00d53b600.png)


そこで、データ生成場所とデータ利用場所に __中継役__ となるメッセージングシステムを配置します。
__Apache Kafkaはこの中継役（メッセージングシステム）に当たるもの__ です。
　
![メッセージングシステム2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/b460c4e8-6693-0d5a-4ed9-a041cd72ce1c.png)


メッセージングシステムを介すことで、下記のようなメリットがあります。

- __シンプルな構成を実現できる__
データ生成場所は、どのデータ利用場所にデータを送ればよいかを考える必要がなくなり、メッセージングシステムに送ればよいだけになります。
同様にデータ利用場所もメッセージングシステムからのみデータを受信すれば済むようになります。
データ生成場所・データ利用場所ともに接続先を一つにできるため、とてもシンプルな構成を実現することができます。


- __構成の変更に強くなる__
新しいデータ生成場所・データ利用場所が作られても、メッセージングシステムに繋げばよいだけになり、実装コストが低くなります。

このようなメリットはありますが、下記のような懸念が浮かんだ方もいるのではないでしょうか。
- __メッセージングシステムや連携しているシステムに不具合が起きた場合、どうなる？__
- __データの送受信量が増えたら、性能は悪くなってしまう？__
- __そもそもどのように他のシステムと連携するのか？__

ここからはKafkaの特徴を見て、以上の懸念をどのように解消しているのかを説明していきたいと思います。

その前に、Kafkaのデータ送受信の基本的な構成を簡単に説明します。

### Apache Kafkaの構成
以下がKafkaを構成する主要な要素になります。
以降の説明では、ここで紹介する用語を使っていきます。

- __Broker__
データを受信・送信するサービス。中継役。
- __Message__
Kafkaが受信し送信するデータの単位。
- __Producer__
データの送信元。Brokerに対してMessageを送信する。
- __Consumer__
データの送信先。BrokerからMessageを取得する。
- __Topic__
Messageを種類ごとに管理するためのストレージ。Broker上で管理される。

Producer・Consumerは __特定のTopicを指定してMessageの送信・受信__ を行います。

![kafka構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/e116a02a-56c2-0aa7-b507-1f63aef1e97a.png)

## Apache Kafkaの特徴
それではKafkaの特徴を見ていきましょう。
Kafkaには大きく分けて以下4つの特徴があります。

- __分散システム__
- __データのディスクへの永続化__
- __メッセージの送達保証__
- __わかりやすいAPIの提供__

### 分散システム
一つの窓口でデータの受け取り・送信を処理する場合、データ量が増えるにつれて負荷は上がっていきます。
また、その窓口に障害が発生した場合、受け取ったデータが失われたり、データの送信ができなくなったりしてしまいます。
これを防ぐために、Kafkaでは分散システムを実現しています。

まずは、Kafkaの分散システムの仕組みを見ていきましょう。
先ほどBroker上ではメッセージを種類ごとにTopicで管理していると説明しましたが、Topicはさらに以下の要素で構成されています。

- __Partition__
Topicをさらに分割するため単位。

- __Replica__
Partitionの複製。つまりデータの複製のこと。

また、Replicaには二つの種類があります。
- __Leader Replica__
Producerからメッセージを受け取り、Consumerにメッセージを送信する役割を持つ。
各Partitionに必ず一つ存在している。

- __Follower Replica__
Leader Replicaからメッセージを取得して複製を保ち、障害発生時に備える。

![partitionImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/67a30771-5eae-c4ca-9db2-aeaf4b7614a3.png)

ここではわかりやすくするために、Partion、Replicaが一つのBrokerに配置されているように図を書きましたが、実際には __各Replicaは異なるBrokerに配置__ されます。
Replicaを異なるBrokerに配置することで、Kakaは分散処理を実現しているのです。

以下の図のように同じPartitionのLeader Replica・Follower Replicaを異なるBroker上に配置することで、 __一つのメッセージの複製が複数のサーバーで管理される__ ようになります。
また、実際にデータのやり取りを行うPartitionは複数のサーバーに分散されるため、 __サーバーそれぞれの役割も分散される__ ことになります。

![分散処理1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/c3b1a122-100e-28d2-1d11-1f1f785f459f.png)


このような分散システムの実現によって、以下のようなメリットがあります。

- __スケールアウトが可能__
サーバー(Broker)を増やしPartitionを再配置することで、Partitionの役割も均等になり、各Partitionの役割が少なくなります。サーバーの役割が分散されるため、性能も上がります。

- __障害発生時のデータ紛失を防ぐ__
別のBrokerにデータの複製があるため、あるBrokerに障害が発生してもデータは失われません。
また、Leaderが配置されているBrokerで障害が発生しても、 __別のBrokerに配置されているFollowerがすぐにLeaderとしての役割を引き継ぐ__ ため、データの受信・送信を問題なく続けることができます。

![分散処理障害時.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/f79c2b2e-235b-8bd8-b325-66f587d1a02c.png)

### データのディスクへの永続化
Kafkaでは、BrokerがProducerからメッセージを受け取るとディスク上に保存を行います。
これにより、 __Consumerは任意のタイミングでメッセージを取り出すことができる__ ようになります。

Brokerが能動的に送るのではなく、Consumerからのリクエストに応答して受動的にデータを送る仕組みにより、以下のメリットがあります。

- __バッチ処理を実現できる__
Consumerが任意のタイミングでデータを受信するという仕組みを活用し、Broker上に複数のメッセージを溜めておき、一括で受信することで、バッチ処理を実現することも可能です。
データを順次処理するストリーム処理とデータをまとめて処理するバッチ処理両方を実装できるため、柔軟にデータを利用することができます。

- __onsumerが故障した際のBrokerへの影響が少ない__
Consumerからのリクエストが無ければBrokerはデータを送信しません。そのため、Consumerが故障してもBrokerのデータ送信を停止するといった対応が不要になります。
### メッセージの送達保証
Kafkaには、Messageが正しく送信・受信されたことを確認する機能や、失敗した時のリトライ機能が備わっています。

前提として、送達保証には一般的に以下3つのレベルがあります。
|  レベル  |  概要  |  説明  |
| ---- | ---- | ---- |
|  At Most Once |  1回は送達を試みる |メッセージが失われても、再送信しない。性能は最も良い。 |
|  At Least Once  |  少なくとも1回は送達する |メッセージを複数回送信することを許容し、メッセージを失わない。複数回送信するため、データが重複する可能性がある。|
|  Exactly Once  | 1回だけ送達する |メッセージが失われることも重複することもなく、確実に送達できる。性能は最も悪くなる。|

Kafkaでは送達保証のレベルをこの3つから選択し設定することが可能ですが、
当初Kafkaは __At Least Once__ を実現する製品としてリリースされました。
At Least Onceを実現するための概念として、AckとOffset Commitというものがあります。

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

- __Offset Commit__
ConsumerがBrokerからどこまでメッセージを受け取り、Consumer側の処理が完了したかを知らせること。
※オフセット：基準となる位置からの差・距離

![送達保証.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/e4f33a2d-4d60-2341-6917-dafc90fc48b5.png)

オフセットという言葉にもあるように、BrokerとConsumerのやり取りでは、 __「どこまで送信したか」「どのまで処理が完了したか」__ という位置情報を管理しています。

具体的には、Partitionで受信したメッセージには連番が振られ、以下二つの位置情報でどのメッセージまで処理したかを記録します。

- __Current Offset__
Consumerがどこまでメッセージを読んだかを示す

- __Commit Offset__
ConsumerがどこまでOffset Commitしたかを示す

![offset1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/76ab01aa-21d6-8642-ed8a-73b032ef05e2.png)

ここからは、上記2つの位置情報を利用した、メッセージの送信に失敗した際のリトライの仕組みについて説明していきます。

ConsumerがMessage3,4を取得し、処理が完了する前(Offset Commitをする前)にConsumerで障害が発生したケースを考えてみましょう。

![offset2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/a4e4aa0a-6ff5-15bc-35c1-89382f257b67.png)

Counsumerが復旧した後、Current OffsetをCommitOffsetまで巻き戻し(ロールバック)を行います。
これにより、ConsumerはMessage3,4を再度受信することが可能になります。

![offset3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/c57cd9bf-cea8-17af-13c3-7824be2e9f75.png)


ここで注目したいのが、ConsumerはMessage3,4を2回受信しているということです。
このリトライはAt Least Oneを実現する仕組みであり、重複したデータをどのように対処するかは後続のアプリケーションに委ねられています。

### わかりやすいAPIの提供
Kafkaには以下が用意されており、自分で実装することなく連携できる製品が多く存在します。
- __Connect API__
Producer・ConsumerがBrokerとやりとりを行うためのAPI。

- __Kafka Connect__
Connect APIをベースにProducer・ConsumerとKafkaをつなぐためのフレームワーク。

- __Connector__
Kafkaと他製品を繋げるためのプラグイン。製品ごとに用意されているため、接続先にあったConnectorがあれば自分でコーディングする必要なく接続を実現することができます。
こちらのサイトから、実装されているConnectorを見ることができます。
https://www.confluent.io/product/connectors/

![kafkaAPI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710642/e94a1bb1-fb5c-8941-1996-69db4be97b70.png)

## まとめ
:::note info
Kafkaとは?

複数台のサーバーで大量のデータを処理する分散メッセージングシステムのこと。
:::

:::note info
メッセージングシステムとは?

データ生成場所からデータを受け取り、データ利用場所へデータを送信する役割を担う中継役。
:::

:::note info
Kafkaの特徴は？
1. 分散システム
2. データのディスクへの永続化
3. メッセージの送達保証
4. わかりやすいAPIの提供
:::

:::note info
Kafkaや連携しているシステムに不具合が起きた場合、どうなる？

複数のサーバー(Broker)で分散処理をしているため、あるサーバーが故障しても、他のサーバーで処理を続けられる。データも失わない。
連携システムに故障があった場合は、Ack・Offset Commitという仕組みで、修理後再度データの受信・送信を行うことができる。
:::

:::note info
データの送受信量が増えたら、性能は悪くなってしまう？

データの量が増えても、サーバーの台数を増やすことで性能を維持・改善することが可能。
:::

:::note info
そもそもどのように他のシステムと連携するのか？

Kafka Connectというフレームワークが用意されている。また、すでに多くの製品をKafkaに接続するためのConnectorが用意されているため、手間がかかることなく接続可能。
:::

## 最後に
今回はApache Kafkaについて、概要を簡単にまとめてみました。

まだまだ構造については理解できていない部分や触れられていない部分があるので、引き続き勉強していきたいのと、Kafkaを使ったアプリケーションの開発にも取り組んでみようと思います！

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