ScalaMatsuri 2016 なぜリアクティブは重要か 岡本 雄太 (@okapies) 2016/01/30 https://www.flickr.com/photos/livenature/204420128
ScalaMatsuri 2016
なぜリアクティブは重要か岡本 雄太 (@okapies)
2016/01/30
https://www.flickr.com/photos/livenature/204420128
自己紹介
• 岡本 雄太(@okapies)
• 製造業で働くソフト屋さん
• Scala と Scala OSSs 愛好家
• 最近の仕事はインフラエンジニアっぽい感じ
• ScalaMatsuri 2016 準備委員会 運営委員
公開してる OSS
• finagle-kafka (https://github.com/okapies/finagle-kafka)
• sircuit (https://github.com/okapies/sircuit)
• rx-process (https://github.com/okapies/rx-process)
書き物
• 翻訳:
• リアクティブ宣言 v2.0
• Effective Scala 日本語版
• 命令型のコールバック、関数型のプロミス
• ブログ記事 (http://okapies.hateblo.jp/):
• 非同期ストリーム処理の標準化を目指す "Reactive Streams" とは
• 関数型プログラマのための Rx 入門
• マイクロサービスが Scala を選ぶ3つの理由
関連するセッション
• 12:00 - 12:40(国際交流会議場):
• リアクティブ・マイクロサービス (Christopher
Hunt)
• 15:00 - 15:40(メディアホール):
• レジリエンスが無ければ、他は無いも同じ (Jonas
Bonér)
どこでもリアクティブ!
• フロントエンドからバックエンドまで、様々な文脈で〈リアクティブ〉がキーワードに
• 互いに似ているけど異なる様々なコンセプト:
• Reactive Programming
• Reactive Streams
• Reactive Manifesto
フロントエンド GUI
マルチクリックストリーム
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
250 ミリ秒毎にクリックを集積 リストを長さ
にマッピング
RxJS による非同期クリックストリーム
GUI とネットワークサービスhttps://github.com/reark/reark
RxJava + Android製のデモアプリ
非同期 GUI イベントと JSON API 呼び出 しを並行処理として組み合わせる
マイクロサービス
val userAndTweets = Future.join( userService.findByUserId(userId), tweetService.findByUserId(userId))
find
finduserId userAndTweets
User Service
Tweet Service
http://www.slideshare.net/knoldus/finagle-by-twitter-engineer/16
join
他のマイクロサービスへクエリを投げ、全ての応答が揃ったら非同期に集約してタプルにする
Twitter Finagle
ビッグデータ処理https://speakerdeck.com/googlecloudjapan/google-cloud-dataflowwoli-jie-suru
ビッグデータの分散並列処理
Google Cloud Dataflow
コールバックじゃダメなの?
// asynchronous eventdef mouseClick(f: (Int, Int) => Unit): Unit!// register a callback as an argumentmouseClick { case (x, y) => println(s"x: $x, y: $y”) }
コールバック
破滅のピラミッド
var g = ...!step1 { a => step2 { b => step3 { c => step4 { d => // do something with a, b, c, d and g } } }}
依存する非同期ステップがピラミッドのように積み上がる
外側のスコープの状態を暗黙に参照していてモジュール性が低い
http://en.wikipedia.org/wiki/Reactive_programming
〈データフロー〉と〈変更の伝播〉を指向するパラダイム
実行モデル
• データフローそのものは、変数と演算の間の依存関係を記述しているだけ
• グラフの具体的な計算方法は実行モデルが決める
AB C
+1
—1
×2
+A = 1;B = 2;C = (A+1) + (B-1)*2;
-1
×2
+
+1A
B C
変数への再代入A = 1; B = 2;C = (A+1) + (B-1)*2; A = 2;
1 $ 2
2 4
A の変更はC に伝搬しない
命令型の実行モデル
××
××× ×
-1
×2
+
+1A
B C
変数への再代入A := 1; B := 2; C := (A+1) + (B-1)*2; A := 2;
1 $ 2
4 $ 52
1 $ 3
1 2A の変更が
C に伝搬する
リアクティブの実行モデル
-1
×2
+
+1A
B C
変数への再代入
2
5 $ 72 $ 31 $ 2 2 $ 4
3
C := (A+1) + (B-1)*2; A := 2;B := 3;
リアクティブの実行モデル
B の変更がC に伝搬する
例 (Akka Streams):
implicit val system = ActorSystem()implicit val mat = ActorMaterializer()!val a = Source(...)val b = Source(...)!val a1 = a.map(_ + 1)val b1 = b.map(_ - 1).map(_ * 2)!val c = (a1 zip b1).map{case (a, b) => a + b}!c.runWith(Sink.foreach(println))(mat)
AB C
+1
—1
×2
+
先ほどのデータフローを関数型 DSL で記述する
例 (Akka Streams):
implicit val system = ActorSystem()implicit val mat = ActorMaterializer()!val a = Source(...)val b = Source(...)!val a1 = a.map(_ + 1)val b1 = b.map(_ - 1).map(_ * 2)!val c = (a1 zip b1).map{case (a, b) => a + b}!c.runWith(Sink.foreach(println))(mat)入力に適用する関数を
高階関数 map で繋ぎ合わせる
関数入力
AB C
+1
—1
×2
+
なぜ関数プログラミングは重要か
• QuickCheck の開発や QuviQ の創業者として知られるジョン・ヒューズ博士の著名な論文
• 初版は 1984 年(30 年前!)
• 関数型プログラミングを活用して、コードのモジュール性を高める方法について論じている
http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html
遅延評価
class Cons[A](hd: A, tl: => List[A]) extends List[A]!def nats(n: Int): List[Int] = new Cons(n, nats(n+1))def fizzbuzz(n: Int) = n match { case _ if n % 15 == 0 => "FizzBuzz" case _ if n % 3 == 0 => "Fizz" case _ if n % 5 == 0 => "Buzz" case _ => n.toString}nats.map(fizzbuzz).take(100).foreach(println)
必要呼び (プル型)
コードを生成器と選択器の 組み合わせでモジュール化できる
無限リスト
高階関数
• プログラムを、汎用的な高階関数とユースケースに特化した関数に分けてモジュール化
!
!
• ビジネスロジックと、それが乗っかるデータ型の文脈を分離できる
set. map(_ + 1) // Set[A]map. map(_ + 1) // Map[A, B]list.map(_ + 1) // List[A]
局所化された文脈 ビジネスロジックを使い回せる
FP の糊を RP に適用すると?
• 遅延評価:
• 非同期イベントを少しずつ処理する生成器・選択器のパイプラインとしてプログラムを構成する(プッシュ型)
• 高階関数:
• ビジネスロジックと、それが乗っかる非同期イベント駆動の文脈を分離する
FRP の〈糊〉
implicit val system = ActorSystem()implicit val mat = ActorMaterializer()!val a = Source(...)val b = Source(...)!val a1 = a.map(_ + 1)val b1 = b.map(_ - 1).map(_ * 2)!val c = (a1 zip b1).map{case (a, b) => a + b}!c.runWith(Sink.foreach(println))
AB C
+1
—1
×2
+
生成器
非同期の文脈を局所化した高階関数 (map, zip 等)を使い、 ビジネスロジックをパイプライン化する
選択器
局所化された非同期の文脈
• 多くの FRP のデータフロー記述は宣言型 DSL: 構築したデータフローを実際にスケジュールし実行するのはランタイムの役割
what と how の分離
implicit val system = ActorSystem()implicit val mat = ActorMaterializer()!val c = (a1 zip b1).map{case (a, b) => a + b}!c.runWith(Sink.foreach(println))(mat) ランタイム
what と how の分離
Input
Input
Output(2) ランタイム
(1) プログラミングモデル (DSL)
(how を実行する = 変更の伝搬)
(what を記述する = データフロー)
例: 融合 (Fusing)
• Akka Streams 2.0 の新機能
This new abstraction … is called fusing. This feature … will be now possible to execute multiple stream processing steps inside one actor, reducing the number of thread-hops where they are not necessary … will increase performance for various use cases, including HTTP.
http://akka.io/news/2015/11/05/akka-streams-2.0-M1-released.html
複数の処理ステップを一つにまとめて性能向上
実装例
• データフロー DSL とランタイムの組み合わせは、近年、様々な分野で適用されている
• Akka Streams, ReactiveX, …
• 科学技術計算: TensorFlow, Halide
• ビッグデータ処理: Spark, Google Cloud Dataflow,
Asakusa Framework, Gearpump
例: TensorFlow
http://download.tensorflow.org/paper/whitepaper2015.pdf
アプリケーションの要求の変化
数年前 現在
Configuration 数十のサーバ 数千のマルチコアプロセッサ
Responsetime 秒単位 ミリ秒単位
Availability数時間の
オフラインメンテナンス稼働率 100%
Data size ギガバイト単位 ペタバイト単位
システムの構築方法の変化
“大規模システムを構築する組織はこの変化に対処する設計原則を
すでに発見している”
“そのような特徴を備えるシステムをReactive Systems と呼ぼう”
http://www.reactivemanifesto.org/ja
リアクティブシステムの性質
リアクティブシステムの四つの特徴: 即応性、弾力性、レジリエンス、メッセージ駆動
http://www.slideshare.net/Typesafe_Inc/going-reactive-2016-data-preview/6
リアクティブシステムの性質実現したい価値:
ユーザへ迅速かつ一貫した速度でサービスを提供する
障害時にも即応性を維持する
非同期メッセージパッシングが全ての基盤である
負荷が変動しても即応性を維持する
http://www.slideshare.net/Typesafe_Inc/going-reactive-2016-data-preview/6
メッセージ駆動が達成するもの
• 弾力性: スケーラビリティ、シャーディング、レプリケーション、位置透過性
• レジリエンス: レプリケーション、隔離、タスクやエラーの委譲 (“Let it crash”)
リアクティブシステムをどう構築するか?
マニフェストの中で、その実現方法について規範的なことは述べたくなかった。 — Martin Thompson
• マニフェストは、リアクティブなコンポーネントやシステムの性質や品質についてのみ記述しており、具体的な実現方法には触れていない
• 実例としてマイクロサービス・アーキテクチャ (MSA) を見てみよう
http://www.infoq.com/news/2014/10/thompson-reactive-manifesto-2
マイクロサービス・アーキテクチャ
• Amazon, Netflix, Twitter のような巨大な開発組織をスケールさせるための方法論
• サービスを、ビジネス遂行能力に沿って小さく独立したモジュールに分ける(c.f. コンウェイの法則)
• リアクティブシステムの実例の一つとみなせる
STORAGE & RETRIEVAL
LOGICPRESENTATIONROUTING
Redis
Memcache
Flock
T-Bird
MySQLTweet
User
Timeline
Social Graph
DMs
API
Web
Monorail
TFE
HTTP Thrift “Stuff”http://monkey.org/~marius/scala2015.pdf
例: Twitter における マイクロサービス
システムレベルのデータフロー
• MSA は(リアクティブな)コンポーネントと、そのビジネス上の依存関係で構成される • つまりシステムレベルのデータフロー
• ならば、分散システム全体をコードとして記述できないだろうか?
AB C
リアクティブ・ビッグデータ
• 近年のビッグデータ処理フレームワークでは、リアクティブなアーキテクチャが採用されることが多い
• Spark
• Google Cloud Dataflow
• Gearpump (Intel)
• Asakusa Framework
https://speakerdeck.com/googlecloudjapan/google-cloud-dataflowwoli-jie-suru
DAG でデータ処理パイプラインを記述
in Google Cloud Dataflow
DAG on Spark
https://cloud.githubusercontent.com/assets/2133137/7625997/e0878f8c-f9b4-11e4-8df3-7dd611b13c87.png
実行中の Spark ジョブを DAG として可視化した例
http://www.gearpump.io/overview.html
Gearpump の Akka を使ったリアクティブ・アーキテクチャ
http://knowledge.sakura.ad.jp/tech/4016/http://docs.asakusafw.com/preview/ja/html/asakusa-on-spark/user-guide.html
Asakusa Framework
一つの DSL アプリを Hadoop でも Spark でもポータブルに実行できる
Apache Dataflow (New!)
http://googlecloudplatform.blogspot.co.uk/2016/01/Dataflow-and-open-source-proposal-to-join-the-Apache-Incubator.html
Google 主導によるデータフロー記述の標準化の取り組み
https://speakerdeck.com/googlecloudjapan/google-cloud-dataflowwoli-jie-suru
ランタイムとしてのGoogle Cloud
Optimize
Schedule
Flow of pipelineUser code & SDK Monitoring UI
データフロー定義
データフロー・ランタイムとしての Google クラウドが データフローの最適化とタスクのスケジュールを行う
一般化すると…
• クラウドレベルのランタイムは:
• 指定されたデータフローを最適化すると共に、リアクティブなコンポーネントをスケジューリングする
• YARN や のような分散リソースマネージャからリソースを獲得する
• 確保したリソースにコンポーネントを配備し、リアクティブ・システムとして実行する
Dataflow
Reactive System
Cloud-level Runtime
ウェブサービスの配備自動化
• 現代の DevOps ツールは、主にノードごとにシステムを設定することに焦点を当てており、Dockerfile のような命令的な記述を用いている
• イミュータブル・インフラストラクチャは、本来、リアクティブなコンポーネントと宣言的なデータフローの組み合わせとして実現されるべきだったのでは?
まとめ
• リアクティブ・プログラミングとアーキテクチャによって、今日のソフト開発が直面する課題を解決できる:
• 非同期&イベント駆動プログラミング
• 並行・並列処理
• システムと組織のスケーラビリティ
• 耐障害性