我有一个actor,它创建一个子actor来执行一些冗长的计算。
问题是子actor的初始化需要几秒钟,并且父actor在它之间发送给子节点的所有消息都被删除了。
这是我正在使用的代码的逻辑:
class ChildActor extends Actor {
val tagger = IntializeTagger(...) // this takes a few seconds to complete
def receive = {
case Tag(text) => sender ! tagger.tag(text)
case "hello" => println("Hello")
case _ => println("Unknown message")
}
}
class ParentActor extends Actor {
val child = context.ActorOf(Props[ChildActor], name = "childactor")
// the below two messages seem to get lost
child ! "hello"
child ! Tag("This is my sample text")
def receive = {
...
}
}
我怎么能解决这个问题?是否可以让父actor等到孩子完全初始化?我将使用带有路由的子actor,可能还有远程actor系统。
修改
根据drexin的建议,我已将代码更改为:
class ChildActor extends Actor {
var tagger: Tagger = _
override def preStart() = {
tagger = IntializeTagger(...) // this takes a few seconds to complete
}
def receive = {
case Tag(text) => sender ! tagger.tag(text)
case "hello" => println("Hello")
case _ => println("Unknown message")
}
}
class ParentActor extends Actor {
var child: ActorRef = _
override def preStart() = {
child = context.ActorOf(Props[ChildActor], name = "childactor")
// When I add
// Thread.sleep(5000)
// here messages are processed without problems
// wihout hardcoding the 5 seconds waiting
// the below two messages seem to get lost
child ! "hello"
child ! Tag("This is my sample text")
}
def receive = {
...
}
}
但问题仍然存在。我错过了什么?
答案 0 :(得分:17)
不要在构造函数中初始化tagger
,但是在preStart
挂钩中,这样消息将在消息框中收集并在actor准备就绪时传递。
修改强>:
您应该在ParentActor
课程中为演员创建做同样的事情,因为如果ChildActor
会在ParentActor
初始化之前响应,您会遇到同样的问题。< / p>
<强> EDIT2 强>:
我创建了一个简单的例子,但我无法重现你的问题。以下代码完美无缺:
import akka.actor._
case class Tag(x: String)
class ChildActor extends Actor {
type Tagger = String => String
var tagger: Tagger = _
override def preStart() = {
tagger = (x: String) => x+"@tagged" // this takes a few seconds to complete
Thread.sleep(2000) // simulate time taken to initialize Tagger
}
def receive = {
case Tag(text) => sender ! tagger(text)
case "hello" => println("Hello")
case _ => println("Unknown message")
}
}
class ParentActor extends Actor {
var child: ActorRef = _
override def preStart() = {
child = context.actorOf(Props[ChildActor], name = "childactor")
// When I add
// Thread.sleep(5000)
// here messages are processed without problems
// wihout hardcoding the 5 seconds waiting
// the below two messages seem to get lost
child ! "hello"
child ! Tag("This is my sample text")
}
def receive = {
case x => println(x)
}
}
object Main extends App {
val system = ActorSystem("MySystem")
system.actorOf(Props[ParentActor])
}
输出是:
[info] Running Main
Hello
This is my sample text@tagged
答案 1 :(得分:8)
我认为您可能正在寻找的是Stash
和become
的组合。这个想法是,子actor会将它的初始状态设置为未初始化,并且在此状态下,它将隐藏所有传入的消息,直到它完全初始化为止。完全初始化后,您可以在将行为交换到初始化状态之前卸载所有消息。一个简单的例子如下:
class ChildActor2 extends Actor with Stash{
import context._
var dep:SlowDependency = _
override def preStart = {
val me = context.self
Future{
dep = new SlowDependency
me ! "done"
}
}
def uninitialized:Receive = {
case "done" =>
unstashAll
become(initialized)
case other => stash()
}
def initialized:Receive = {
case "a" => println("received the 'a' message")
case "b" => println("received the 'b' message")
}
def receive = uninitialized
}
请注意preStart
我正在异步进行初始化,以便不停止actor的启动。现在这有点难看,关闭了可变dep
var。您当然可以通过向另一个处理缓慢依赖项实例化的actor发送消息并将其发送回此actor来处理它。收到依赖关系后,它会调用become
状态为initialized
。
现在有一个警告Stash
,我会将其粘贴在Akka文档中:
Please note that the Stash can only be used together with actors that
have a deque-based mailbox. For this, configure the mailbox-type of the
dispatcher to be a deque-based mailbox, such as
akka.dispatch.UnboundedDequeBasedMailbox (see Dispatchers (Scala)).
现在,如果这不适合你,你可以尝试更多的DI
类型方法,并通过它的构造函数将缓慢的依赖注入到子actor中。所以你要像这样定义子actor:
class ChildActor(dep:SlowDependency) extends Actor{
...
}
然后在启动这个actor时,你会按如下方式执行:
context.actorOf(new Props().withCreator(new ChildActor(slowDep)), name = "child-actor")
答案 2 :(得分:0)
我建议发送&#34;准备好&#34;从子actor到父节点的消息,只有在收到此消息后才开始向子actor发送消息。您可以在receive()
方法中为简单用例执行此操作,或者可以在初始化子项后使用become
或FSM
更改父actor行为(例如,存储消息在一些中间存储中的孩子,并在它准备就绪时发送它们。