SlideShare a Scribd company logo
1 of 19
Download to read offline
Spark Streaming Snippets
@a#y303
今まで作った Spark Streaming アプリ
• rtg -- リアルタイムリタゲ用データ生成
• pixelwriter -- Dynamic Crea4ve 用マークデータ書き込み
• feedsync -- 商品フィード同期
• segment:elas4c -- リアルタイムセグメント化
• (logblend -- 異なるイベントログの Join)
これらのアプリから適当に共有すると嬉しそうなとこ
ろを抜いてみた
• SparkBoot
• Cron
• UpdatableBroadcast
• Connector
• SparkStreamingSpec
SparkBoot
h"ps://gist.github.com/a"y303/c83f3c8cb8a930951be0
• Spark アプリの main 実装
• SparkContext / StreamingContext を提供する
• Configura4on 管理
• spark-submit の --files で applica4on.conf を送ってカスタ
マイズ
バッチの場合
object TrainingBatchApp extends SparkBatchBoot {
val appName = "TrainingBatchApp"
override def mkApp(sc: SparkContext, args: Array[String]): SparkApp =
new TrainingBatchApp(sc, appConfig)
}
class TrainingBatchApp(
sc: SparkContext, appConfig: Config)
extends SparkApp {
def run(): Try[Int] = Try {
0
}
}
ストリーミングの場合
object PredictStreamingApp extends SparkStreamingBoot {
val appName = "PredictStreamingApp"
override val checkpointPath: String = "app.training.streaming.checkpoint-path"
override val batchDurationPath: String = "app.training.streaming.batch-duration"
override def mkApp(sc: SparkContext, ssc: StreamingContext, args: Array[String]): SparkApp =
new PredictStreamingApp(ssc, batchDuration, appConfig)
}
class PredictStreamingApp(
ssc: StreamingContext, appConfig: Config)
extends SparkApp {
val sparkContext = ssc.sparkContext
def run(): Try[Int] = Try {
0
}
}
Cron 的なことをやる
• batch-dura+on より長い間隔で定期実行したい処理がある
• 外部データストアから読んでいるマスタデータのリフレッシュ
など
def repeatedly(streamingContext: StreamingContext, interval: Duration)
(f: (SparkContext, Time) => Unit): Unit = {
// トリガーを生成する DStream
val s = streamingContext.queueStream(
mutable.Queue.empty[RDD[Unit]],
oneAtATime = true,
defaultRDD = streamingContext.sparkContext.makeRDD(Seq(())))
.repartition(1)
s.window(s.slideDuration, interval)
.foreachRDD { (rdd, time) =>
f(rdd.context, time)
rdd.foreach(_ => ())
}
}
使い方
repetedly(streamingContext, Durations.seconds(300)) { (sc, time) =>
// @driver: 5 分毎に実行する処理
}
更新可能な Broadcast
• Streaming が動き始めた後に Broadcast を更新したい
• 単なる Broadcast を保持するラッパー
/**
* 値の更新(再ブロードキャスト)が可能な Broadcast
*
* https://gist.github.com/Reinvigorate/040a362ca8100347e1a6
* @author Reinvigorate
*/
case class UpdatableBroadcast[T: ClassTag](
@transient private val ssc: StreamingContext,
@transient private val _v: T) {
@transient private var v = ssc.sparkContext.broadcast(_v)
def update(newValue: T, blocking: Boolean = false): Unit = {
v.unpersist(blocking)
v = ssc.sparkContext.broadcast(newValue)
}
def value: T = v.value
private def writeObject(out: ObjectOutputStream): Unit = {
out.writeObject(v)
}
private def readObject(in: ObjectInputStream): Unit = {
v = in.readObject().asInstanceOf[Broadcast[T]]
}
}
使い方
def loadModel(): Model = ???
val ub: UpdatableBroadcast[Model] =
UpdatableBroadcast(streamingContext, loadModel())
StreamingUtil.repeatedly(streamingContext, refreshInterval) { (_, _) =>
ub.update(loadModel())
}
dstream.foreachRDD { rdd =>
val model = ub.value
// use model
}
外部接続の抽象化
• Spark で外部リソースにアクセスするとき、個々の Executor が
接続を維持する必要がある
• Driver から Executor に「接続そのもの」を送信することはでき
ない
• 「接続する方法」を Connector trait として抽象化している
trait Connector[A] extends java.io.Closeable with Serializable {
def get: A
def close(): Unit
def using[B](f: A => B): B = f(get)
}
case class PoolAerospikeConnector(name: Symbol, config: AerospikeConfig)
extends Connector[AerospikeClient] {
def get: AerospikeClient =
PoolAerospikeConnector.defaultHolder.getOrCreate(name, mkClient)
def close(): Unit =
PoolAerospikeConnector.defaultHolder.remove(name)(
AerospikeConnector.aerospikeClientClosable)
private val mkClient: () => AerospikeClient =
() => new AerospikeClient(config.clientPolicy.underlying,
config.asHosts:_*)
}
object PoolAerospikeConnector {
private val defaultHolder = new DefaultResourceHolder[AerospikeClient]
}
case class ScalikeJdbcConnector(name: Symbol, config: Config)
extends Connector[Unit] {
def get: Unit = {
if (!ConnectionPool.isInitialized(name)) {
// Load MySQL JDBC driver class
Class.forName("com.mysql.jdbc.Driver")
ConnectionPool.add(name, config.getString("url"),
config.getString("user"), config.getString("password"))
}
}
def close(): Unit = ConnectionPool.close(name)
}
case class KafkaProducerConnector[K :ClassTag, V :ClassTag](
name: Symbol, config: java.util.Map[String, AnyRef])
extends Connector[ScalaKafkaProducer[K, V]] {
def get: ScalaKafkaProducer[K, V] =
KafkaProducerConnector.defaultHolder.getOrCreate(name, mkResource)
.asInstanceOf[ScalaKafkaProducer[K, V]]
def close(): Unit = KafkaProducerConnector.defaultHolder.remove(name)(
KafkaProducerConnector.kafkaProducerClosable)
private val mkResource = () => {
val keySer = mkDefaultSerializer[K](ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG)
val valueSer = mkDefaultSerializer[V](ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG)
new ScalaKafkaProducer[K, V](
new KafkaProducer[K, V](config, keySer.orNull, valueSer.orNull))
}
private def mkDefaultSerializer[A :ClassTag](configKey: String): Option[Serializer[A]] = {
if (!config.containsKey(configKey)) {
implicitly[ClassTag[A]].runtimeClass match {
case c if c == classOf[Array[Byte]] => Some(new ByteArraySerializer().asInstanceOf[Serializer[A]])
case c if c == classOf[String] => Some(new StringSerializer().asInstanceOf[Serializer[A]])
case _ => None
}
} else None
}
}
Spark Streaming のテスト
h"ps://gist.github.com/a"y303/18e64e718f0cf3261c0e
class CountProductSpec extends SpecWithJUnit with SparkStreamingSpec {
val batchDuration: Duration = Duration(1000)
"Count" >> {
val (sourceQueue, resultQueue) = startQueueStream[Product, (Product, Long)] { inStream =>
// テスト対象の Streaming 処理
CountProduct(inStream).run(sc)
}
// 入力キューにテストデータを投入する
sourceQueue += sc.parallelize(Seq(
Product(1, "id"), Product(1, "id"), Product(2, "id")))
// 時間を進める
advance()
// 出力されるデータをテストする
resultQueue.dequeue must eventually(contain(exactly(
Product(1, "id") -> 2L, Product(2, "id") -> 1L
)))
}
}

