developing an akka edge6

67
Developing an Akka Edge Chapter 6

Upload: saaaaaaki

Post on 15-Jul-2015

372 views

Category:

Software


1 download

TRANSCRIPT

Developing an Akka EdgeChapter 6

前回のおさらい

(Chapter 6の前に…)

・結局…アクターはメッセージを到着した順に1つずつ処理する

メッセージが来たら、あらかじめ定義されたふるまいを実行する

Putting actors to workアクターを使うのにまず必要な事

・akka.actor.Actorトレイトを継承したクラスと、そこにreceiveメソッドを定義

・ActorSystemを作る

・ActorSystemからアクターを生成

メッセージの送り方② ask

Futureを返す

Futureが含んでいる値(処理済/まだ処理されていない/永遠に処理されない)

   永遠に処理されない可能性もあるので、implicit valを使って時間を設定

アクターへ送るメッセージは非同期なので、アクター以外のコード内でレスポンスを得るにはFutureを使う必要がある

Developing an Akka EdgeChapter 6

Developing an Akka Edgeだけでは理解できない部分があったため、

AkkaとJavaのDocumentLet it crashAkka ConcurrencyJava言語で学ぶデザインパターン入門

も参考にしました。

(今回はもはや本の要約じゃないです)

これまでと同様に

自分が持っていた知識やいくつかの情報を元にまとめたり、

不慣れな英語を読み進めているため正しくない項目がある可能性があります。

Chapter 6.Dispatchers

*この章のポイント*

・いろんなDispatcherといろんなパラメータ

2つのexecutor(Fork/JoinとThreadPoolExecutor)

・いろんなMailboxとパラメータ

・ざっくりした使用例とチューニングについて

Chapter 6.DispatchersDispatcherとRouterは全然別物!!

Dispatcherのコンセプト

アクターが処理を実行する時のスレッド割当

スレッドプールを持っていて、スレッドをアクターに割り当てる

導入Dispatcherを使うと…?

Low latency例:特定のActorに1スレッド丸ごと割り当てると…

Low latency例:特定のActorに1スレッド丸ごと割り当てると…

スレッドが割り当てられるのを待たなくて良いので

メッセージをすぐ処理してくれる

Bulkhead Pattern例:IO処理と計算処理のActor達を別のDispatcherにすると…

Bulkhead Pattern例:IO処理と計算処理のActor達を別のDispatcherにすると…

計算処理Dispatcherのスレッド数を多くするなど

IO と 計算のどっちに比重を置くかチューニングができる

Dispatcherのメリット

システムのある一部において

応答性UP負荷分散

パフォーマンスUPチューニング

が可能!!

(また、耐障害性もUP?)

Dispatcherの設定

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← Dispatcherの名前

後でコード上から呼び出す時に使う。好きな名前で

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← Dispatcherの種類

DispatcherPinnedDispatcherBalancingDispatcherCallingThreadDispatcher

から選ぶ

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

←executorの種類

fork-join-executorthread-pool-executorから選ぶ

並列処理の実行の仕方が違う

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← throughput

この値は、Actorにスレッドを割り当てた時1回あたりいくつのメッセージを処理するか(終わったらスレッドプールに戻る)

throughputとfairnessthroughput = 1の時

Actor1のメッセージを1つ処理

Actor2の…Actor3の…Actor1の…

Actor3のMailboxが最初に空になる

throughputとfairnessthroughput = 100の時

Actor1のメッセージを100つ処理

Actor2の…Actor3の…

Actor1のMailboxが最初に空になる

throughput = 1だと公平にメッセージを処理できるが、

切り替えが頻繁に起こるとパフォーマンスが悪くなる

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← このへんはスレッド数の設定的な感じ

thread-pool-executorを使う時は別の項目

Fork/Joinと

ThreadPoolExecutor

Fork/Joinとは

タスクを分割していって、それらを並列に処理していくフレームワーク

Java7から導入された

a + b + c + d

ちゃんと理解してないので雑な説明 …

Fork/Joinとは

タスクを分割していって、それらを並列に処理していくフレームワーク

Java7から導入された

a + b = e c + d = f

a + b + c + d

ちゃんと理解してないので雑な説明 …

Fork/Joinとは

タスクを分割していって、それらを並列に処理していくフレームワーク

Java7から導入された

e + f

