48
Real World Android Akka

Real world android akka

Embed Size (px)

Citation preview

Real World Android Akka

Who am I?

麻植泰輔 / Taisuke OeTwitter: @OE_uiaGitHub: taisukeoe

会社

- BONX INC. でAndroidアプリ書いたりスノー

ボードするお仕事

- ScalaMatsuri座長 / Japan Scala Association, Inc.常務理事

個人

- Scalaを教える仕事しています

スポンサー募集CFPスタッフ募集

OPEN!!

What’s the topic?

本日のお話

本日のお話

Android

+

Scala

+

Akka

VoIPクライアント

本日のお話

◉ Akka○ メッセージ駆動○ Actorの実行モデル○ Actorのヒエラルキー

◉ BONX○ BONXとは?○ BONX VoIPクライアントの要求仕様

◉ Android Akkaの注意点○ Dispatcher, Mailboxの実装など

◉ Scala Androidってどうなの?

Akka?

Akka

◉ メッセージ駆動で並行/分散処理するtoolkit◉ Lightbend CTO Jonas Bonér氏が作者◉ 名前の由来はスイスの先住民の神様◉ スイスのLaponiaという山がAkka神のシンボル

Message Driven?

メッセージ駆動?

メッセージ駆動

Actorがメッセージを送り合い協調動作することで、システムを構成

あとはたのむ

メッセージ駆動

あとはたのむ次に手が空いてからやるわ

Actorが受け取ったメッセージは、そのActorに紐付いたMailboxに入る

メッセージ駆動

あとはたのむ次に手が空いてからやるわ

Actorはメッセージ一つ一つを逐次処理する

=> (特にスレッドセーフではない)状態管理を伴う並行プログラミングに有用

メッセージ駆動

あとはたのむ 今からやるお

Actorは他のActorについては、メッセージの送信先と受け取れるメッセージの種類についてのみ知っておけばよいため、Actor同士を疎結合にしやすい

Akka Dispatcher

Actorの実行モデル

Actorの実行モデル

Thread

ThreadDispatcher

DispatcherがActorの実行基盤。内部にThreadPoolを保持。

Actorの実行モデル

Thread

ThreadDispatcher

1つのActorは、同時に一つのスレッドにだけ割り振られる

Actorの実行モデル

Thread

ThreadDispatcher

Threadが空いたら、次のActorが割り振られる

Actorの実行モデルDispatcher

Actorは、Mailboxにメッセージが有り、なおかつThreadが割り当てられてるときにメッセージを処理する

Actorプログラミングのアンチパターン

Thread

ThreadDispatcher

Actorが長時間Threadをブロックすると、同じdispatcherを使用しているActorシステム全体の処理が進みにくくなる

進捗ダメです

Akka Actor Hierarchy

Actorのヒエラルキー

Actorの階層構造Dispatcher

Actorには親子関係が作れる

ワシが育てた

Actorの階層構造Dispatcher

子ActorでThrowableが投げられると、親ActorのsupervisorStrategy関数が呼ばれる

_人人人人人人人人人_

> 突然のThrowable < ̄Y^Y^Y^Y^Y^Y^Y^Y ̄

Actorの階層構造Dispatcher

supervisorStrategyの実装により、その後の子Actorの処遇が決定される

Restart

Akkaについて - まとめ

◉ Actorがメッセージを送り合うことでシステムを構成◉ 1つのActorは同時に1メッセージを1スレッドで逐次処

理するため、状態管理が楽◉ 実行モデル

○ Dispatcher … Thread(pool)の保持と、ActorへのThread貸出

○ Mailbox … Actorに送られたメッセージ置き場

◉ ActorのヒエラルキーとSupervisorStrategyの実装を適切に設定することで、障害からの回復力をもつ

The Way We Talk

BONX

BONXとは?

野外の激しい運動中でも

複数の仲間とスムーズに

コミュニケーションとれるVoIPサービス

VoIP = Voice over Internet Protocol

Requirement specification in BONX VoIP client

BONX VoIPクライアントの要求仕様

BONX VoIPクライアントの要求仕様

◉ 状態管理を伴う並行プログラミング

◉ (それなりに)低遅延

◉ 障害からの回復力

VoIPは音声ストリーム処理

ソケット通信録音 音量調整 発話検知 エンコード

デコード再生

音声ストリーム処理: 並行プログラミング

◉ 1フレームの処理が終わる前に、次のフレームの音声デー

タが送られてくる

◉ 必然的に並行プログラミングすることになる

◉ しかしエンコーダー・デコーダーなどのモジュールは、多く

の場合Threadセーフではない

Actorモデル向き

低遅延

◉ 合計の遅延:大きくなりすぎると、(特に顔が見えていると

き)通話中に違和感

◉ 個々の遅延:フレーム間の遅延が大きくなりすぎると、「ブ

ツッ」という音の途切れの原因

Dispatcher, Mailboxのカスタマイズ性は必要十分

障害からの回復力

◉ クライアントアプリとしては長時間(~8時間)の連続使用を想定

◉ 使用中、録音/再生のハードウェア I/Oなど一部のモジュールに問題が発生するケースがある

◉ 何かの状態がおかしい場合は、関連するモジュール含めて、自動で再初期化が必要

AkkaのActorヒエラルキーを適切に設定しておくと、モジュール群を自動で再初期化することで障害から回復できる

Pitfalls in Android Akka

Android Akkaの注意点

Android Akkaの注意点