More Related Content

What's hot

OCP Meetup Tokyo #05 ECK on OCP
OCP Meetup Tokyo #05 ECK on OCPOCP Meetup Tokyo #05 ECK on OCP
OCP Meetup Tokyo #05 ECK on OCPTetsuya Sodo
 
もうちょっと早く知りたかった kubectl
もうちょっと早く知りたかった kubectlもうちょっと早く知りたかった kubectl
もうちょっと早く知りたかった kubectlHiroki Sakonju
 
20150219 初めての「embulk」
20150219 初めての「embulk」20150219 初めての「embulk」
20150219 初めての「embulk」Hideto Masuoka
 
Cosmos DB 入門 multi model multi API編
Cosmos DB 入門 multi model multi API編Cosmos DB 入門 multi model multi API編
Cosmos DB 入門 multi model multi API編Takekazu Omi
 
Rancher Charts Introduction
Rancher Charts IntroductionRancher Charts Introduction
Rancher Charts IntroductionTetsurou Yano
 
CloudWatch Logsについて
CloudWatch LogsについてCloudWatch Logsについて
CloudWatch LogsについてSugawara Genki
 
Spring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractSpring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractTakeshi Ogawa
 
TerraformでECS+ECRする話
TerraformでECS+ECRする話TerraformでECS+ECRする話
TerraformでECS+ECRする話Satoshi Hirayama
 