a + b = e c + d = f

ちゃんと理解してないので雑な説明 …

Fork/Joinとは

タスクを分割していって、それらを並列に処理していくフレームワーク

Java7から導入された

タスクを細分化して…というのがActorの設計と似ているので、相性が良い

fork-join-executorの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

CPUのコア数×factor⇒並列実行するスレッド数

ただし、min < スレッド数 < max

ThreadPoolExecutorとは

java.util.concurrent.ThreadPoolExecutorワーカスレッドを管理するクラス

タスクキューを持っていて

スレッドプールのスレッドを使ってタスクを実行

⇒終わったらスレッドプールにスレッドがもどる

というのを繰り返している

スレッド

thread-pool-executorの設定

my-thread-dispatcher{type=Dispatcherexecutor="thread-pool-executor"thread-pool-executor{

core-pool-min = 4core-pool-max = 32core-pool-factor = 2.0

max-pool-min = 8max-pool-max = 84max-pool-factor = 3.0

keep-alive-time = 30ms

task-queue-type = “linked”}

}

core-poolとmax-poolそれぞれの設定がある

corePoolとmaxPool新しいタスクが、キューに追加された時

①corePoolSize > 実行中のスレッド数

新しいスレッドが作成される

②corePoolSize < 実行中のスレッド数 < maxPoolSizeキューが一杯の時のみ新しいスレッドが作成される

③maxPoolSize == 実行中のスレッド数

キューが一杯ならタスクがrejectされる

keep-alive-timecorePoolSize < 実行中のスレッド数 の時

タスクキューが空で暇なスレッドがあったら…

アイドル状態がkeep-alive-timeを超えるとスレッドが終了する

task-queue-typeLinkedBlockingQueue(サイズ上限なし)

ArrayBlockingQueue(サイズ上限指定)

から選ぶ

※LinkedBlockingQueueを使うと、corePoolSizeの全てのスレッドがbusy状態の時

新しいタスクはキュー内にどんどんたまっていくので

corePoolSizeを超えるスレッドは作成されない※

大きなキューと小さなプールを使用すると、スループットが低下する恐れあり

(CPU使用率・context switchingのオーバーヘッドは最小化される)

thread-pool-executorの設定

my-thread-dispatcher{type=Dispatcherexecutor="thread-pool-executor"thread-pool-executor{

core-pool-min = 4core-pool-max = 32core-pool-factor = 2.0

max-pool-min = 8max-pool-max = 84max-pool-factor = 3.0

keep-alive-time = 30ms}

}

いろんなDispatcher

いろんなDispatcherDispatcher

デフォルトで使っている

PinnedDispatcher

BalancingDispatcher

CallinThreadDispatcherakka.testkitに入ってるので、9章で登場するかも!?

いろんなDispatcherDispatcher

デフォルトで使っている

PinnedDispatcher

BalancingDispatcher

CallinThreadDispatcherakka.testkitに入ってるので、9章で登場するかも!?

PinnedDispatcherそれぞれのアクターに1スレッドずつ割り当てる。

⇒全てのアクターがスレッドをフルに使える

特に優先度が高いリソースを持っている時に使う

※このパターンを使いすぎないように!!※

BalancingDispatcher1つのMailboxをシェアしている。

暇になったActorがメッセージを処理するので、負荷は公平

全て同じ型のActorと使う事を想定されている

(型が違っても、受け取れるメッセージが同じなら動きそう)

6種のMailbox

6種のMailboxUnboundedMailboxSingleConsumerOnlyUnboundedMailboxBoundedMailboxUnboundedPriorityMailboxBoundedPriorityMailboxDurable

6種のMailboxUnboundedMailboxSingleConsumerOnlyUnboundedMailboxBoundedMailboxUnboundedPriorityMailboxBoundedPriorityMailboxDurable 1つのActorに1つのMailbox

(デフォルトはこれ)

他のMailboxが全部シェアしているのかは未確認です …

6種のMailboxUnboundedMailboxSingleConsumerOnlyUnboundedMailboxBoundedMailboxUnboundedPriorityMailboxBoundedPriorityMailboxDurable Mailboxの容量が

有限 or 無限

6種のMailbox容量が有限の時のパラメータ

mailbox-capacityMailboxの容量

mailbox-push-timeout-timeMailboxが一杯の時、空くのを待つ時間(?)

