並行処理初心者のためのAkka入門

Preview:

DESCRIPTION

Concurrent programing explanation for akka beginers. 並行処理初心者のためのAkka入門 akka meetup 2014/09/28(日) http://connpass.com/event/8622/ このイベントの導入説明のために書かれました。 内容には、並行処理、アクターモデル、Akkaの機能の説明となっています。

Citation preview

並行処理初心者のためのAkka入門

株式会社ドワンゴ 吉村総一郎@sifue

今日はAkkaの勉強会

そもそもAkkaとは何か?

そんな人のためのプレセッション

Akkaとは• ScalaとJavaのApach2ライセンスで提供され

ているOSSのライブラリ

• アクターモデルを適用したスケーラビリティや耐障害性に優れた並行処理が書けるライブラリ

• Play Framework 2でも利用されている

http://akka.io/

Akkaを知る前に

•並行処理•アクターモデル

この二つを知らないといけない。今から上記二つをざっくり説明して、Akkaの機能もざっくり解説する

並行処理プログラミングとは

•この本の内容をざっくり•まだ書店にはあるかも?

Java並行処理プログラミング ―その「基盤」と「最新API」を究める―

ブライアン・ゲーツダグ・リー

並行処理の歴史1. OSのプロセスによる並行処理

• 資源の有効利用: 入出力のような外部操作の待ち時間

• 公平性: 複数のユーザーで複数の処理

• 利便性: 一つのプログラムで一つのタスクの方がシンプル

➡タイムシェアリングシステムでメモリは別

2. スレッド (軽量プロセス)による並行処理

• プロセスと同じ動機

• ひとつのプロセス内で複数のお仕事

• メモリ、ファイルハンドルは共有

• 共有データへのアクセスは明示的な同期化が必要

スレッドの利点•マルチプロセッサの有効利用• 1つのスレッドに1つのタスクで設計の単純化※

•サーバーへのクライアント接続などの処理を単純化※

•応答性の良いUIの実現※問題点でもある

スレッドのリスク: 安全性の危機

インクリメントと取得が別スレッドで交互に実行

p.7より

スレッドのリスク: デッドロック

リソースをロックし合って永遠に待つ

パターン

p.233より

スレッドのリスク: 実行性能の危機

•スレッドが増えると、プロセス上のスレッドの入れ替えのためのコンテキストスイッチのコストが増大

スレッドセーフの解決案1.ステートレスにする

2.ロックして同期化処理を行う

• メモリの可視性にはすごく注意が必要 (激難)

変数の更新が別スレッドからは見えない

p.40より

オブジェクトの共有の難しさ

• synchronizedによるlockでの同期• volatileによる揮発性変数の利用•ただし同一スレッドからの書き込みのみの場合に限る

•同期化されてない内部変数を逸出しないような入念なチェックが必要

並行処理プログラミングってとてもむずかしい...

→ concurrentパッケージのお陰でなんとか

ちなみにJavaのconcurrentパッケージの概要

名前 内容Executor フレームワーク スレッドを、CallableとFutureで扱えるようにしたり、

並列度やリトライ、スケジュールを制御できる

Concurrent コレクション Javaのコレクションをマルチスレッドで扱えるようにしたコレクション群、putIfAbsentとかもある。

Atomic パッケージ AtomicLongやAtomicReferenceなど、並行処理で変更しても問題が起こらない値や参照のクラス群

ReentrantLock 再利用可能なロック。いわゆる排他処理とかMutexとかいわれるものを実現。tryLockとlockの両対応。

CyclicBarrier 全スレッドがバリアに到達するまで待って、何かを実行してくれる部品

CountDownLatch 指定した数まで処理が実行されるまで、全スレッドを待たせてくれる部品

Semaphore 一度に指定された数のスレッドしか実行されないようにしてくれる部品

DelayQueue 特定の時間が経過すると中身を取得できるようになるキュー

とはいえ、チーム開発してるとよくわからんバグが起こりやすい

→ なぜかテストが2回に1回落ちるw

誰かが界王拳みたいなものだと言ってた

(数倍の力出るけど死ぬかもしれない...)

そこで登場

アクターモデル

アクターモデルの歴史

•将来的に「数百・数千のマイクロプロセッサから構成され、個々にローカルメモリを持ち、高性能通信ネットワークで通信を行う並列コンピュータが近い将来登場するとの予測」から開発された