Myfirst buildpack session_mgmt_20161201
Myfirst buildpack session_mgmt_20161201Myfirst buildpack session_mgmt_20161201
Myfirst buildpack session_mgmt_20161201Tomohiro Ichimura
 
FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係moai kids
 
Rancher kubernetes storages
Rancher kubernetes storagesRancher kubernetes storages
Rancher kubernetes storagesTetsurou Yano
 
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3Toshiaki Maki
 
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送Google Cloud Platform - Japan
 
20160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #520160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #5Koichiro Sasaki
 
EmbulkとDigdagとデータ分析基盤と
EmbulkとDigdagとデータ分析基盤とEmbulkとDigdagとデータ分析基盤と
EmbulkとDigdagとデータ分析基盤とToru Takahashi
 
Spring Fest 2018 Spring Bootで作るRESTful Web Service
Spring Fest 2018 Spring Bootで作るRESTful Web ServiceSpring Fest 2018 Spring Bootで作るRESTful Web Service
Spring Fest 2018 Spring Bootで作るRESTful Web ServiceWataruOhno
 
アドテク×Scala×パフォーマンスチューニング
アドテク×Scala×パフォーマンスチューニングアドテク×Scala×パフォーマンスチューニング
アドテク×Scala×パフォーマンスチューニングYosuke Mizutani
 

What's hot (19)

OCP Meetup Tokyo #05 ECK on OCP
OCP Meetup Tokyo #05 ECK on OCPOCP Meetup Tokyo #05 ECK on OCP
OCP Meetup Tokyo #05 ECK on OCP
 
もうちょっと早く知りたかった kubectl
もうちょっと早く知りたかった kubectlもうちょっと早く知りたかった kubectl
もうちょっと早く知りたかった kubectl
 
20150219 初めての「embulk」
20150219 初めての「embulk」20150219 初めての「embulk」
20150219 初めての「embulk」
 
Cosmos DB 入門 multi model multi API編
Cosmos DB 入門 multi model multi API編Cosmos DB 入門 multi model multi API編
Cosmos DB 入門 multi model multi API編
 
Rancher Charts Introduction
Rancher Charts IntroductionRancher Charts Introduction
Rancher Charts Introduction
 
CloudWatch Logsについて
CloudWatch LogsについてCloudWatch Logsについて
CloudWatch Logsについて
 
Spring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contractSpring data-rest-and-spring-cloud-contract
Spring data-rest-and-spring-cloud-contract
 
TerraformでECS+ECRする話
TerraformでECS+ECRする話TerraformでECS+ECRする話
TerraformでECS+ECRする話
 
Myfirst buildpack session_mgmt_20161201
Myfirst buildpack session_mgmt_20161201Myfirst buildpack session_mgmt_20161201
Myfirst buildpack session_mgmt_20161201
 
FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係FluentdとRedshiftの素敵な関係
FluentdとRedshiftの素敵な関係
 
APIMeetup 20170329_ichimura
APIMeetup 20170329_ichimuraAPIMeetup 20170329_ichimura
APIMeetup 20170329_ichimura
 
Rancher kubernetes storages
Rancher kubernetes storagesRancher kubernetes storages
Rancher kubernetes storages
 
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
Reactive Webアプリケーション - そしてSpring 5へ #jjug_ccc #ccc_ef3
 
Hive chapter 2
Hive chapter 2Hive chapter 2
Hive chapter 2
 
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送
[Cloud OnAir] Google Cloud 主催イベント Anthos Day 情報 2020 年 2 月 13 日放送
 
