Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Spark Structured Streaming with Kafka

274 views

Published on

Hadoopソースコードリーディング 第24回での発表資料

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Spark Structured Streaming with Kafka

  1. 1. Spark Structured Streaming with Kafka Hadoopソースコードリーディング 第24回 Kimura, Sotaro(@kimutansk)
  2. 2. 自己紹介 • Kimura, Sotaro(@kimutansk) – データエンジニア雑用係 @ ドワンゴ • オンプレ~クラウド、インフラ~個別機能 • バッチ~ストリーム、開発~プロマネ – すなわち雑用係 – 好きな技術分野 • ストリーム処理(主にJVM上) • 分散システム – ストリーム処理で最近やらかした失敗 • Spark StreamingをSSH接続>起動して 実行ツールのセッション切れてログロスト
  3. 3. 本資料の前提 • 本資料の内容は Logical Planの流れを確認して作成しているため、 Physical Planに変換した場合に成り立たないケース もあります。 – Logical > Physicalへの変換について詳しい方が いらっしゃったら適宜補足をいただけると・・・
  4. 4. アジェンダ • Spark Structured Streamingとは? • Spark Structured Streamingの用語 • Spark Strucutred Streamingの実行の流れ • Kakfa用コンポーネントの構成
  5. 5. Spark Strucutred Streamingとは?
  6. 6. Spark Structured Streamingとは? • Spark SQL上でストリーム処理アプリケーションを 簡単に組むためのコンポーネント – Spark2.2系でProduction Ready! – バッチ処理と同様の方法でストリーム処理を記述可能 • バッチ処理で読み込んだデータとストリームのJoinも可能! – Scala/Java/PythonのDataset/DataFrame APIで記述 – Dataset/DataFrameを用いることで 構造化データとして最適化された状態で動作 • メモリ使用量の節約 • ベクトル演算によるCPUリソースの有効活用
  7. 7. Spark Structured Streamingとは? • 注意点 – Spark Streamingと同様マイクロバッチ方式であり、 レコード単位で処理するストリーム処理ではない。 – Sparkの新実行エンジンDrizzleとは独立した別の機能 • https://github.com/amplab/drizzle-spark – Spark2.3.0で継続的に実行する方式についての提案も 挙がっているが、現状の進み具合から おそらくSpark2.3.0では無理? • [SPARK-20928] Continuous Processing Mode for Structured Streaming
  8. 8. 簡単なアプリケーション例 // Sparkアプリケーション生成 val spark = SparkSession .builder .appName("StructuredNetworkWordCount") .getOrCreate() import spark.implicits._ // ローカルポート上にソケットを生成してデータを待ち受け val lines = spark.readStream .format("socket") .option("host", "localhost") .option("port", 9999) .load()
  9. 9. 簡単なアプリケーション例 // 入力データを単語毎に分割 val words = lines.as[String].flatMap(_.split(" ")) // 入力単語毎にカウント val wordCounts = words.groupBy("value").count() // 集計結果を毎回すべてコンソールに出力するStreamingQueryを生成 val streamingQuery = wordCounts.writeStream .outputMode("complete") // 出力モードを指定 .format("console") .start() // アプリケーションが外部から停止されるまで実行 streamingQuery.awaitTermination()
  10. 10. 簡単なアプリケーションイメージ https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html
  11. 11. Spark Strucutred Streamingの用語
  12. 12. 用語説明(アプリケーション例) • Source – データの入力元 • Sink – データの出力先 • Streaming Query – 1連の処理単位。出力先1つ、入力元1個以上持つ。 • Output Mode – Sinkへ出力する際の出力方式 • Append(結果を1回出力)/Complete(毎回全出力) Update(更新があった結果のみ出力)の3モードが存在
  13. 13. 用語説明(その他) • EventTime – データの時刻を示す値 通常はデータの特定カラムを用いるが、 処理タイミング(ProcessingTime)を用いることも可能 • Offset – Sourceのどこまでを処理したか? を示す概念 • Checkpoint – Structured Streamingの実行状態を保存する先 – Query毎にパスを指定し、その場所に保存
  14. 14. 用語説明(その他) • Trigger – マイクロバッチを実行するタイミング制御 1回実行と、定期実行の指定が可能 • Watermark – 遅れデータを除去するための機構 前回マイクロバッチ処理データのEventTimeが基準と • BatchId – マイクロバッチのシーケンス番号 初回が1で、1ずつインクリメントされる Checkpointから復旧した場合、引き継ぐ
  15. 15. Spark Strucutred Streamingの実行の流れ
  16. 16. 実行の流れ(前提) • Sparkは以下の流れで実行されており、 Spark Strucured Streamingも同様。 • マイクロバッチごとに以下が実行 – Code • アプリケーションコード – Logical Plan • アプリケーションコードから生成 • データの依存性グラフ – Physical Plan • Logical Planから生成 • 処理をStageに区切り、Partitionに分割してExecutor上で実行
  17. 17. 実行の流れ(概要) • マイクロバッチごとの処理は StreamExecutionクラスで起動 – StreamingQuery=Sink1個につき1インスタンス存在 – 各StreamingQueryの以下を実施 • 起動時の状態復旧 • Checkpointの管理 – Stateful Operatorの状態については除外 • マイクロバッチの定期起動 • Logical Planの生成 – Sourceの状態に応じたLogical Planの最適化も実施 • Watermarkの管理
  18. 18. StreamExecutionの管理する状態 • StreamExecutionでは以下のCheckpointを管理 – Metadata • 初回起動時に割り振られるQueryの識別子 – Offset • Sourceのどこまでを処理したかを管理する状態値 • Source毎に形式は異なる • Offsetは自前で管理 – BatchCommitLog • どのBatchまで終了したかを管理する状態値 – ※StatefulOperatorの状態は直接管理しないため除外
  19. 19. 実行の流れ(StreamExecution) • StreamExecution#runBatchesで以下が実行 • 起動時、前回の状態を復旧 • 以下繰り返し(Trigger設定次第では1回のみ) – 次のマイクロバッチのLogical Planを作成 – 新規データがSourceに存在する場合のみ、バッチ実行 – バッチ実行時、完了済みとCheckpointを更新 – BatchIdをインクリメント
  20. 20. 実行の流れ(詳細:状態復旧) • StreamExecution#populateStartOffsetsで以下が実行 – BatchCommitLog最終値+1を次実行するBatchIdとして読込 – OffsetLogの最終値を次のバッチの取得対象として読込 – OffsetLogの最終値-1をCommitしたOffsetとして読込 • 後述のように、 SourceへのCommitは「次のバッチ作成」時に行われるため、 「取得してない」か「取得したがCommitしてない」の扱い。 – OffsetLogの最終値=BatchCommitLogの最終値の場合 Sourceからデータの取得>破棄を実施。
  21. 21. 実行の流れ(詳細:状態復旧) • 「 Sourceからデータの取得>破棄」を行う理由 – OffsetLogの最終値=BatchCommitLogの最終値の場合、 Spark側として処理完了したものの、 Sourceに対してCommitせずに完了したことを表すため。 • 繰り返しになるが、SourceへのCommitはSparkとして 処理完了したタイミングとは異なるタイミングで実施。 – Source側がCommitされたから復旧したケースを 考慮していると思われる。 • その場合、そのまま実行すると復旧した範囲を無視して、 その次のデータを読み込んでCommitという流れとなるため。
  22. 22. 実行の流れ(詳細:次バッチ作成) • StreamExecution#constructNextBatchで以下が実行 – Sourceから最新のOffsetを取得 – 新規データが存在した場合、Watermarkを更新 – 最新OffsetをOffsetLogに書込 – 前回バッチで処理したOffsetまでをSourceにcommit • 前述のように、 「該当のバッチが完了したタイミング」ではなく、 次のバッチ構築のタイミングでCommitを行っている。 – (※何故こうしているかはまだ読み取れていません)
  23. 23. 実行の流れ(詳細:次バッチ作成) – Watermarkの更新 • 前回のバッチ実行時の最新EventTime値 - Delay値を基準 – 以下のようなコードで指定可能 – 出力されるOffset値の形式 • Sourceに依存する。 Sourceコンポーネント側で生成。 – Kafkaについては後述。 – 保存されるLogの世代 – 「spark.sql.streaming.minBatchesToRetain」で指定(Def:100) exampleDataFrame.withWatermark('eventTime', "10 minute") # eventTimeカラムを用いてWatermark設定、許容遅延を10分に設定
  24. 24. 実行の流れ(詳細:バッチ実行) • StreamExecution#runBatchで以下が実行 – 前回Offset~最新OffsetまでのデータをSourceから取得 • ただし、新規データが存在する場合のみ – 新規データ未存在Sourceを省くようLogical Planを更新 – 「QueryPlanning」フェーズとしてPlanを作成&更新 • StatefulOperatorのPlan生成はこのフェーズで実施 – Sourceを基に結果を取得 • DataSetとして取得しているため、実体はExecutor上のみ – Sinkに出力結果を出力
  25. 25. 実行の流れ(詳細:後処理) • StreamExecution#runBatchesで以下が実行 – BatchCommitLogに完了したBatchIdを追加 – BatchIdをインクリメント
  26. 26. 確認中のポイント • StatefulOperatorの状態の読込書込タイミング – Physical Plan上での具体的な実行プランが不明 • Source/SinkへのアクセスのPhysical Plan上への 落とし込んだ際の動作
  27. 27. Kakfa用コンポーネントの構成
  28. 28. Kafka用コンポーネント一覧 • 代表的なコンポーネント一覧 – KafkaSource – KafkaSink – KafkaSourceProvider • Spark Structured Streamingのformat指定時に紐付けるクラス – KafkaSourceOffset – KafkaOffsetReader • Logical Plan作成時にOffset取得に使用されるクラス
  29. 29. KafkaSource/Sinkのロードフロー • KafkaSourceProviderが「DataSourceRegister」の Serviceに登録されており、ServiceLoaderでロード – デフォルトではcsv/jdbc/json/parquet/console等が存在
  30. 30. Kafka用コンポーネント固有箇所 • KafkaSource – Offsetを基にデータを取得するRDDを作成 • DataFrame上のスキーマは以下 – key/value/topic/partition/offset/timestamp/timestampType • KafkaSink – DataFrameを用いてデータを書き込み • ※KafkaClientは「kafka-client-0.10系」を使用
  31. 31. Kafka用コンポーネント固有箇所 • KafkaSourceOffset – Topic-PartitionとOffsetのペアのリスト – CheckpointのOffsetLogとして読込書込が行われる。 • 内容例 v1 {"batchWatermarkMs":0,"batchTimestampMs":1506805200115,"conf":{"spark.sql.shuf fle.partitions":"200"}} {"common_event":{"2":318574460,"5":318572400,"4":318579256,"1":318600441,"3":318 558444,"6":318540065,"0":318576508}}
  32. 32. Kafka用コンポーネント固有箇所 • KafkaOffsetReader – KafkaのOffsetを取得 – 実行状態によって以下のパターンの取得を実施 • Latest – 次バッチ生成時の最新Offset取得 – 初回起動時に初期Offset設定が「latest」の場合 • Earliest – 初回起動時に初期Offset設定が「earliest」の場合
  33. 33. まとめ • Spark Strucured Streamingは Source/Sink/Offsetという形で 各コンポーネントを抽象化して使用 – コンポーネント毎の固有個所は意外に少ない • 上記はSourceRegisterに登録して適用 • Offsetは自前で管理しており、 KafkaのOffset管理の機構は未使用

×