現状スケールのためには

•マルチコア•クラスタ構成

が、今の大規模サービスの基本戦略なので、すごく今の現状にマッチ

1985年ぐらいにアクターモデル理論として完成したらしい…ここらへんの学術的な話は→ Wikipediaのアクターモデルを見てください

アクターモデルとは

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

アクターという処理行い、状態を持つオブジェクト

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

それぞれメールボックスとアドレスがある

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

アクター内ではループで逐次処理しながら、メッセージを別なアクターに投げたりして、メールボックスに溜まった命令を

順不同で処理 (Akkaではデフォルトでは到着順で処理)

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

メッセージパッシングすることで非同期に処理し、単一のループで逐次処理しているので、並行処理のロックや同期が不要

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

さらにAkkaでは、子どものActorを作り、それぞれにラウンドロビンでメッセージをブロードキャストしたりすることもできる

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

すばらしい!

アクター

メールボックス

メッセージ

address

address

address

address

address

address

ループ

Akka Actorの主な特徴• ActorSystemというActorの実行コンポーネントが内部的にスレッドプールを持っており、tellによるメッセージパッシングのイベント駆動モデルと、マルチスレッドによる実行の融合がなされている

•デフォルトでは、単一実行するMailboxの実装により逐次メッセージが処理される

Actor同士がリモートでメッセージを送信できる

• ActorPathというアドレスを利用•ロケーション透過性があり、アクターの物理的な位置をAkka上では考慮する必要が無い

•ポートは自由に設定可能

耐障害性の機能• SupervisorがActorを監視しており、処理中

に例外を吐いた時に再実行を行わせたりすることができる

• SupervisorStrategyにより子どものアクターが起こしたエラーの挙動を変えられる

• let-it-crashという考え (とっとと壊して新しいモノを再度作ったり、やり直したりする)

Akkaで実現できる機能• 並行処理

• 分散処理のために子どものアクターを作る

• アクターが失敗した時に再実行する

• アクターのメッセージの結果をFutureで受け取る (ask)

• メッセージの送信元を知る

• メッセージ送信のスケジューリング

• リモートのアクターへのメッセージ送信

• アクター同士のゴシッププロトコルによるクラスタリング

• トランザクションの実行とロールバック

• ロギング

どんな用途に向いているか• トランザクション処理

• サービスのバックエンド処理

• 並行処理

• 大規模なシミュレーション計算

• バッチ処理

• コミュニケーションハブ

• オンラインゲーム、賭けなどの実装

• BIやデータマイニング

• 複雑なイベントストリーム処理

スケールアップ、スケールアウト、耐障害性の必要な

http://doc.akka.io/docs/akka/2.3.6/intro/use-cases.html

Akkaの基本的な機能の解説• Actor

• ActorSystem

• tell, ask

• SupervisorStrategy

• ActorPath

• DeadLetter

• Akka Cluster

Actor• メッセージを受信するためのMailboxと、メッ

セージの対する処理を記述するreceiveを持つ

• それぞれのActorは、並列に動作するが、1つのActorは並列にメッセージを処理することはない

• そのため、Actorでスレッドセーフを意識する必要はない

HelloWorld• Actorにメッセージを送信して表示する

ActorSystem

•複数のActorを構成するために必要なもろもろを管理するクラス

• ActorSystemからActorを作っていくことができる

tell, ask

• Actorにメッセージを送信する

tell (別名: “!”)• Actorにメッセージを投げて、返答を待たない

ask (別名: “?”)• Actorにメッセージを投げて、返答を待つ

ブロッキングするのではなく、Futureオブジェクトが返る

SupervisorStrategy• Actorは複数の子供Actorを持つことができる

http://doc.akka.io/docs/akka/snapshot/scala/fault-tolerance.html

親Actor• 子供Actorを管理するための戦略、SupervisorStrategyを持つことができる

• 自分で定義することもできるがすでに2つ定義されている1. OneForOneStrategy

- ある子供Actorが例外で停止した時、その子供Actorだけを再起動する

2. OneForAllStrategy

- ある子供Actorが例外で停止した時、全ての子供Actorを再起動する

SupervisorStrategyの設定の例

ActorPath• ActorにはURIが付いており、それでメッセージを送信できる

akka://system/user/MyActor

ActorPathはActorの親子関係に対応

•MySupervisorがMyActorという子供Actorを持っている場合