-1にすると無限

6種のMailboxUnboundedMailboxSingleConsumerOnlyUnboundedMailboxBoundedMailboxUnboundedPriorityMailboxBoundedPriorityMailboxDurable Mailbox内のメッセージに

優先度付き

6種のMailboxextendsして使う

Intで優先度を指定(0が優先度最高)

6種のMailboxUnboundedMailboxSingleConsumerOnlyUnboundedMailboxBoundedMailboxUnboundedPriorityMailboxBoundedPriorityMailboxDurable Mailboxのメッセージを

永続化できる(ファイルシステム)

ここまで長くなりましたが…

実際にDispatcherを使う

Dispatcherを使う

①ActorにDispatcherを設定する場合

val myActor = system.actorOf(Props[MyActor].withDispatcher(“my-dispatcher”))

Actorの設定をconfigurationに書いていれば、withDispatcherを使わなくてもよい

val myActor = system.actorOf(Props[MyActor],”myactor”)

akka.actor.deployment{/myactor{

dispatcher = my-dispatcher}

}

Dispatcherを使う

②FutureのためにDispatcherを使う場合

implicit val executionContext = system.dispatchers.lookup(“my-dispatcher”)

Mailboxを使う

myMailbox{mailbox-type = “BoundedMailbox”mailbox-capacity = 1000mailbox-push-timeout-time = 1

}

val myActor = system.actorOf(Props[MyActor].withMailbox(“myMailbox”)

Dispatcherの時と同様に、akka.actor.deployのActorの設定内に書いてもよい

使用例Akka Concurrencyの

When to choose a dispatching method参照

Low latency ⇒ PinnedDispatcherを使う

例:特定のActorに1スレッド丸ごと割り当てると…

スレッドが割り当てられるのを待たなくて良いので

メッセージをすぐ処理してくれる

Bulkhead Pattern ⇒ Dispatcherを使う

例:IO処理と計算処理のActor達を別のDispatcherにすると…

計算処理Dispatcherのスレッド数を多くするなど

IO と 計算のどっちに比重を置くかチューニングができる

cruch…ボリボリ噛む

なるべく全てのActorをbuzyにしたい時

Number cruching ⇒ BalangingDispatcherを使う

IO処理が伴うので、Durableを使う時は他のアクターと分離させる

Message durability⇒ DurableとDispatcherを使う

チューニングについてLet it crashの

Tuning Dispatchers in Akka Applications参照

Dispatcherの設定

デフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

なんやかんや言ってもデフォルト設定で良い場合が多い

Typesafe Consoleでチェック

Dispatcher view でメッセージハンドリングのlatency、mailbox-sizeなどが見れる

(ちゃんと調べてませんが、現在は無料で使えないようです…)

profilingしてheavy computationな部分を特定し

⇒RouterとBalancingDispatcherを使って外に出す

mailbox sizeが増え続けているならRouterを作った方がよい

デフォルトでもスレッド数の上限が決まっているので、

コア数を増やしても別のDispatcherを使わないと

futureがタイムアウトになったり、スループットが上がらない

typeデフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← Dispatcherの種類

最優先処理がある場合PinnedDispatcher(よっぽど特別な場合だけ使用する)

負荷分散したい時はRouterとBalancingDispatcher

executorデフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

←executorの種類

fork-join-executorの方が良い(PinnedDispatcherはthread-pool-のみ)

Let it crashの記事より、thread-poolからfork-joinにしたら1100%増になった

Throughput比較

task-queueへのアクセス/ロックが多い ⇒ context switchingが発生しやすい

throughputデフォルトの設定

default-dispatcher{type=Dispatcherexecutor="fork-join-executor"throughput=10

fork-join-executor{parallelism-min=4parallelism-max=32parallelism-factor=4.0

}}

← throughput

Actor数≒スレッド数なら多め時間のかかるタスクが多いなら少なめ(頻繁に切り替わると時間がかかる)

デフォルト値から初めてチューニングすべし

まとめ

・Actorにスレッドが割り当てられると、数個のメッセージを処理して

 スレッドプールに戻る

・パフォーマンスが悪い部分を別DispatcherとかRouterで切り出す

・Actorを小さく作れば、どのDispatcherに属させるか柔軟にチューニングできそう(チームの先輩談)