01冪等とても重要
カフカの分散としてMQ、大規模な分散システムは、そのようなプッシュメッセージシステム、サービスプラットフォームシステム(例えば、課金プラットフォームなど)、それは、課金を取ったデータがある場合は、トラフィックデータは、プラットフォーム決済の上流側を襲ったとして、計算は、数回の処理、結果は特に深刻になります。
冪等に影響を与えるどのような要因02
カフカ、まさにワンスセマンティクスを確保する必要性を使用している場合。プロデューサーは、繰り返し再試行が発生しないときことを保証するために、どのように、応答ACKでカフカブローカー、ネットワーク障害やフルGC原因ACKタイムアウトは、プロデューサーが再発行される場合には、分散システムでは、ネットワークパーティションの出現は、避けられないことを知っているために、または順序のうち?またはプロデューサをハング、新しいプロデューサーが冪等を確保するために、どのようにこの時間、状態データの古いプロデューサーではないでしょうか?カフカも、消費者は、スレッドプールの労働者に、メッセージを取った後、メッセージを引っ張られ、メッセージ処理スレッドの労働者が非同期操作を含むことができ、次のことが発生します、メッセージの冪等を送信会いました:
- まずコミットし、ビジネスロジックを実行します。成功した送信に失敗しました。損失が発生
- ビジネスロジックの最初の実装は、その後、コミット:失敗し、実行成功をコミットします。リピート原因
- その後、ビジネスロジックの最初の実装、コミット:コミットは、非同期実行が失敗、成功します。損失が発生
この記事では、上記の問題を解決するために説明します
03カフカは、配信冪等を保証しました
上記の問題は、新しい電力および他のトランザクション型プロデューサ及びプロデューサの0.11バージョンでカフカを解決します。マルチセッション冪等を解決するなど、電力の問題を解決するために旧シングルセッション。
シングルセッション冪等
プロデューサーによって引き起こされる障害を解決し、再試行が繰り返されます。カフカは、PIDおよび配列を追加します。各PID-seqのブローカーはまた、マッピングを維持上のTP、すべてがlastSeqをコミット更新され、各RecordBatchプロデューサーは単調に増加する配列を持っています。シリアル番号baseSeq(最初のメッセージの配列)もしブローカーのメンテナンス(lastSeq)バッチ大きなものよりも、そのデータを保存するか、または(inSequence法)を保存しない:このようrecordBatch到着で、ブローカーは、データを保存しRecordBatchをチェックします。
ProducerStateManager.scala
private def maybeValidateAppend(producerEpoch: Short, firstSeq: Int, offset: Long): Unit = {
validationType match {
case ValidationType.None =>
case ValidationType.EpochOnly =>
checkProducerEpoch(producerEpoch, offset)
case ValidationType.Full =>
checkProducerEpoch(producerEpoch, offset)
checkSequence(producerEpoch, firstSeq, offset)
}
}
private def checkSequence(producerEpoch: Short, appendFirstSeq: Int, offset: Long): Unit = {
if (producerEpoch != updatedEntry.producerEpoch) {
if (appendFirstSeq != 0) {
if (updatedEntry.producerEpoch != RecordBatch.NO_PRODUCER_EPOCH) {
throw new OutOfOrderSequenceException(s"Invalid sequence number for new epoch at offset $offset in " +
s"partition $topicPartition: $producerEpoch (request epoch), $appendFirstSeq (seq. number)")
} else {
throw new UnknownProducerIdException(s"Found no record of producerId=$producerId on the broker at offset $offset" +
s"in partition $topicPartition. It is possible that the last message with the producerId=$producerId has " +
"been removed due to hitting the retention limit.")
}
}
} else {
val currentLastSeq = if (!updatedEntry.isEmpty)
updatedEntry.lastSeq
else if (producerEpoch == currentEntry.producerEpoch)
currentEntry.lastSeq
else
RecordBatch.NO_SEQUENCE
if (currentLastSeq == RecordBatch.NO_SEQUENCE && appendFirstSeq != 0) {
// We have a matching epoch, but we do not know the next sequence number. This case can happen if
// only a transaction marker is left in the log for this producer. We treat this as an unknown
// producer id error, so that the producer can check the log start offset for truncation and reset
// the sequence number. Note that this check follows the fencing check, so the marker still fences
// old producers even if it cannot determine our next expected sequence number.
throw new UnknownProducerIdException(s"Local producer state matches expected epoch $producerEpoch " +
s"for producerId=$producerId at offset $offset in partition $topicPartition, but the next expected " +
"sequence number is not known.")
} else if (!inSequence(currentLastSeq, appendFirstSeq)) {
throw new OutOfOrderSequenceException(s"Out of order sequence number for producerId $producerId at " +
s"offset $offset in partition $topicPartition: $appendFirstSeq (incoming seq. number), " +
s"$currentLastSeq (current end sequence number)")
}
}
}
private def inSequence(lastSeq: Int, nextSeq: Int): Boolean = {
nextSeq == lastSeq + 1L || (nextSeq == 0 && lastSeq == Int.MaxValue)
}
拡張:取引を行っていたものを注文するのカフカプロデューサー
私たちは5つの要求、batch1、BATCH2、batch3、batch4、batch5があるとし、唯一BATCH2 ACKが失敗した場合には、3,4,5が保存され、次のバッチ2の再送を繰り返し発生しているになります。我々は、障害に対処するために、(クライアントの要求に対する非応答の数は、単一の接続を介して送信することができる)max.in.flight.requests.per.connection = 1を設定するが、システムのスループットを低下させることができます。
動的MAX-で飛行-要求カフカが真enable.idempotence =を提供する新しいバージョンを調整することができます。通常、1より大きいmax.in.flight.requests.per.connection。それの前にバッチ番号よりも小さくなるように配列バッチによる要求と再試行の到着は、キュー内の適切な場所に戻って加え、1〜max.in.flight.requests.per.connectionされる場合、最初の上で行われ、それが送ることができます。
private void insertInSequenceOrder(Deque<ProducerBatch> deque, ProducerBatch batch) {
// When we are requeing and have enabled idempotence, the reenqueued batch must always have a sequence.
if (batch.baseSequence() == RecordBatch.NO_SEQUENCE)
throw new IllegalStateException("Trying to re-enqueue a batch which doesn't have a sequence even " +
"though idempotency is enabled.");
if (transactionManager.nextBatchBySequence(batch.topicPartition) == null)
throw new IllegalStateException("We are re-enqueueing a batch which is not tracked as part of the in flight " +
"requests. batch.topicPartition: " + batch.topicPartition + "; batch.baseSequence: " + batch.baseSequence());
ProducerBatch firstBatchInQueue = deque.peekFirst();
if (firstBatchInQueue != null && firstBatchInQueue.hasSequence() && firstBatchInQueue.baseSequence() < batch.baseSequence()) {
List<ProducerBatch> orderedBatches = new ArrayList<>();
while (deque.peekFirst() != null && deque.peekFirst().hasSequence() && deque.peekFirst().baseSequence() < batch.baseSequence())
orderedBatches.add(deque.pollFirst());
log.debug("Reordered incoming batch with sequence {} for partition {}. It was placed in the queue at " +
"position {}", batch.baseSequence(), batch.topicPartition, orderedBatches.size());
// Either we have reached a point where there are batches without a sequence (ie. never been drained
// and are hence in order by default), or the batch at the front of the queue has a sequence greater
// than the incoming batch. This is the right place to add the incoming batch.
deque.addFirst(batch);
// Now we have to re insert the previously queued batches in the right order.
for (int i = orderedBatches.size() - 1; i >= 0; --i) {
deque.addFirst(orderedBatches.get(i));
}
// At this point, the incoming batch has been queued in the correct place according to its sequence.
} else {
deque.addFirst(batch);
}
}
マルチセッション冪等
説明した単一セッション冪等では、シングルセッションカフカは、冪等とSEQのPIDを導入することによって達成するが、アプリケーションの再起動は、新しいプロデューサがプロデューサステータスデータ旧でない場合には、PIDを導入します。保存繰り返します。
機構を単離することによってマルチセッション冪等を実装するカフカトランザクション
カフカ総務はのtransactionIdとエポックを導入し、transactional.idを設定した後、のtransactionIdは、唯一のPIDに対応し、サーバー側では、最新のエポック値を記録します。このように、新しいプロデューサーの初期化があり、TransactionCoordinator InitPIDRequest要求を送信し、TransactionCoordinatorは、PIDが復帰する前に割り当てられたメタに対応する、こののtransactionIdを持って、古い生産再開操作が要求が来たときにときように、1つインクリメントさエポックに戻り、これは、無効なプロデューサースローとみなされます。何も開いているトランザクションがない場合、TransactionCoordinatorしたがって、できないマルチセッション冪等新しいプロデューサー新しいPIDを持つ戻りますので、分離を達成できないだろう、と。
private def maybeValidateAppend(producerEpoch: Short, firstSeq: Int, offset: Long): Unit = {
validationType match {
case ValidationType.None =>
case ValidationType.EpochOnly =>
checkProducerEpoch(producerEpoch, offset)
case ValidationType.Full => //开始事务,执行这个判断
checkProducerEpoch(producerEpoch, offset)
checkSequence(producerEpoch, firstSeq, offset)
}
}
private def checkProducerEpoch(producerEpoch: Short, offset: Long): Unit = {
if (producerEpoch < updatedEntry.producerEpoch) {
throw new ProducerFencedException(s"Producer's epoch at offset $offset is no longer valid in " +
s"partition $topicPartition: $producerEpoch (request epoch), ${updatedEntry.producerEpoch} (current epoch)")
}
}
04消費者エンド冪等
上述したように、スレッドプールの労働者に消費者プルメッセージ、メッセージに取った後、メッセージの労働者ハンドルが非同期動作を含んでいてもよい、以下が発生します。
- まずコミットし、ビジネスロジックを実行します。成功した送信に失敗しました。損失が発生
- ビジネスロジックの最初の実装は、その後、コミット:失敗し、実行成功をコミットします。リピート原因
- その後、ビジネスロジックの最初の実装、コミット:コミットは、非同期実行が失敗、成功します。損失が発生
我々はこの方法を使用すると、以下のコードを実行するメッセージを取得するために動作します:
if(cache.contain(msgId)){
// cache中包含msgId,已经处理过
continue;
}else {
lock.lock();
cache.put(msgId,timeout);
commitSync();
lock.unLock();
}
// 后续完成所有操作后,删除cache中的msgId,只要msgId存在cache中,就认为已经处理过。Note:需要给cache设置有消息
あなたは私の記事のような場合は、2次元コードを押してください、ジンはただの学生に焦点を当て
あなたの転送は、私にとって最大のサポートです、ありがとう!