web-dev-qa-db-ja.com

手動で信頼できるオフセット管理のための構造化クエリのKafkaオフセットを取得するには?

Spark 2.2では、Kafkaの構造化ストリーミングソースが導入されました。私が理解しているように、オフセットを保存し、「一度だけ」のメッセージ配信を保証するために、HDFSチェックポイントディレクトリに依存しています。

しかし、古いドック( https://blog.cloudera.com/blog/2017/06/offset-management-for-Apache-kafka-with-Apache-spark-streaming/ など)は、= Sparkストリーミングチェックポイントは、アプリケーション間またはSpark=アップグレードでは回復できないため、あまり信頼性が高くありません。解決策として、外部ストレージへのオフセットの保存をサポートする方法があります。 MySQLやRedshiftDBなどのトランザクションをサポートします。

KafkaソースからトランザクションDBへのオフセットを格納する場合、構造化ストリームバッチからオフセットを取得するにはどうすればよいですか?

以前は、RDDをHasOffsetRangesにキャストすることで実行できました。

_val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges    
_

しかし、新しいStreaming APIでは、DatasetInternalRowであり、オフセットを取得する簡単な方法が見つかりません。 Sink APIにはaddBatch(batchId: Long, data: DataFrame)メソッドしかありませんが、特定のバッチIDのオフセットを取得するにはどうすればよいですか?

22
dnaumenko

関連するSpark DEVメーリングリストディスカッションスレッドは here です。

それからの要約:

Spark Streamingは、将来のバージョン(2.2.0以降)でオフセットの取得をサポートします。フォローするJIRAチケット- https://issues-test.Apache.org/jira/browse/SPARK-18258

Spark <= 2.2.0の場合、チェックポイントディレクトリからjsonを読み取ることにより、指定されたバッチのオフセットを取得できます(APIは安定しないため、注意が必要です):

val checkpointRoot = // read 'checkpointLocation' from custom sink params
val checkpointDir = new Path(new Path(checkpointRoot), "offsets").toUri.toString
val offsetSeqLog = new OffsetSeqLog(sparkSession, checkpointDir)

val endOffset: Map[TopicPartition, Long] = offsetSeqLog.get(batchId).map { endOffset =>
  endOffset.offsets.filter(_.isDefined).map { str =>
    JsonUtilsWrapper.jsonToOffsets(str.get.json)
  }
}


/**
  * Hack to access private API
  * Put this class into org.Apache.spark.sql.kafka010 package
  */
object JsonUtilsWrapper {
  def offsetsToJson(partitionOffsets: Map[TopicPartition, Long]): String = {
    JsonUtils.partitionOffsets(partitionOffsets)
  }

  def jsonToOffsets(str: String): Map[TopicPartition, Long] = {
    JsonUtils.partitionOffsets(str)
  }
}

このendOffsetには、各トピック/パーティションのuntilオフセットが含まれます。開始オフセットの取得には問題があり、「コミット」チェックポイントdirを読み取る必要があります。ただし、通常、信頼性の高いSparkジョブの再起動には終了オフセットの保存で十分なので、開始オフセットは気にしません。

処理済みのバッチIDもストレージに保存する必要があることに注意してください。 Sparkは、場合によっては同じバッチIDで失敗したバッチを再実行できます。したがって、最新の処理済みバッチID(外部ストレージから読み取る必要があります)でカスタムシンクを初期化して、 id <latestProcessedBatchId。のバッチ。ただし、バッチIDはクエリ間で一意ではないため、クエリごとにバッチIDを個別に保存する必要があります。

4
dnaumenko

Spark 2.2では、Kafkaの構造化ストリーミングソースが導入されました。私が理解しているように、オフセットを保存し、「必ず1回」のメッセージ配信を保証するためにHDFSチェックポイントdirに依存しています。

正しい。

すべてのトリガーSpark Structured Streamingは、チェックポイントの場所のoffsetディレクトリ(checkpointLocationオプションまたはspark.sql.streaming.checkpointLocation Sparkプロパティまたはランダムに割り当てられた)オフセットが処理されることを保証することになっている最大1回。この機能はWrite Ahead Logsと呼ばれます。

チェックポイントの場所にあるもう1つのディレクトリは、バッチごとに1つのファイル(バッチIDであるファイル名)で完了したストリーミングバッチのcommitsディレクトリです。

Fault Tolerance Semantics の公式ドキュメントを引用:

これを実現するために、再起動や再処理によってあらゆる種類の障害を処理できるように、処理の正確な進行を確実に追跡するように構造化ストリーミングソース、シンク、実行エンジンを設計しました。すべてのストリーミングソースには、ストリーム内の読み取り位置を追跡するためのオフセット(Kafkaオフセット、またはKinesisシーケンス番号と同様)があると想定されます。エンジンは、チェックポイントと先書きログを使用してオフセット範囲を記録します。ストリーミングシンクは、再処理を処理するためにi等となるように設計されています。再生可能なソースとdem等シンクを使用して、構造化ストリーミングは、障害が発生した場合にエンドツーエンドの1回限りのセマンティクスを保証できます。

トリガーが実行されるたびに、StreamExecutionはディレクトリをチェックし、すでに処理されたオフセットを「計算」します。これにより、少なくとも1回のセマンティクスと完全に1回が得られます。

しかし、古いドキュメント(...)では、Spark=ストリーミングチェックポイントはアプリケーション間で回復できないか、Sparkアップグレードであるため、あまり信頼性が高くありません。

あなたがそれらを「古い」と呼ぶ理由がありました、はありませんでしたか?

彼らは古いものと(私の意見では)死んだものを指しますSparkオフセットだけでなく、チェックポイントがほとんど使用できない状況につながるクエリコード全体を保持したストリーミング、たとえばコードを変更したとき。

時代は終わりました。構造化ストリーミングでは、いつ、何時にチェックポイントが設定されるのか、より慎重になります。

KafkaソースからトランザクションDBへのオフセットを格納する場合、構造化ストリームバッチからオフセットを取得するにはどうすればよいですか?

解決策は、オフセットチェックポイントの処理に使用される MetadataLog インターフェイスを実装するか、何らかの方法で使用することです。 couldは機能します。

特定のバッチIDのオフセットを取得するにはどうすればよいですか?

現在は不可能です。

私の理解では、ストリーミングのセマンティクスがあなたから隠されているので、あなたはnotできません。単純にnotではなく、Spark Structured Streamingが提供するために使用するオフセットと呼ばれるこの低レベルの「もの」を処理する必要がありますちょうど一度保証。

Spark Summit Apache Sparkの構造化ストリーミングによる簡単でスケーラブルなフォールトトレラントストリーム処理 :での講演からMichael Armbrustを引用

あなたはストリーミングについて推論する必要はないはずです

および 講演中(次のスライド)

簡単なクエリを記述する必要があります&Spark


そこにisStreamingQueryProgressを使用してオフセット(任意のソースからKafkaを含む)を取得する方法 StreamingQueryListener およびonQueryProgressコールバックを使用してインターセプトできます。

onQueryProgress(event:QueryProgressEvent):Unit何らかのステータスの更新(取り込み率の更新など)がある場合に呼び出されます

StreamingQueryProgressを使用すると、 SourceProgresssourcesプロパティにアクセスできます。

36
Jacek Laskowski

Kafka sourceには field の1つとしてoffsetがあります。クエリ内のすべてのオフセットをクエリし、JDBC Sinkに保存できます]

1
T. Gawęda