2016-12-16 3 views
2

私はかなり頻繁に俳優に直面する問題を抽象化し、それを解決するための共通のデザインパターンを許可します。Akka兄弟再帰ActorRefの必要条件

── user 
    └── ServiceActor 
     ├── TicketManager 
     │   ├── WorkerA 
     │   ├── WorkerB 
     │   └── WorkerC 
     └── UserManager 
      ├── WorkerA 
      ├── WorkerB 
      └── WorkerC 

チケットやユーザーマネージャは、重要ではありません。次のアクター階層があるとします。私は、TicketManagerがUserManagerにメッセージを送り、その逆を行うという要件を持っています。 (これらは私が使用した例の名前に過ぎず、実際の問題を代表するものではありません)

次のうちどれが最適ですか?

オプション1:

TicketManagerコンストラクタは、コンストラクタの引数としてのUserManagerを取ります。 UserManagerコンストラクタは、TicketManagerをコンストラクタ引数として受け取ります。 しかし、両方ともお互いに再帰呼び出しを行うことはできません。したがって、これはおそらく不可能です。

オプション2:

UserManagerがactorSelectionとしてactorSelection(「../ TicketManager」)を介してTicketManagerを参照するなど要件に、または一度だけ起動時に、それを維持します。

オプション3:時間の任意の時点で

、のUserManager/TicketManagerは、それらの他の兄弟を参照するための親(ServiceActor)を要求し、親がその参照を保持するように適切actorRefバック返信されます。

オプション4:

この状況は決してありませんか?何らかの形でデザインを「複雑にする」ように、お互いに話し合っている兄弟はないと思いますか?木のような階層があればいいですか?そのような場合にそれを避けるのに役立つ共通のデザインパターンは何ですか?

私は要件を明確にしたいと考えています。必要に応じてさらに明確にすることを許可してください。

+0

すなわち初期化戦術は、エドモンドによって提案された洞察に満ちたもののだろう、私はオプション4とオプションの混合物をやってしまいました3.私の俳優階層の混乱を解くオプション4。それを超えて、お互いに話す必要がある瞬間、彼らは親を介して話しました。それは合理的なパターンだと思う。 – Ra41P

答えて

2

オプション4は、依存関係が複雑になる場合はおそらく最良のアプローチですが、2人の俳優にとっては、単にメッセージを使用して初期化することができます:俳優は可変状態を維持するように設計されています。

だから私はあなたが探していたものを、複数のActorSystemを使用していたことServiceDiscoveryのいくつかの並べ替えになる場合TickerManagerとActorManagerを作成する責任親俳優は、以下の

case class InitializeTicketManager(userManager:ActorRef) 

case class InitializeUserManager(ticketManager:ActorRef) 
val userManager = context.actorOf(...) 
val ticketManager = context.actorOf(...) 
userManager ! InitializeUserManager(ticketManager) 
ticketManager ! InitializeTicketManager(userManager) 
+0

答えをありがとう。しかし、2人以上の俳優がいればどうでしょうか?チケット、ユーザ、キオスクなどの管理者がトップレベルのServiceActorのすぐ下に同じフラットレベルにあり、それぞれが深いツリーを作成するために十分な下位責任を持たない場合はどうなりますか?あなたはまだinitメソッドを使って初期化を提案していますか? – Ra41P

1

を行うことができますと言うでしょう。これらも役立ちます(クラスタルーティング、クラスタシャーディング、ClusterRouterGroup)。

あなたの方が簡単ですが、ActorSystemは1つだけです。私は怠け者ヴァルスのひねりを加えたオプション1を使用して、コールによって、名前

package sumnulu 

import akka.actor.{Actor, ActorLogging, ActorRef, Props} 

/** 
    * Created by sumnulu 
    */ 
class RootActor extends Actor with ActorLogging { 
    lazy val serviceA: ActorRef = context.actorOf(ServiceA.props(serviceB)) 
    lazy val serviceB: ActorRef = context.actorOf(ServiceB.props(serviceA)) 

    override def preStart() = { 
    log info "Root Actor Ready" 
    serviceA ! "test" 
    } 

    override def receive = Actor.ignoringBehavior 
} 

class ServiceA(serviceB: ActorRef) extends Actor { 
    println("serviceA constructor") 
    serviceB ! "Hi" 


    override def receive: Receive = { 
    case x => println(s"self:$self from:$sender msg:$x") 
    } 
} 


class ServiceB(serviceA: ActorRef) extends Actor { 
    println("serviceB constructor") 
    serviceA ! "Hi" 

    override def receive: Receive = { 
    case x => println(s"self:$self from:$sender msg:$x") 
    } 

} 

//call-by-name important is i.e `=>` 
object ServiceA { 
    def props(serviceB: => ActorRef) = Props(new ServiceA(serviceB)) 
} 

//call-by-name important is i.e `=>` 
object ServiceB { 
    def props(serviceA: => ActorRef) = Props(new ServiceB(serviceA)) 
} 

object RootActor { 
    def props = Props[RootActor] 
} 

[info] [INFO] [02/18/2017 17:49:52.485] [Main-akka.actor.default-dispatcher-4] [akka://Main/user/app] Root Actor Ready 
[info] serviceA constructor 
[info] serviceB constructor 
[info] self:Actor[akka://Main/user/app/$a#1420483478] from:Actor[akka://Main/user/app#274353135] msg:test 
[info] self:Actor[akka://Main/user/app/$b#1573183992] from:Actor[akka://Main/user/app/$a#1420483478] msg:Hi 
[info] self:Actor[akka://Main/user/app/$a#1420483478] from:Actor[akka://Main/user/app/$b#1573183992] msg:Hi 
+0

ああ、うわー!このようなことは決して考えなかった。私は値による呼び出しと名前による呼び出しの違いを理解していますが、なぜこれが問題を解決するのですか?あなたはそれについていくつかの詳細を追加できますか? – Ra41P