リアルタイム処理エンジン
Gearpumpの紹介
2016/01/30
Kimura Sotaro(@kimutansk)
アジェンダ
1. Gearpumpとは?
2. 何故Gearpumpが必要なのか?
3. Gearpumpの特長
4. Gearpumpの構成
5. どうリアルタイム処理が組めるか?
1
1. Gearpumpとは?
• リアルタイムなデータ処理エンジン
• Scala製のApache License OSS
• Intelによって開発され、2014/7/23にOSS化
2
シンプルで強力な
メッセージレベルの
ストリーム処理Daemon
•Communication
•Concurrency
•Isolation
•Fault-tolerant
akkaベースで構築され、シンプル、高性能
1. Gearpumpとは?
• ビッグデータスタック上の位置づけ
3
DataStore
Execute
Engine
storm Here!
DSL /Query
Analytics
2. 何故Gearpumpが必要なのか?
• リアルタイムストリーム処理に求めたい性質
① Keep the data moving
② Query using StreamSQL
③ Handle stream imperfections
④ Generate predictable outcomes
⑤ Integrate stored and streaming data
⑥ Guarantee data safety and availabilit
⑦ Partition and scale applications automatically
⑧ Process and Respond Instantaneously
• 『Meet The 8 Requirements of Real-Time Stream Processing (2006)』
• 上記の性質を満たすストリーム処理基盤が必要
• Apache Flinkも同様の性質をもつ
だが、小規模のCheckpointに区切る方式
4
3. Gearpumpの特長
• 下記のような特長を持つ。
① 高スループット
② 低レイテンシ
③ メッセージの処理信頼性設定可能
(At least once / exactly once)※但し条件あり
④ 高拡張性
⑤ 動的DAG
5
4. Gearpumpの構成
• コンポーネントは全てakka Actorで構成
• アドレス解決をAkkaを用いることで
全体のアーキテクチャはシンプルに出来ている。
6
4. Gearpumpの構成
• 耐障害性もakka clusterで維持
• 親Actorは子Actorの状態を確認し、自動復旧
• 一番Rootとなる親はakka cluster / CRDTで冗長化
7
WorkerWorkerWorker
Master
standb
y
Master
Standb
y
Master
State
Gossi
p
CRDT Data type example:
leader
5. どうリアルタイム処理が組めるか?
• Gearpumpのアプリケーションは下記で構成
• 各ProcessorのActorコード
• ActorをDAGとして組み合わせてアプリケーションとして
デプロイするコード
8
例:WordCountを行うアプリケーション
Split Sum
単語ごとに集約
Sum.scala
Split.scala
HashPartitioner
(既存コンポーネント)
WordCount.scala
5. どうリアルタイム処理が組めるか?
• Split.scala
9
class Split(taskContext : TaskContext, conf: UserConfig) extends Task(taskContext, conf) {
import taskContext.{output, self}
// 1. 自分自身にStartメッセージを通知。
override def onStart(startTime : StartTime) : Unit = {
self ! Message("start")
}
// 2. 文章を単語に分割し、空文字を除去した上で下流に送信
override def onNext(msg : Message) : Unit = {
Split.TEXT_TO_SPLIT.lines.foreach { line =>
line.split("[¥¥s]+").filter(_.nonEmpty).foreach { msg =>
output(new Message(msg, System.currentTimeMillis()))
}
}
// 3. 次メッセージを自分に対して送信するタスクを仕掛ける
import scala.concurrent.duration._
taskContext.scheduleOnce(Duration(100, TimeUnit.MILLISECONDS))(self ! Message("continue",
System.currentTimeMillis()))
}
}
5. どうリアルタイム処理が組めるか?
• Sum.scala
10
class Sum (taskContext : TaskContext, conf: UserConfig) extends Task(taskContext, conf) {
private[wordcount] val map : mutable.HashMap[String, Long] = new mutable.HashMap[String, Long]()
private[wordcount] var wordCount : Long = 0
private var snapShotTime : Long = System.currentTimeMillis()
private var snapShotWordCount : Long = 0
private var scheduler : Cancellable = null
override def onStart(startTime : StartTime) : Unit = {
// 1. 起動時に状態出力タスクを仕掛ける。
scheduler = taskContext.schedule(new FiniteDuration(5, TimeUnit.SECONDS),
new FiniteDuration(30, TimeUnit.SECONDS))(reportWordCount)
}
override def onNext(msg : Message) : Unit = {
if (null == msg) {
return
}
// 2. 受信したメッセージから単語を取得し、総受信回数と単語ごとの受信回数をカウント
val current = map.getOrElse(msg.msg.asInstanceOf[String], 0L)
wordCount += 1
map.put(msg.msg.asInstanceOf[String], current + 1)
}
5. どうリアルタイム処理が組めるか?
• WordCount.scala
11
object WordCount extends AkkaApp with ArgumentsParser {
private val LOG: Logger = LogUtil.getLogger(getClass)
val RUN_FOR_EVER = -1
// 1. 起動時のCLIから読み込む項目と形式、注釈、必須/オプショナル、デフォルト値を定義
override val options: Array[(String, CLIOption[Any])] = Array(
"split" -> CLIOption[Int]("<how many split tasks>", required = false, defaultValue = Some(1)),
"sum" -> CLIOption[Int]("<how many sum tasks>", required = false, defaultValue = Some(1))
)
def application(config: ParseResult) : StreamApplication = {
// 2. CLIから読み込んだ設定項目を用いてProcessorを生成
val splitNum = config.getInt("split")
val sumNum = config.getInt("sum")
val split = Processor[Split](splitNum)
val sum = Processor[Sum](sumNum)
// 3. メッセージのProcessor間の割り振りを行うPartitionerを生成
val partitioner = new HashPartitioner
// 4. ProcessorとPartitionerを用いてDAGを作成
val app = StreamApplication("wordCount", Graph(split ~ partitioner ~> sum), UserConfig.empty)
app
}
非常に直観的にグラフが
組める!
Enjoy Gearpump!

リアルタイム処理エンジン Gearpumpの紹介

