81
ScalaMatsuri 2016 なぜリアクティブは重要か 岡本 雄太 (@okapies) 2016/01/30 https://www.flickr.com/photos/livenature/204420128

なぜリアクティブは重要か #ScalaMatsuri

Embed Size (px)

Citation preview

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

Reactive Streams が JDK 9 に

Reactive?

リアクティブ ◯◯

プログラミングモデル? !

ランタイムエンジン? !

アーキテクチャ?=

文脈によってかなり意味付けが異なるのが実情

リアクティブの基盤

• リアクティブなコンポーネント

!

• リアクティブなデータフロー

in out

1

2

AB C

リアクティブなコンポーネント

• 入力にのみ反応 (react) してデータを出力する

• 自己完結性

in out

関数? オブジェクト? アクター? サブシステム?

リアクティブなデータフロー

Source

Source Sink

in out

コンポーネントの入力と出力を結びつけてデータを運搬するパイプライン

リアクティブ ◯◯

プログラミングモデル !

ランタイムエンジン !

アーキテクチャ=

vs

vs

課題(再掲)

• 非同期&イベント駆動プログラミング

• 並行・並列処理

• システムと組織のスケーラビリティ

• 耐障害性

コールバックじゃダメなの?

// 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 } } }}

依存する非同期ステップがピラミッドのように積み上がる

外側のスコープの状態を暗黙に参照していてモジュール性が低い

リアクティブなコンポーネント• データフローから非同期イベントが入力されたときだけ反応する

in out

自己完結性• 各コンポーネントの内部状態は互いに隔離され、かつ独立したライフサイクルを持つ

• 自己完結性があると非同期処理がやりやすい

? ?in out

× ×外側の変数を暗黙に使わない

利点• モジュール性(組み合わせ可能性)が向上

• 状態や障害の封じ込めが容易になる

? ?in out

× ×

実行順序と依存性

• 非同期プログラミングで実行順序をどうやって制御すればいいか?

• 解決策: リアクティブ・プログラミング

http://en.wikipedia.org/wiki/Reactive_programming

〈データフロー〉と〈変更の伝播〉を指向するパラダイム

データフロー

g1

g2

h

Source

Source Sink

fin out

演算の間を流れるデータの有向グラフ

一般的な (命令型の) プログラム

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;

1

2 41 2

2

実行モデル

• データフローそのものは、変数と演算の間の依存関係を記述しているだけ

• グラフの具体的な計算方法は実行モデルが決める

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 に伝搬する

-1

×2

+

+1A

B C

変数への再代入

2 $ 0

7 $ 532 4

3 $ 1

A := 2; B := 3; A := 0;

リアクティブの実行モデル

A の変更がC に伝搬する

リアクティブプログラミング !

!

関数型 リアクティブプログラミング

(FRP)≒

一般に〈リアクティブプログラミング〉というと 〈関数型~〉(FRP) を指すことが多い

例 (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 を記述する = データフロー)

リアクティブ ◯◯

プログラミングモデル !

ランタイムエンジン !

アーキテクチャ=

vs

vs

リアクティブなプログラミングモデルとランタイムには 密接な関係がある

可搬性(マルチプラットフォーム)

• リアクティブなプログラムは、ランタイムによって様々なアーキテクチャにマッピングできる

• 単一マシン

• GPU クラスタ

• 分散環境

最適化

• データフローの性能や安定性をランタイムが最適化できる

• 融合、データ局所化、キャッシュ

• 並列分散化

• 検証

例: 融合 (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

課題(再掲)

• 非同期&イベント駆動プログラミング

• 並行・並列処理

• システムと組織のスケーラビリティ

• 耐障害性

DONE!

リアクティブ ◯◯

プログラミングモデル !

ランタイムエンジン !

アーキテクチャ=

vs

vs

単一マシン !

!

分散システム

なぜ分散システムか?

• 非同期&イベント駆動プログラミング

• 並行・並列処理

• システムと組織のスケーラビリティ

• 耐障害性

→ リアクティブ・システム

(マイクロサービス)

http://www.reactivemanifesto.org/ja

アプリケーションの要求の変化

数年前 現在

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

RS におけるコンポーネント

• メッセージのみを介して互いに通信する

• 自己完結しており非同期(バイナリ)境界で互いに隔離されている

アクター サブシステム

in out

メッセージ駆動が達成するもの

• 弾力性: スケーラビリティ、シャーディング、レプリケーション、位置透過性

• レジリエンス: レプリケーション、隔離、タスクやエラーの委譲 (“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 クラウドが データフローの最適化とタスクのスケジュールを行う

一般化すると…

ビッグデータにおけるリアクティブ・システムの アーキテクチャを一般化してみる

Dataflow

Reactive System

Cloud-level Runtime

一般化すると…

• クラウドレベルのランタイムは:

• 指定されたデータフローを最適化すると共に、リアクティブなコンポーネントをスケジューリングする

• YARN や      のような分散リソースマネージャからリソースを獲得する

• 確保したリソースにコンポーネントを配備し、リアクティブ・システムとして実行する

Dataflow

Reactive System

Cloud-level Runtime

ウェブサービスの配備自動化

• 現代の DevOps ツールは、主にノードごとにシステムを設定することに焦点を当てており、Dockerfile のような命令的な記述を用いている

• イミュータブル・インフラストラクチャは、本来、リアクティブなコンポーネントと宣言的なデータフローの組み合わせとして実現されるべきだったのでは?

まとめ

• リアクティブ・プログラミングとアーキテクチャによって、今日のソフト開発が直面する課題を解決できる:

• 非同期&イベント駆動プログラミング

• 並行・並列処理

• システムと組織のスケーラビリティ

• 耐障害性

まとめ

• リアクティブなコンポーネントとデータフローは、システムのあらゆる階層において非同期と分散の課題に対処する際に有効なツール

• プログラミングモデルよりもランタイムが提供する能力(性能、耐障害性、運用性)が重要な時代が到来しつつある