Kafka は一般的に使用されている分散メッセージ ミドルウェアであり、RabbitMQ と比較して無制限の水平拡張が特徴であり、高信頼性、高スループット、低遅延を維持するため、RabbitMQ よりも高い市場シェアを持っています (オンラインで検索すると、Kafka は約 41 %、RabbitMQ は約 29%)。
1. カフカの共通概念
一般に、通常の開発では最初の 6 つの概念を理解するだけで十分で、残りの概念は Kafka の運用および保守構成、またはトラブルシューティングによく使用されます。
1.プロデューサープロデューサー
メッセージを生成し、kafka に配信する、kafka の一部ではない外部アプリケーションを指します。
2. 消費者
kafka に接続し、メッセージを受信/サブスクライブし、その後の論理処理を実行する外部アプリケーションを指します。これは kafka の一部ではありません。
コンシューマーは、kafka の複数のキュー (トピック) を同時に使用できます。
3. 消費者団体
Kafka に接続されたコンシューマはコンシューマ グループを指定する必要があります。複数のコンシューマが同じコンシューマ グループを指定できるため、同じメッセージが繰り返し消費されることを防ぐことができます。
2 つのコンシューマが同じキュー (トピック) にバインドされ、異なるコンシューマ グループを指定した場合、各メッセージは 2 つのコンシューマに同時に配信されます。
4. トピックのテーマ
送受信メッセージの論理的な集合である kafka では、各トピックをキューとして考えることができ、
プロデューサーとコンシューマーはトピックを接続することによってメッセージを処理します。
5、パーティションパーティション
Kafka に格納されるメッセージの物理的なコレクション。トピックは 1 つ以上のパーティションに分割でき、サブキューとして理解できます。
各パーティションは 1 つのトピックにのみ属し、1 つのコンシューマ (同じグループ) によってのみ使用できます。。
トピックによって受信されるすべてのメッセージは、メッセージのキーに従って選択された対応するパーティションに従って配信されます。
メッセージがキーを指定せず、パーティション ルールが定義されていない場合、Kafka はメッセージをランダムかつ均等に複数のパーティションに配信します。トピック。
注: 各パーティション内のメッセージは、先入れ先出しを保証するためにキューのルールに従う必要がありますが、異なるパーティション内のメッセージは保証されません。
したがって、コンシューマーがメッセージが配信された順序で消費できるようにしたい場合は、次のようにします。
- トピックごとにパーティションを 1 つだけ作成します (これはお勧めしません)
- 注文する必要があるメッセージの同じバッチについては、メッセージのキーとしてユーザー ID を使用するなど、同じキーを指定すると、同じキーを持つメッセージが同じパーティションに配信されます。
6.オフセットオフセット
各パーティション内のメッセージの一意の番号を指し、0 から増加します。トピック + パーティション + オフセットにより、メッセージを一意に見つけることができます。
注: 各パーティションの各メッセージのオフセットは異なる必要があり、異なるパーティションのオフセットが繰り返されます。
コンシューマーはまた、各消費のオフセット値を記録して、現在処理しているメッセージを識別し、切断および再接続時に消費を続行できるようにします。コンシューマのオフセットも Kafka に保存されます。
7. ブローカークラスターノード
Kafka クラスター内のノード。通常はサーバー上の Kafka インスタンスです。
8、レプリカコピー
トピックの各パーティションには複数のコピーを指定でき、各コピーにはまったく同じメッセージ データが保存されます。
一般に、ブローカーの障害によるパーティションのデータ損失を避けるために、同じパーティションの異なるコピーを異なるブローカーに保存することをお勧めします。
9、リーダー/フォロワー
トピックの各パーティションに複数のコピーがある場合、コピーの 1 つが読み取りおよび書き込みサービスを提供するリーダーとして機能し、残りのコピーはフォロワーとしてデータを同期するだけです。
リーダーが失敗した場合は、フォロワーからコピーがリーダーとして選出され、再度サービスを提供します。
10、ISR(同期レプリカ)
パーティションの同期レプリカのコレクション。各パーティションは、リーダーと同期しているフォロワーのリストである ISR リストを維持します。
フォロワーが同期の進行状況に追いつけない場合、または同期を維持できない場合、そのフォロワーは ISR リストから削除されます。
ISR リスト内のフォロワーのみがリーダーに昇格する機会があります。
注: リーダーになったコピーも ISR に含まれます。
11. LEOログ終了オフセット
LEO は、現在のパーティションに書き込まれる次のメッセージのオフセットであるログ終了オフセットを参照します。このメッセージは特定のメッセージを指すものではありません。
パーティションの各レプリカには独自の LEO があります。
12. HW 最高水準点
HW は、ハイ ウォーターマーク オフセット、つまり、送信され、すべてのレプリカにコピーされた現在のパーティション内の最高のメッセージ オフセット (オフセット) を指します。リーダーは、メッセージを受信してもまだ同期されていないときに、HW 値を更新しません
。
リーダーは、自身とすべてのフォロワーの LEO を比較し、小さい方の値を使用して HW 値を更新します。
13. LAG 遅延メッセージの数
コンシューマ グループは、トピックの各パーティションを消費するときに、各パーティションで LAG 値を計算します。LAG 値は、パーティション内のメッセージの総数とコンシューマによって消費されるメッセージの数の差を指します。
通常、パーティションの HW からコンシューマ グループのオフセットを引いた値です。
実際には運用保守担当者がLAGを監視し、例えば10,000を超えた場合にアラームを発して処理を行う必要があります。
これらの概念を理解した後、インターネットで kafka の動作原理の図を見つけました。
2.kafkaとRabbitMQの比較
RabbitMQ と比較すると、Kafka には次の特徴があります。
- Kafka のメッセージは使用後すぐには削除されませんが、一定期間 (デフォルトは 7 日) 後に削除されます。そして、RabbitMQ は消費後に削除されます。
特にトラブルシューティングでデータの回復が必要な場合に、kafka がメッセージを削除しないという事実が気に入っています。 - Kafka コンシューマはプル モードのみをサポートし、プッシュ モードはサポートしません。つまり、コンシューマはメッセージを取得するために Kafka をアクティブにポーリングすることしかできません。デフォルトでは 500 ミリ秒に 1 回プルされ、毎回最大 500 個のデータをプルできます。ポーリングの利点は次のとおりです。柔軟性はありますが、メッセージの時間とスペースを消費するパフォーマンスがないという欠点があります。
デフォルトでは、RabbitMQ はプッシュ モードのみをサポートし、コンシューマにメッセージを積極的にプッシュし、リアルタイム パフォーマンスが向上します。 - Kafka はトピック トピックを通じてプロデューサーとコンシューマを接続します。同じトピックに接続された複数のコンシューマがトピックのすべてのメッセージを消費できます。不要なメッセージがある場合は、コンシューマ自身が判断して破棄することしかできません。RabbitMQ は、Exchange を通じてメッセージを受信し、コンシューマーによる消費のために指定さ
れたルールに従って特定のキューにメッセージを転送します。不必要なコンシューマへのメッセージの配信を避けるために多くのルートを設定しますが、RabbitMQ はキューを介したメッセージの直接受信と配信もサポートしています。 - パフォーマンスの点では、RabbitMQ はシングルスレッド モデルであり、ビッグ データにはボトルネックが存在しますが、Kafka はほぼ無限に拡張できます。
- 順序性: トピックの各パーティションについて、コンシューマが 1 つだけ存在するため、Kafka はメッセージの順序性を保証できます。パーティションが異なると順序性は保証できません。コンシューマが複数ある場合、RabbitMQ はメッセージを均等に分散します。順序は保証できません
。メッセージの消費が再配信されなかった場合、メッセージの順序も破棄されます。
3. Kafka のベスト プラクティス
1.プロデューサーの設定
-
プロデューサーが送信するとき、次のように説明される acks 構成があります。
- 0 の場合、プロデューサーはメッセージを送信した後、ボーカーの応答を待たずに成功を返し、最高のパフォーマンスと最高のデータ損失確率を実現します。
- 1 の場合、プロデューサーがメッセージを送信した後、リーダー ノードは成功した場合でも成功を返しますが、リーダーは他のレプリカと同期する前にハングアップし、データは失われます。
- all または -1 の場合、データが失われないように、すべてのレプリカが正常に同期されるまで待機してから成功を返す必要がありますが、パフォーマンスは最低になります。
運用環境では、-1 を構成することをお勧めします。他の 2 つの構成ではデータが失われる可能性があります。
-
min.insync.replicas 必要なレプリカの最小数。デフォルト値は 1 ですが、構成が 1 の場合は 2にすることをお勧めします (もちろん、各トピックのレプリカの数は 3 つ以上である必要があります)。、リーダーがデータを受信すると、同期される前に失敗し、データが失われます。 -
retries: 再試行の回数。より大きな値に設定されます。デフォルト値は、
Integer.MAX_VALUE
送信が確実に成功するようにするためのものです。
注: デフォルトでは再試行回数は多くなっていますが、再試行は別の時間設定 (delivery.timeout.ms
デフォルトでは 2 分) の影響も受けます。再試行回数が使い果たされない場合、タイムアウトが経過し、送信が中断されます。
さらに、大規模な再試行を設定する場合は、同期操作によって引き起こされるスレッドのブロック、ユーザー エクスペリエンスへの影響、またはその他のビジネス上の問題を避けるために、非同期メッセージ送信を使用してください。 -
構成リファレンス:
spring:
kafka:
producer:
bootstrap-servers: 10.1.1.1:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
retries: 10000
properties:
delivery.timeout.ms: 2000 # 发送消息上报成功或失败的最大时间,默认120000,两分钟
linger.ms: 0 # 生产者把数据组合到一个批处理进行请求的最大延迟时间,默认0
# 参考 https://cwiki.apache.org/confluence/display/KAFKA/KIP-19+-+Add+a+request+timeout+to+NetworkClient
request.timeout.ms: 1000 # 批处理就绪后到响应的等待时长,含网络+服务器复制时间
batch.size: 1000
2. コンシューマー構成
- メッセージの損失を避けるために、コンシューマーは手動確認を有効にし、メッセージのビジネス ロジックの処理が完了した後にオフセットを送信する必要があります。
- 無限ループの問題については後述しますが、String デシリアライザを使用することをお勧めします。
- ビジネス状況に応じて、適切なバッチ プル数量を構成します
max-poll-records
。デフォルト値は 500 です。 - ビジネス状況に応じて、適切な
auto-offset-reset
値を設定します。デフォルト値は最新です。- 最新: コンシューマーがトピックのパーティションを消費するときに、以前の消費レコード (以前に送信されたオフセット) がない場合は、最新のニュースのみが取得され、履歴メッセージは無視されます。
- 最も早い:最新とは逆に、過去の消費履歴がない場合、最も古いメッセージから処理を開始します。
- none: 以前の消費記録がない場合、例外がスローされます。
- 構成リファレンス:
spring:
kafka:
consumer:
bootstrap-servers: 10.1.1.1:9092
max-poll-records: 100
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
auto-offset-reset: latest
listener:
type: batch
ack-mode: manual_immediate
3. その他
- 接続障害の原因となる単一ノード障害を回避するために、複数のブートストラップ サーバー URL を構成します。
- メッセージが非同期で送信される場合は、kafkaTemplate の成功コールバック メソッドと失敗コールバック メソッドにあまりにも多くのビジネス ロジックを含めないでください。コールバック メソッドはシングルスレッドであり、内部のビジネス ロジックがタイムアウト構成を占有するため、後続のメッセージが発生する可能性があります。送信がタイムアウトになりました
delivery.timeout.ms
。 - 同様に、コンシューマもシングルスレッドであるため、コンシューマロジックが重すぎると
session.timeout.ms
タイムアウトが発生し、コンシューマがオフラインとみなされ、問題が発生する可能性があります。
4. Kafkaツールの紹介
1. グラフィカルツール
推奨OffsetExplorer
、ダウンロードアドレス: https://www.kafkatool.com/download.html
2. コマンドラインツール
Kafka インストール パッケージには、Kafka のステータスを簡単にクエリできる多くの組み込みスクリプト ツールが含まれており、これらのツールはダウンロードするだけで、インストールせずに使用できます。
- ダウンロードアドレス: https://kafka.apache.org/downloads
ダウンロード後、解凍すると、linux で使用する bin ディレクトリに sh ファイルが多数ありますが、
Windows で使用する場合は、bin\windows\ Bat ファイルにあるものを使用してください。
以下では例として Windows の Bat ファイル コマンドを使用します (Linux は対応する sh ファイルで実行できます) - 使用方法については、公式ドキュメントを参照してください: https://kafka.apache.org/documentation/
特定のコンシューマ グループに属するコンシューマと、トピックに関するこれらのコンシューマの消費ステータスをクエリします。
d:\kafka_2.13-3.4.0\bin\windows\kafka-consumer-groups.bat --describe --group=cb_consumers --bootstrap-server=10.0.0.1:9092
フィールドの説明:
- GROUP 消費者グループ
- TOPIC 消費の話題
- PARTITION 消費パーティション
- CURRENT-OFFSET 現在消費されているメッセージ オフセット
- LOG-END-OFFSET 現在のパーティションの最大メッセージ オフセット
- LAG ラグメッセージ数
- CONSUMER-ID コンシューマ ID
- HOST コンシューマが存在するホスト
- CLIENT-ID クライアント ID
注: LAG は単に と理解できますLOG-END-OFFSET 减 CURRENT-OFFSET
が、実際にはLAG=HW 减 CURRENT-OFFSET
4番目、springbootプロジェクトの使用
1. プロデューサーのデモ コード:
1.1. pom 依存関係を追加します。
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
1.2. application.yml 設定を追加します。
spring:
kafka:
producer:
bootstrap-servers: 10.1.1.1:9092
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
retries: 2 # 失败重发次数
1.3. Java 送信コード:
private final KafkaTemplate kafkaTemplate; // 注入的Bean
// 同步发送消息
String topic = "beinetTest111";
Object result = kafkaTemplate.send(topic, "我是key", objData).get();
2. 消費者向けデモ コード:
2.1. pom 依存関係を追加します。
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
2.2. application.yml 設定を追加します。
spring:
kafka:
consumer:
bootstrap-servers: 10.1.1.1:9092
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
listener:
type: batch
ack-mode: manual_immediate
2.3. Java 送信コード:
@KafkaListener(topics = "${kafka-topic.reports}")
public void consumerCreateTask(List<ConsumerRecord<String, Object>> consumerRecordList, Acknowledgment ack) {
if (consumerRecordList == null || consumerRecordList.size() <= 0)
return;
long start = System.nanoTime();
ConsumerRecord lastRecord = consumerRecordList.get(0);
try {
// 转换dto,并进行业务逻辑处理
long elapsedTime = System.nanoTime() - start;
log.debug("Topic:{} 分区:{} 偏移:{} 条数:{} 耗时:{}ns",
lastRecord.topic(),
lastRecord.partition(),
lastRecord.offset(),
dtos.size(),
elapsedTime);
} catch (Exception exp) {
long elapsedTime = System.nanoTime() - start;
log.error("Topic:{} 分区:{} 偏移:{} 耗时:{}ns 出错:",
lastRecord.topic(),
lastRecord.partition(),
lastRecord.offset(),
elapsedTime,
exp);
} finally {
// 不论成败,都提交,避免出错导致死循环,避免丢消息的逻辑,可以在catch里备份
ack.acknowledge();
}
}
5、カフカによくある問題
1. 複数のコンシューマが存在しますが、メッセージ データを取得できないコンシューマが常に 1 つ存在します。
コンシューマ・グループの場合、トピックに複数のパーティションがある場合、最大で複数のコンシューマを受け入れることができます。
たとえば、トピックに 2 つのパーティションがある場合、各パーティションはグループ内の 1 つのコンシューマにのみ割り当てることができ、コンシューマは 2 つだけですグループ内に最大 3 人のコンシューマがいる場合、アイドル状態で何もする必要のないコンシューマが 1 人存在する必要があります。
トピックに 2 つのパーティションがあるが、グループ内にコンシューマが 1 つしかない場合、2 つのパーティションのメッセージがこのコンシューマに配信されます。
2. このトピックのパーティション割り当て戦略は何ですか?
トピックに複数のパーティションと複数のコンシューマーがある場合、Kafka のソース コード実装には次のパーティション割り当て戦略があります。
- 範囲戦略 (デフォルト戦略):
現在のコンシューマー グループによって消費される各トピックのすべてのパーティションが、コンシューマーに 1 つずつ割り当てられます。各トピックは個別に処理されるため、不均衡が生じることに注意してください。
例: トピック a には 3 つのパーティション a0/a1/a2 があり、トピック b には 3 つのパーティション b0/b1/b2 があり、2 つのコンシューマー C0/C1 があります。分散プロセスは大まかに次のとおりです: ステップ 1 トピック a の割り当て: a0-> C0
、 a1->C1、a2->C0
ステップ 2 トピック b を割り当てます: b0->C0、b1->C1、b2->C0 [コンシューマー C0 は 4 つのパーティションにデータを維持する必要があり、C1 は次のとおりであることが
わかります。2 つのパーティションのデータは維持されます]、明らかな不均衡の問題があります。 - ラウンドロビン戦略:
すべてのパーティションを並べ替えた後、ラウンドロビン方式ですべてのコンシューマーに 1 つずつ割り当てます。
たとえば、トピック a には 3 つのパーティション a0/a1/a2 があり、トピック b には 3 つのパーティション b0/b1/b2 があります。 2 つのコンシューマー C0/C1 があり、割り当てプロセスは大まかに次のとおりです。
ステップ 1 トピック a を割り当てます: a0->C0、a1->C1、a2->C0
ステップ 2 トピック b を割り当てます: b0->C1、b1->C0 、 b2->C1
注: 2 番目のステップは最初から開始するのではなく、最初のステップの後に割り当てを継続するため、Range スキームの不均衡の問題は除外され、最終的な割り当て結果は [ 2 つの消費者、それぞれの責任者] になり
ます。 3パーティション用】。
ただし、2 人の消費者が消費するテーマが部分的にのみ交差し、完全に同じでない場合は、依然として不均衡が生じます。
代わりにこの戦略を使用する場合は、現在構成方法がありません。コード内のプロパティを変更する必要がありますpartition.assignment.strategy
。コードを参照してください。
@Configuration
@RequiredArgsConstructor
public class KafkaConfiguration {
private final KafkaProperties kafkaProperties;
private final ConcurrentKafkaListenerContainerFactory<String, Object> kafkaFactory;
@Bean("myKafkaFactory")
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, Object>> batchFactory() {
Map<String, Object> props = kafkaProperties.buildConsumerProperties();
props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, "org.apache.kafka.clients.consumer.RoundRobinAssignor");
kafkaFactory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(props));
return kafkaFactory;
}
}
次に、コンシューマ コードでこのファクトリ Bean の使用を指定します。
@KafkaListener(id = "beinetHandler1", groupId = "beinetGroup", topicPattern = "beinetTest.*",
containerFactory = "myKafkaFactory")
public void msgHandler(List<ConsumerRecord> message, Acknowledgment ack) {
- kafka のソース コードには他にも 2 つの実装がありますが、この記事では詳しくは紹介しません。
org.apache.kafka.clients.consumer.CooperativeStickyAssignor
org.apache.kafka.clients.consumer.StickyAssignor
3. コンシューマが参加または終了しても、メッセージは通常どおりに消費できますか?
結論: コンシューマーが生き残っている限り、すべてのメッセージは通常どおりに使用できます。
新しいコンシューマがグループに参加するか、グループ内のコンシューマがオフライン/終了すると、すべてのコンシューマにパーティションを再割り当てするコンシューマのリバランス アクションがトリガーされます。
リバランスが発生すると、デフォルトでは、割り当てが完了するまですべてのコンシューマ ジョブが停止します。
4. ある兄弟は、メッセージは Kafka に書き込まれたが、コンシューマー側にはデータが保存されていないと言いました。
- まず、Kafka にニュースがあることを確認し、上記のグラフィカル ツールのオフセット エクスプローラーを使用して、対応するトピックのデータを検索し、実際にデータがあることを確認します。
- ツールで、Consumers の下の対応するグループを確認すると、ラグが 0 であることがわかり、メッセージが正常に消費されたことを示します。
- コンシューマのアプリケーション ログを確認します。消費ログは生成されません。
- コンシューマのアプリケーション ログを引き続き確認し、次のログを見つけます。
cb_consumers: partitions assigned: []
これは、コンシューマがパーティションに割り当てられておらず、動作していないことを意味します。
予備的な判断は、誰かが別の場所でコンシューマを開始してデータを消費したかどうかです。 - Offset Explorer ツールはコンシューマ IP 情報を表示できません。上記のコマンドを使用して
kafka-consumer-groups.bat
コンシューマ IP を表示し、
運用と保守を検索して IP が誰であるかを確認することしかできません。 - 最終的にはテスト環境の構成が間違っていたことが判明し、開発環境のデータが消費されてしまいました。
5. 逆シリアル化に失敗し、コンシューマーの無限ループが発生する
ある日、テスト環境に公開したところ、プログラムの開始後に次の例外が常にスローされ、それが中断されることなく数十分続いたことがわかりました。
<#6d8d6458> j.l.IllegalStateException: No type information in headers and no default type provided
at o.s.util.Assert.state(Assert.java:76)
at o.s.k.s.s.JsonDeserializer.deserialize(JsonDeserializer.java:535)
at o.a.k.c.c.i.Fetcher.parseRecord(Fetcher.java:1387)
at o.a.k.c.c.i.Fetcher.access$3400(Fetcher.java:133)
at o.a.k.c.c.i.Fetcher$CompletedFetch.fetchRecords(Fetcher.java:1618)
at o.a.k.c.c.i.Fetcher$CompletedFetch.access$1700(Fetcher.java:1454)
at o.a.k.c.c.i.Fetcher.fetchRecords(Fetcher.java:687)
at o.a.k.c.c.i.Fetcher.fetchedRecords(Fetcher.java:638)
at o.a.k.c.c.KafkaConsumer.pollForFetches(KafkaConsumer.java:1272)
at o.a.k.c.c.KafkaConsumer.poll(KafkaConsumer.java:1233)
at o.a.k.c.c.KafkaConsumer.poll(KafkaConsumer.java:1206)
at j.i.r.GeneratedMethodAccessor109.invoke(Unknown Source)
at j.i.r.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at j.l.reflect.Method.invoke(Unknown Source)
at o.s.a.s.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at o.s.a.f.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at c.s.proxy.$Proxy186.poll(Unknown Source)
at o.s.k.l.KafkaMessageListenerContainer$ListenerConsumer.doPoll(KafkaMessageListenerContainer.java:1413)
at o.s.k.l.KafkaMessageListenerContainer$ListenerConsumer.pollAndInvoke(KafkaMessageListenerContainer.java:1250)
at o.s.k.l.KafkaMessageListenerContainer$ListenerConsumer.run(KafkaMessageListenerContainer.java:1162)
at j.u.c.Executors$RunnableAdapter.call(Unknown Source)
at j.u.c.FutureTask.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Googleで検索したところ、逆シリアル化で型情報が見つからないため、JSON逆シリアル化を使用しないことをお勧めします。
構成変更レコードを確認し、実際に Kafka 逆シリアル化構成の変更を追加しました。
spring:
kafka:
producer:
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
spring.consumer.value-deserializer を次のように変更してorg.apache.kafka.common.serialization.StringDeserializer
復元します。
それを理解した後、私の同僚はコンシューマのメソッドパラメータで文字列の代わりにオブジェクトを直接使用したいと考えているため、この変更を加えました。
そして、たまたま間違いを犯したコンシューマは、型情報を含まない他のプロジェクトによって生成されたメッセージを消費します。
そして、この例外は Spring の最下層でスローされ、ビジネス コードでの try キャプチャが実行できず、同時にコードが手動で ack を送信するように設定されているため、コードが無限ループに陥ります。
このような問題を回避するには、StringDeserializer を使用してデシリアライズすることをお勧めします。コード内でデシリアライズを行うことをお勧めします。
6. ブローカーの単一の障害により、消費者はオフセットを送信できなくなりました
実稼働環境では、パフォーマンスとフェイルオーバーのために 6 つのブローカーがデプロイされました。ある日、ブローカーが失敗してオフラインになりました。自動的に切り替えられるはずです。実際、すべてのコンシューマーが例外をスローし始めました。「同期グループ中にグループ割り当てを保存するときにエラーが発生しました
」ブローカーを手動で復元してオンラインにすると、障害は回復しません。
最終的な調査結果:
- O&M は、kafka の内部トピックを構成します:
__consumer_offsets
レプリカの数を 2、 - 同時に構成します
min.insync.replicas=2
。この構成の意味は、ISR リストの同期コピーの最小数が 2 つ以上である必要があることです。 - 障害が原因でオフラインになるブローカーには、トピックのコピーが 1 つだけ含まれており、結果としてトピックのコピーが 1 つだけになり、構成要件を
__consumer_offsets
満たさないため、動作が停止します。min.insync.replicas=2
- トピックの役割:
__consumer_offsets
すべてのコンシューマ グループの消費オフセットを受信して保存することです。このトピックが機能しない場合、コンシューマはオフセットを送信できず、その結果、データが異常に消費され、繰り返し消費されます。
問題を認識し、調整は__consumer_offsets
トピックのコピー数を 3 に調整することです (デフォルト値は 3 です。運用と保守は修正されました)
7. すべてのコンシューマはメッセージを消費しません
コンシューマが最初に起動してからトピックを作成すると、コンシューマはデータを消費できなくなります。コンシューマを再起動してみてください。
8. 春の Kafka にはスレッド セーフティの問題がありますか?
プロデューサが使用する KafkaTemplate はスレッドセーフであり、テスト後は同じスレッドを使用してメッセージが送信されます。
同様に、コンシューマもスレッドセーフであり、各コンシューマも受信したすべてのメッセージを単一のスレッドで処理します。
9. Kafka メッセージの輻輳に対処する方法
- メッセージが重要でない場合は、トピックを直接削除して再構築すると、トピックの下にあるすべてのメッセージが自然に消えます。トピックを再構築してからコンシューマを再起動する必要があることに注意してください。
- すべてのメッセージを消費する必要がある場合、kafka のパーティショニング機能に基づいて、各パーティションにはコンシューマが 1 つしか存在できないため、コンシューマを追加するだけでは問題を解決できません。
- コンシューマに異常がないかを確認します。開発者によってはコンシューマが正常であるかのように異常を鵜呑みにする場合があるので注意してください。ビジネスデータが増加し続けているかどうかを判断できます。コンシューマに異常がある場合は、バグを修正してください。
- 消費が正常であれば、バースト メッセージのデータが増加しているかどうかを確認します。簡単な判断は、トピックの LAG が通常の速度で減少し続けているかどうかであり、数分間観察します。
- ニュースが正常に落ちない場合は、基本的には消費スピードが遅いと判断し、
- まず、ツールを使用して、トピックの各パーティションの LAG を確認します。特定のパーティションの LAG が特に高く、他のパーティションが正常 (ブロックされていない) の場合、メッセージの分散がアンバランスになっているはずです。このパーティションには大量のメッセージが含まれています。すべてのパーティション内のメッセージ数のバランスが保たれるように、プロデューサーのメッセージ キーを調整することを検討してください。
- メッセージのタイミングに特別な要件がない場合は、コード内のスレッド プールを介してメッセージを非同期に処理でき、oom が適用されないようにスレッド プールの数の制御に注意することができます。
- いくつかのパーティションとさらにいくつかのコンシューマを追加することを検討してください。これにより、新しく生成されたメッセージが別のパーティションに再配布され、古いコンシューマへの負担が軽減されます。
- ブロックされたメッセージについては、別の一時トピックに新しいコンシューマを追加することを検討し、高速に消費するために数倍のパーティションとコンシューマを一時トピックに追加します。ダウンストリームやデータベースに影響を与えて他の問題を引き起こさないように注意してください。