◉ Akka version◉ Proguard設定◉ Dispatcherの罠◉ Mailboxのサイズ

Akka version

◉ AndroidはJava6/7相当の環境◉ Akka 2.14系はJava8要求

=> Akka 2.13系を使う

Proguard

● Akkaはapplication.confやPropsを基に、リフレクションでActorなどをインスタンス化

● Proguardは使用されていないクラス、メソッドをバイトコードから抜くpost-processingツール

● Proguardはリフレクションを理解しない● Android ScalaにおけるNoSuchMethodErrorの発生原因

の殆どはProguard起因

=> proguard.cfgに随時指定

参考: Macroid | Akka Integration

Dispatcherの罠

application.confのparallerism-factorは、(min~maxの範囲で)availableProcessorsに乗じた数のThreadPoolを作る

akka.dispatch.ThreadPoolConfig

object ThreadPoolConfig {/* … */ def scaledPoolSize(floor: Int, multiplier: Double, ceiling: Int): Int = math.min(math.max((Runtime.getRuntime.availableProcessors * multiplier).ceil.toInt, floor), ceiling)/* … */}

AndroidのavailableProcessorsAndroidの場合、電力消費を抑えるため、高クロックと低クロックのコアから構成されているヘテロCPUが主流。そのため、一度に全てのCPUコアがonlineにならない端末が多い。

しかしRuntime#availableProcessorsは常に全合計コア数を返す。この数字をそのまま使うと、Thread数がコア数に対して多くなりすぎてパフォーマンスが低下することがある

/sys/devices/system/cpu/online では 1-5,6 のようにオンライン状態のCPUのindexをチェックできる (参考:Linux kernel related to hotplug a CPU)

カスタムDispatcher

akka.dispatcher.ExecutorServiceConfiguratorを継承したクラスのFQCN(フル修飾名)をapplication.confに指定可能

my-dispatcher { type = Dispatcher executor = "com.taisukeoe.MyExecutorConfigurator" fork-join-executor { parallerism-min = 8 parallerism-factor = 2 parallerism-max = 16 }}

カスタムExecutorServiceConfigurator

class MyExecutorConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {

final def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = {

val c = config.getConfig("fork-join-executor")

new ForkJoinExecutorServiceFactory( threadFactory.asInstanceOf[ForkJoinPool.ForkJoinWorkerThreadFactory], CPUConfig.scaledPoolSize( c.getInt("parallelism-min"), c.getDouble("parallelism-factor"), c.getInt("parallelism-max")), true) }}

OnlineのCPUコア数を取得

object CPUConfig { private lazy val rangeRegex = "([0-9]+)\\-([0-9]+)".r

def onlineProcessors: Option[Int] = Try { val reader = new BufferedReader(new FileReader("/sys/devices/system/cpu/online")) val num = Iterator.continually(reader.readLine()).takeWhile(_ != null).toSeq.headOption map { cpu:String => cpu.split(",").map { case rangeRegex(from, to) => to.toInt - from.toInt + 1 case num => 1 }.sum reader.close() num }}.toOption.flatten

def scaledPoolSize(floor: Int, multiplier: Double, ceiling: Int): Int =math.min(math.max(onlineProcessors.getOrElse(Runtime.getRuntime.availableProcessors) * multiplier).ceil.toInt, floor), ceiling)}

Mailboxのサイズ

◉ Androidは(増えてきたとはいえ)基本的には低メモリ環境◉ 1 VM ~ 数百MB RAM◉ サイズ制限のないMessageBoxだとOutOfMemoryErrorのおそれ◉ できるだけ有限サイズのMailbox (BoundedMailbox) を使う方が

良い◉ それで必要要件をみたさないときは、カスタムMailboxを作るべき

参考: Mailboxes -- Akka

my-mailbox { mailbox-type = "com.taisukeoe.MyMailBox"}

Android Scala Pros & Cons

ScalaでAndroidってどうなの?

ScalaでAndroidってどうなの?

◉ ScalaとAndroid両方に詳しい人がチームに1人いたら選択肢として

アリ。チーム2人目以降は、どちらかについて詳しければ、教育可能

◉ sbt-androidが(クセはあるけど)とても優秀。組み合わせると便利な

sbt pluginも色々

◉ 今回のケースではScala + Akka + Androidのメリットが存分に活き

○ 常時1~2人分の工数の少人数チームで新規機能開発&メンテ

○ 必要な低遅延/スループットなどのパフォーマンスを達成しつ

つ、モジュールの疎結合化、コードの抽象化に(それなりに)成功

ScalaでAndroidってどうなの?

◉ 万人にオススメはしない

◉ ScalaとAndroid両方の知識がそれなりに必要

◉ ビルドが長い (Proguardのせい)

◉ Googleのサポート外

○ Scala2.12 & Java8対応? ■ Jack & JillがJava8対象

○ Jack & Jill対応?

■ sbt-androidにPullReqは一応....

まとめ

◉ Akkaはメッセージ駆動で並行/分散処理を便利に行えるtoolkit

◉ Androidでも幾つかのポイントに注意すれば、Akkaは十分実用的である。

◉ VoIPアプリの音声ストリーム処理など、AndroidでもActorモデルが非常に有用となるケースがある

We’re hiring!

◉ VoIPクライアントアプリに興味がある人◉ Android Scalaを触ってみたい人◉ Akkaのチューニングをゴリゴリしたい人◉ スノボ、サイクリング 等々が好きな人

お気軽に @OE_uia までメッセください