  • 1.
  • 2.
    アジェンダ 1. Gearpumpとは? 2. 何故Gearpumpが必要なのか? 3.Gearpumpの特長 4. Gearpumpの構成 5. どうリアルタイム処理が組めるか? 1
  • 3.
    1. Gearpumpとは? • リアルタイムなデータ処理エンジン •Scala製のApache License OSS • Intelによって開発され、2014/7/23にOSS化 2 シンプルで強力な メッセージレベルの ストリーム処理Daemon •Communication •Concurrency •Isolation •Fault-tolerant akkaベースで構築され、シンプル、高性能
  • 4.
  • 5.
    2. 何故Gearpumpが必要なのか? • リアルタイムストリーム処理に求めたい性質 ①Keep the data moving ② Query using StreamSQL ③ Handle stream imperfections ④ Generate predictable outcomes ⑤ Integrate stored and streaming data ⑥ Guarantee data safety and availabilit ⑦ Partition and scale applications automatically ⑧ Process and Respond Instantaneously • 『Meet The 8 Requirements of Real-Time Stream Processing (2006)』 • 上記の性質を満たすストリーム処理基盤が必要 • Apache Flinkも同様の性質をもつ だが、小規模のCheckpointに区切る方式 4
  • 6.
    3. Gearpumpの特長 • 下記のような特長を持つ。 ①高スループット ② 低レイテンシ ③ メッセージの処理信頼性設定可能 (At least once / exactly once)※但し条件あり ④ 高拡張性 ⑤ 動的DAG 5
  • 7.
    4. Gearpumpの構成 • コンポーネントは全てakkaActorで構成 • アドレス解決をAkkaを用いることで 全体のアーキテクチャはシンプルに出来ている。 6
  • 8.
    4. Gearpumpの構成 • 耐障害性もakkaclusterで維持 • 親Actorは子Actorの状態を確認し、自動復旧 • 一番Rootとなる親はakka cluster / CRDTで冗長化 7 WorkerWorkerWorker Master standb y Master Standb y Master State Gossi p CRDT Data type example: leader
  • 9.
    5. どうリアルタイム処理が組めるか? • Gearpumpのアプリケーションは下記で構成 •各ProcessorのActorコード • ActorをDAGとして組み合わせてアプリケーションとして デプロイするコード 8 例:WordCountを行うアプリケーション Split Sum 単語ごとに集約 Sum.scala Split.scala HashPartitioner (既存コンポーネント) WordCount.scala
  • 10.
    5. どうリアルタイム処理が組めるか? • Split.scala 9 classSplit(taskContext : TaskContext, conf: UserConfig) extends Task(taskContext, conf) { import taskContext.{output, self} // 1. 自分自身にStartメッセージを通知。 override def onStart(startTime : StartTime) : Unit = { self ! Message("start") } // 2. 文章を単語に分割し、空文字を除去した上で下流に送信 override def onNext(msg : Message) : Unit = { Split.TEXT_TO_SPLIT.lines.foreach { line => line.split("[¥¥s]+").filter(_.nonEmpty).foreach { msg => output(new Message(msg, System.currentTimeMillis())) } } // 3. 次メッセージを自分に対して送信するタスクを仕掛ける import scala.concurrent.duration._ taskContext.scheduleOnce(Duration(100, TimeUnit.MILLISECONDS))(self ! Message("continue", System.currentTimeMillis())) } }
  • 11.
    5. どうリアルタイム処理が組めるか? • Sum.scala 10 classSum (taskContext : TaskContext, conf: UserConfig) extends Task(taskContext, conf) { private[wordcount] val map : mutable.HashMap[String, Long] = new mutable.HashMap[String, Long]() private[wordcount] var wordCount : Long = 0 private var snapShotTime : Long = System.currentTimeMillis() private var snapShotWordCount : Long = 0 private var scheduler : Cancellable = null override def onStart(startTime : StartTime) : Unit = { // 1. 起動時に状態出力タスクを仕掛ける。 scheduler = taskContext.schedule(new FiniteDuration(5, TimeUnit.SECONDS), new FiniteDuration(30, TimeUnit.SECONDS))(reportWordCount) } override def onNext(msg : Message) : Unit = { if (null == msg) { return } // 2. 受信したメッセージから単語を取得し、総受信回数と単語ごとの受信回数をカウント val current = map.getOrElse(msg.msg.asInstanceOf[String], 0L) wordCount += 1 map.put(msg.msg.asInstanceOf[String], current + 1) }
  • 12.
    5. どうリアルタイム処理が組めるか? • WordCount.scala 11 objectWordCount extends AkkaApp with ArgumentsParser { private val LOG: Logger = LogUtil.getLogger(getClass) val RUN_FOR_EVER = -1 // 1. 起動時のCLIから読み込む項目と形式、注釈、必須/オプショナル、デフォルト値を定義 override val options: Array[(String, CLIOption[Any])] = Array( "split" -> CLIOption[Int]("<how many split tasks>", required = false, defaultValue = Some(1)), "sum" -> CLIOption[Int]("<how many sum tasks>", required = false, defaultValue = Some(1)) ) def application(config: ParseResult) : StreamApplication = { // 2. CLIから読み込んだ設定項目を用いてProcessorを生成 val splitNum = config.getInt("split") val sumNum = config.getInt("sum") val split = Processor[Split](splitNum) val sum = Processor[Sum](sumNum) // 3. メッセージのProcessor間の割り振りを行うPartitionerを生成 val partitioner = new HashPartitioner // 4. ProcessorとPartitionerを用いてDAGを作成 val app = StreamApplication("wordCount", Graph(split ~ partitioner ~> sum), UserConfig.empty) app } 非常に直観的にグラフが 組める!
  • 13.