KZKY memo

自分用メモ.

怒涛の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

非常に簡単で次の手順を行う.

インターフェイス/ 実装

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を入れる.
trait HasName {
      def name(): String
}
 
class Named extends HasName {
      import scala.util.Random
      private val id = Random.nextInt(1024)
 
      def name(): String = "name-" + id
}
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()
}