20160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #520160121 データサイエンティスト協会 木曜セミナー #5
20160121 データサイエンティスト協会 木曜セミナー #5
 
EmbulkとDigdagとデータ分析基盤と
EmbulkとDigdagとデータ分析基盤とEmbulkとDigdagとデータ分析基盤と
EmbulkとDigdagとデータ分析基盤と
 
Spring Fest 2018 Spring Bootで作るRESTful Web Service
Spring Fest 2018 Spring Bootで作るRESTful Web ServiceSpring Fest 2018 Spring Bootで作るRESTful Web Service
Spring Fest 2018 Spring Bootで作るRESTful Web Service
 
アドテク×Scala×パフォーマンスチューニング
アドテク×Scala×パフォーマンスチューニングアドテク×Scala×パフォーマンスチューニング
アドテク×Scala×パフォーマンスチューニング
 

Similar to Spark Streaming Snippets

Isomorphic web development with scala and scala.js
Isomorphic web development  with scala and scala.jsIsomorphic web development  with scala and scala.js
Isomorphic web development with scala and scala.jsTanUkkii
 
Real world android akka
Real world android akkaReal world android akka
Real world android akkaTaisuke Oe
 
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...Shotaro Suzuki
 
Building React, Flutter and Blazor development and debugging environment with...
Building React, Flutter and Blazor development and debugging environment with...Building React, Flutter and Blazor development and debugging environment with...
Building React, Flutter and Blazor development and debugging environment with...Shotaro Suzuki
 
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」fukuoka.ex
 
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)NTT DATA Technology & Innovation
 
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版ありますElixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版ありますfukuoka.ex
 
130329 04
130329 04130329 04
130329 04openrtm
 
20130329 rtm4
20130329 rtm420130329 rtm4
20130329 rtm4openrtm
 
AKS と ACI を組み合わせて使ってみた
AKS と ACI を組み合わせて使ってみたAKS と ACI を組み合わせて使ってみた
AKS と ACI を組み合わせて使ってみたHideaki Aoyagi
 
Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略takezoe
 
Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Masahito Zembutsu
 
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
Next2Dで始めるゲーム開発  - Game Development Starting with Next2DNext2Dで始めるゲーム開発  - Game Development Starting with Next2D
Next2Dで始めるゲーム開発 - Game Development Starting with Next2DToshiyuki Ienaga
 
Apache Torqueについて
Apache TorqueについてApache Torqueについて
Apache Torqueについてtako pons
 
成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略Hiroshi SHIBATA
 
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方Alfresco勉強会20120829: やさしいShareダッシュレットの作り方
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方linzhixing
 
AWSとGCPを使用したインフラ環境
AWSとGCPを使用したインフラ環境AWSとGCPを使用したインフラ環境
AWSとGCPを使用したインフラ環境Katsutoshi Nagaoka
 
Inside mobage platform
Inside mobage platformInside mobage platform
Inside mobage platformToru Yamaguchi
 

Similar to Spark Streaming Snippets (20)

Isomorphic web development with scala and scala.js
Isomorphic web development  with scala and scala.jsIsomorphic web development  with scala and scala.js
Isomorphic web development with scala and scala.js
 
Real world android akka
Real world android akkaReal world android akka
Real world android akka
 
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...Let's build a simple app with  .net 6 asp.net core web api, react, and elasti...
Let's build a simple app with .net 6 asp.net core web api, react, and elasti...
 
Building React, Flutter and Blazor development and debugging environment with...
Building React, Flutter and Blazor development and debugging environment with...Building React, Flutter and Blazor development and debugging environment with...
Building React, Flutter and Blazor development and debugging environment with...
 