akka://system/user/MySupervisor/MyActor

ActorPathからActorにメッセージを投げる

存在しないAcotorにメッセージを投げたらどうなるの?

• DeadLetterになる

DeadLetter• メッセージを投げたけど届かなかった場合

にメッセージはDeadLetterとなる

• 以下の場合もDeadLetterになる

- 送信したがActorが停止してしまった場合

- Actorが持つメールボックス(メッセージの入れ物)に追加できなかった場合

‣ 容量制限に引っかかるなど

Akka Cluster• Akka Remoteを利用した、複数ノード構成

のAkkaアプリケーションを構築するエクステンション

• ノードのメンバー管理、クラスタへのノードのJoin・Leaveを行ってくれている

• 参加のやりとりはゴシッププロトコル

• 論理時間はベクタークロックアルゴリズム

ノードの役割• シードノード

- 最初にクラスタに参加するノードが接続するためのエントリーポイント

• リーダー

- クラスタを管理するノード、リーダーがクラスタから落ちると別のノードがリーダーに昇格する

• ロール

- 各ノードに複数のロールを持たせることができる (ただの文字列)

ノードの一生•クラスタにJoinしてLeaveするまでのライフサイクルが存在する

ノードの状態•ノードは一生の中でいくつかの状態を取る•この状態の管理はリーダーノードが行い、クラスタ全体で共有される

状態 意味joining クラスタに入ろうとしているup クラスタに正常に入った状態

leaving/exiting クラスタから正常に抜けようとしてる状態down ノードが落ちた状態removed クラスタから削除された

Unreachable

•特別な状態、あるノードが通信不能、 ハートビートがタイムアウトした時など

•この状態はdown状態と似ているが、クラスタ全体で共有されるわけではなくノードごとに保持している

Unreachableの検出•任意のノードがUnreachableかどうかは、ノードのFailureDetector(*fd)と呼ばれる仕組みにより検出される

•他のノードからはUnreachableではなくとも、自分からはUnreachableであることもある

利用する時に気をつけること

• ActorSystemをたくさん作らない

• メッセージに型がない

• シリアライズ不可能なメッセージを投げない

• DeadLetterに注意する

• Mailboxのサイズに注意する

• ActorPathに注意する

ActorSystemをたくさん作らない

• スレッドプールができるので生成コストが大きい

• ちなみに、以下の2つのActorSystemは同値ではないので要注意

シリアライズ不可能なメッセージを投げない• Actorはノードをまたいで実行される時もある

•シリアライズ不可能なクラスのインスタンスを投げないこと

➡例外投げて落ちる

メッセージに型がない• Actorが受け取る事のできるメッセージは全部Any

• TypedActorというものもあったりするが基本、メッセージ型を作ったほうが便利

• Actorが処理することができないメッセージを投げても、Actorに無視されるだけ

➡DeadLetterにならない

➡Unhandledになる、Loggerで拾うことは可能

DeadLetterに注意する• Actorに処理して欲しいはずなのに処理してもらっていない

• DeadLetterをログに全て出力しているの設定を確認する

•何個まで出すかなどの設定があるhttp://doc.akka.io/docs/akka/2.3.4/scala/logging.html

MailBoxのサイズに注意する

•メッセージが無限に入るので処理が遅れてることに気がつかない

•対策としては、Mailboxにサイズ制限を設ける

•デフォルトでは無制限

ActorPathに注意する

•メッセージを投げたいActorの実装が変わってActorPathが変わるかもしれない

•対策としては、ActorPathのBuilderクラスを用意したほうが無難

もっとAkkaについて知りたい

• akka-user、akka-devを見る• akkaのコードを読む (https://github.com/akka/akka)

• Effective Akkaという洋書がKindleで読める

以上ご静聴ありがとうございました

Special thanks for @suikwasha (Shoshi TAMAKI)

おまけ

Akka Streamsについて

• 2014年9月現在実験的機能として公開中• Akkaの分散、耐障害性の仕組みを利用してストリーム処理ができる

Akka Streamsの用途

•バルクデータの転送•リアルタイムデータソース•巨大データセットのバッチ処理•監視/解析

http://www.slideshare.net/rolandkuhn/reactive-streams

Akka StreamsでのFlowの処理例

http://typesafe.com/activator/template/akka-stream-scala

• データをストリームとして処理

• Flowの情報受取元はTCPを選択することもできる