怒涛のAkka: Typed Actors
When to use Typed Actors
Active Objectの実装.
Active Objectはメソッド呼び出しとメソッド実行をそれぞれ別のスレッドにするデザインパターン.
インターフェイスを持つクラスをTyped Actorを使うことで非同期にできる.
こうするとActorの実装とOO-likeな実装の切り分けが明確にできる.
弊害としてbecome/unbecomeが使えない
The tools of the trade
インターフェイス一覧が書いてある.
import akka.actor.TypedActor //Returns the Typed Actor Extension val extension = TypedActor(system) //system is an instance of ActorSystem //Returns whether the reference is a Typed Actor Proxy or not TypedActor(system).isTypedActor(someReference) //Returns the backing Akka Actor behind an external Typed Actor Proxy TypedActor(system).getActorRefFor(someReference) //Returns the current ActorContext, // method only valid within methods of a TypedActor implementation val c: ActorContext = TypedActor.context //Returns the external proxy of the current Typed Actor, // method only valid within methods of a TypedActor implementation val s: Squarer = TypedActor.self[Squarer] //Returns a contextual instance of the Typed Actor Extension //this means that if you create other Typed Actors with this, //they will become children to the current Typed Actor. TypedActor(TypedActor.context)
Creating Typed Actors
非常に簡単で次の手順を行う.
- インターフェイスを作る
- 実装を作る
- TypedActor(system).typedActorOf(TypedProps[InterfaceImpl]())
インターフェイス/ 実装
trait Squarer { def squareDontCare(i: Int): Unit //fire-forget def square(i: Int): Future[Int] //non-blocking send-request-reply def squareNowPlease(i: Int): Option[Int] //blocking send-request-reply def squareNow(i: Int): Int //blocking send-request-reply @throws(classOf[Exception]) //declare it or you will get an UndeclaredThrowableException def squareTry(i: Int): Int //blocking send-request-reply with possible exception } class SquarerImpl(val name: String) extends Squarer { def this() = this("default") def squareDontCare(i: Int): Unit = i * i //Nobody cares :( def square(i: Int): Future[Int] = Future.successful(i * i) def squareNowPlease(i: Int): Option[Int] = Some(i * i) def squareNow(i: Int): Int = i * i def squareTry(i: Int): Int = throw new Exception("Catch me!") }
引数つきコンストラクタ
val mySquarer: Squarer = TypedActor(system).typedActorOf(TypedProps[SquarerImpl]()) val otherSquarer: Squarer = TypedActor(system).typedActorOf(TypedProps(classOf[Squarer], new SquarerImpl("foo")), "name")
Method dispatch semantics
- Unit: fire-and-forget = tell
- Future[_]: send-request-reply = ask
- Option[_]: send-request-reply, block, timeoutでNone
- 他: use send-request-reply, block, timeoutでerrorも起きる
Messages and immutability
上記例ではSquarerを呼ぶ.
Stopping Typed Actors
すぐ止める
TypedActor(system).stop(mySquarer)
今までに送った処理をしてから止める
TypedActor(system).poisonPill(otherSquarer)
Typed Actor Hierarchies
- Typed Actorの中でやる
//Inside your Typed Actor val childSquarer: Squarer = TypedActor(TypedActor.context).typedActorOf(TypedProps[SquarerImpl]()) //Use "childSquarer" as a Squarer
通常のActorの中でもTypedActorを作れる.
Typed Actor.get(context)
Supervisor Strategy
TypedActor.SupervisorをTypedActorクラスでインプリする.
詳細は,fault toleranceの章.
Lifecycle callbacks
これらをTypedActorクラスでインプリするとライフサイクルでhookかけられる.
- TypedActor.PreStart
- TypedActor.PostStop
- TypedActor.PreRestart
- TypedActor.PostRestart
Receive arbitrary messages
typedActor.Receiverをインプリするとmethod call以外のメッセージ受け取れるようになるので,他のメッセージともインタラクション可能.
例がない.
Proxying
typedActorOfにTypedPropsとActorRefを渡すと,リモート間にあるTypedActorとコミュニケーションするのにいいらしい.
(例がないと想像つかないし,これ以上しらべてない)
Lookup & Remoting
- 別ノードにあるactorRefToRemoteActorをプロクシに使えるらしい.
val typedActor: Foo with Bar = TypedActor(system). typedActorOf( TypedProps[FooBar], actorRefToRemoteActor) //Use "typedActor" as a FooBar
Supercharging
mix-inもできると.
Typed Router pattern
TypedActorをproxyとしてuntyped routerへ,ルーティングするパターン.
- TypedActorを作るメソッドをつくる
- routeePathsにTypedActorのpath入れる
- RoundRobinGroupに登録して,Actorを作る
- TypedActorを作来るときに,参照にRoundRobinGroupのActorを入れる.
- Named.scala
trait HasName { def name(): String } class Named extends HasName { import scala.util.Random private val id = Random.nextInt(1024) def name(): String = "name-" + id }
- TypedRouterApp.scala
package edu.kzk.actor.typedactor import akka.routing.RoundRobinGroup import akka.actor.TypedProps import akka.actor.ActorRef import akka.actor.TypedActor import akka.actor.ActorSystem object TypedRouter extends App { implicit val system = ActorSystem("Typed-Router") // creation method of typed actor def namedActor(): HasName = TypedActor(system).typedActorOf(TypedProps[Named]()) // prepare routees val routees: List[HasName] = List.fill(5) { namedActor() } val routeePaths = routees map { r => TypedActor(system).getActorRefFor(r).path.toStringWithoutAddress } println(routeePaths) // prepare untyped router val router: ActorRef = system.actorOf(RoundRobinGroup(routeePaths).props()) // prepare typed proxy, forwarding MethodCall messages to `router` val typedRouter: HasName = TypedActor(system).typedActorOf(TypedProps[Named](), actorRef = router) println("actor was: " + typedRouter.name()) println("actor was: " + typedRouter.name()) println("actor was: " + typedRouter.name()) println("actor was: " + typedRouter.name()) system.shutdown() }