HTML5&API総まくり
HTML5&API総まくりHTML5&API総まくり
HTML5&API総まくり
 
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
 
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)
Apache Spark 3.0新機能紹介 - 拡張機能やWebUI関連のアップデート(Spark Meetup Tokyo #3 Online)
 
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版ありますElixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
 
130329 04
130329 04130329 04
130329 04
 
20130329 rtm4
20130329 rtm420130329 rtm4
20130329 rtm4
 
AKS と ACI を組み合わせて使ってみた
AKS と ACI を組み合わせて使ってみたAKS と ACI を組み合わせて使ってみた
AKS と ACI を組み合わせて使ってみた
 
Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略Seasarプロジェクト徹底攻略
Seasarプロジェクト徹底攻略
 
Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話Docker ComposeでMastodonが必要なものを梱包する話
Docker ComposeでMastodonが必要なものを梱包する話
 
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
Next2Dで始めるゲーム開発  - Game Development Starting with Next2DNext2Dで始めるゲーム開発  - Game Development Starting with Next2D
Next2Dで始めるゲーム開発 - Game Development Starting with Next2D
 
Apache Torqueについて
Apache TorqueについてApache Torqueについて
Apache Torqueについて
 
成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略成長を加速する minne の技術基盤戦略
成長を加速する minne の技術基盤戦略
 
Parse触ってみた
Parse触ってみたParse触ってみた
Parse触ってみた
 
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方Alfresco勉強会20120829: やさしいShareダッシュレットの作り方
Alfresco勉強会20120829: やさしいShareダッシュレットの作り方
 
AWSとGCPを使用したインフラ環境
AWSとGCPを使用したインフラ環境AWSとGCPを使用したインフラ環境
AWSとGCPを使用したインフラ環境
 
Inside mobage platform
Inside mobage platformInside mobage platform
Inside mobage platform
 

Spark Streaming Snippets

  • 2. 今まで作った Spark Streaming アプリ • rtg -- リアルタイムリタゲ用データ生成 • pixelwriter -- Dynamic Crea4ve 用マークデータ書き込み • feedsync -- 商品フィード同期 • segment:elas4c -- リアルタイムセグメント化 • (logblend -- 異なるイベントログの Join)
  • 4. SparkBoot h"ps://gist.github.com/a"y303/c83f3c8cb8a930951be0 • Spark アプリの main 実装 • SparkContext / StreamingContext を提供する • Configura4on 管理 • spark-submit の --files で applica4on.conf を送ってカスタ マイズ
  • 5. バッチの場合 object TrainingBatchApp extends SparkBatchBoot { val appName = "TrainingBatchApp" override def mkApp(sc: SparkContext, args: Array[String]): SparkApp = new TrainingBatchApp(sc, appConfig) } class TrainingBatchApp( sc: SparkContext, appConfig: Config) extends SparkApp { def run(): Try[Int] = Try { 0 } }
  • 6. ストリーミングの場合 object PredictStreamingApp extends SparkStreamingBoot { val appName = "PredictStreamingApp" override val checkpointPath: String = "app.training.streaming.checkpoint-path" override val batchDurationPath: String = "app.training.streaming.batch-duration" override def mkApp(sc: SparkContext, ssc: StreamingContext, args: Array[String]): SparkApp = new PredictStreamingApp(ssc, batchDuration, appConfig) } class PredictStreamingApp( ssc: StreamingContext, appConfig: Config) extends SparkApp { val sparkContext = ssc.sparkContext def run(): Try[Int] = Try { 0 } }
  • 7. Cron 的なことをやる • batch-dura+on より長い間隔で定期実行したい処理がある • 外部データストアから読んでいるマスタデータのリフレッシュ など
  • 8. def repeatedly(streamingContext: StreamingContext, interval: Duration) (f: (SparkContext, Time) => Unit): Unit = { // トリガーを生成する DStream val s = streamingContext.queueStream( mutable.Queue.empty[RDD[Unit]], oneAtATime = true, defaultRDD = streamingContext.sparkContext.makeRDD(Seq(()))) .repartition(1) s.window(s.slideDuration, interval) .foreachRDD { (rdd, time) => f(rdd.context, time) rdd.foreach(_ => ()) } }
  • 9. 使い方 repetedly(streamingContext, Durations.seconds(300)) { (sc, time) => // @driver: 5 分毎に実行する処理 }
  • 10. 更新可能な Broadcast • Streaming が動き始めた後に Broadcast を更新したい • 単なる Broadcast を保持するラッパー
  • 11. /** * 値の更新(再ブロードキャスト)が可能な Broadcast * * https://gist.github.com/Reinvigorate/040a362ca8100347e1a6 * @author Reinvigorate */ case class UpdatableBroadcast[T: ClassTag]( @transient private val ssc: StreamingContext, @transient private val _v: T) { @transient private var v = ssc.sparkContext.broadcast(_v) def update(newValue: T, blocking: Boolean = false): Unit = { v.unpersist(blocking) v = ssc.sparkContext.broadcast(newValue) } def value: T = v.value private def writeObject(out: ObjectOutputStream): Unit = { out.writeObject(v) } private def readObject(in: ObjectInputStream): Unit = { v = in.readObject().asInstanceOf[Broadcast[T]] } }
  • 12. 使い方 def loadModel(): Model = ??? val ub: UpdatableBroadcast[Model] = UpdatableBroadcast(streamingContext, loadModel()) StreamingUtil.repeatedly(streamingContext, refreshInterval) { (_, _) => ub.update(loadModel()) } dstream.foreachRDD { rdd => val model = ub.value // use model }
  • 13. 外部接続の抽象化 • Spark で外部リソースにアクセスするとき、個々の Executor が 接続を維持する必要がある • Driver から Executor に「接続そのもの」を送信することはでき ない • 「接続する方法」を Connector trait として抽象化している
  • 14. trait Connector[A] extends java.io.Closeable with Serializable { def get: A def close(): Unit def using[B](f: A => B): B = f(get) }
  • 15. case class PoolAerospikeConnector(name: Symbol, config: AerospikeConfig) extends Connector[AerospikeClient] { def get: AerospikeClient = PoolAerospikeConnector.defaultHolder.getOrCreate(name, mkClient) def close(): Unit = PoolAerospikeConnector.defaultHolder.remove(name)( AerospikeConnector.aerospikeClientClosable) private val mkClient: () => AerospikeClient = () => new AerospikeClient(config.clientPolicy.underlying, config.asHosts:_*) } object PoolAerospikeConnector { private val defaultHolder = new DefaultResourceHolder[AerospikeClient] }
  • 16. case class ScalikeJdbcConnector(name: Symbol, config: Config) extends Connector[Unit] { def get: Unit = { if (!ConnectionPool.isInitialized(name)) { // Load MySQL JDBC driver class Class.forName("com.mysql.jdbc.Driver") ConnectionPool.add(name, config.getString("url"), config.getString("user"), config.getString("password")) } } def close(): Unit = ConnectionPool.close(name) }
  • 17. case class KafkaProducerConnector[K :ClassTag, V :ClassTag]( name: Symbol, config: java.util.Map[String, AnyRef]) extends Connector[ScalaKafkaProducer[K, V]] { def get: ScalaKafkaProducer[K, V] = KafkaProducerConnector.defaultHolder.getOrCreate(name, mkResource) .asInstanceOf[ScalaKafkaProducer[K, V]] def close(): Unit = KafkaProducerConnector.defaultHolder.remove(name)( KafkaProducerConnector.kafkaProducerClosable) private val mkResource = () => { val keySer = mkDefaultSerializer[K](ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG) val valueSer = mkDefaultSerializer[V](ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG) new ScalaKafkaProducer[K, V]( new KafkaProducer[K, V](config, keySer.orNull, valueSer.orNull)) } private def mkDefaultSerializer[A :ClassTag](configKey: String): Option[Serializer[A]] = { if (!config.containsKey(configKey)) { implicitly[ClassTag[A]].runtimeClass match { case c if c == classOf[Array[Byte]] => Some(new ByteArraySerializer().asInstanceOf[Serializer[A]]) case c if c == classOf[String] => Some(new StringSerializer().asInstanceOf[Serializer[A]]) case _ => None } } else None } }
  • 19. class CountProductSpec extends SpecWithJUnit with SparkStreamingSpec { val batchDuration: Duration = Duration(1000) "Count" >> { val (sourceQueue, resultQueue) = startQueueStream[Product, (Product, Long)] { inStream => // テスト対象の Streaming 処理 CountProduct(inStream).run(sc) } // 入力キューにテストデータを投入する sourceQueue += sc.parallelize(Seq( Product(1, "id"), Product(1, "id"), Product(2, "id"))) // 時間を進める advance() // 出力されるデータをテストする resultQueue.dequeue must eventually(contain(exactly( Product(1, "id") -> 2L, Product(2, "id") -> 1L ))) } }