Scala Akka TypedActor与编写自己的Actor类静态接口
我已经使用Akka和Scala大约一个月了,我有点担心用消息替换显式接口。考虑以下简单的Akka演员:Scala Akka TypedActor与编写自己的Actor类静态接口,scala,akka,actor,typedactor,Scala,Akka,Actor,Typedactor,我已经使用Akka和Scala大约一个月了,我有点担心用消息替换显式接口。考虑以下简单的Akka演员: case class DoMyHomework() class Parent extends Actor { def receive = { case d: DoMyHomework => // do nothing } } 参与者或非参与者代码,向该参与者发送DoMyHomework消息,如下所示: ActorRef parent = ... parent.ask(D
case class DoMyHomework()
class Parent extends Actor {
def receive = {
case d: DoMyHomework => // do nothing
}
}
参与者或非参与者代码,向该参与者发送DoMyHomework消息,如下所示:
ActorRef parent = ...
parent.ask(DoMyHomework)
object Parent {
/**
* Send this message to make your parent do your homework … yeah, right ;-)
*/
case object DoHomework
}
/**
* This actor will do your homework if asked to.
*
* ==Actor Contract==
*
* ===Inbound Messages===
* - '''DoHomework''' will ask to do the homework
*
* ===Outbound Messages===
* - '''HomeworkResult''' is sent as reply to the '''DoHomework''' request
*
* ===Failure Modes===
* - '''BusinessTripException''' if the parent was not home
* - '''GrumpyException''' if the parent thinks you should do your own homework
*/
class Parent extends Actor {
…
}
不知道结果会是什么。答案的类型是什么?我会得到答案吗?我能得到一个例外吗?等等
解决方案似乎是记录案例类。。。但如果其他参与者也收到了相同的案例类呢。然后,接收该消息的文档应该在actor本身中
为了稍微清理一下,我想到了以下几点:
trait SomeoneSmarter {
def wouldYouDoMyHomework: Future[Boolean]
}
class Parent extends Actor with SomeoneSmarter {
case class DoMyHomework()
def wouldYouDoMyHomework = {
(self ? DoMyHomework()).mapTo(Boolean)
}
def receive = {
case d: DoMyHomework =>
// TODO: If I'm busy schedule a false "No way" reply for a few seconds from now.
// Just to keep their hopes up for a while. Otherwise, say sure right away.
}
}
trait SomeoneSmarter {
def wouldYouDoMyHomework : Boolean
}
class Response()
case class NoWay() extends Response
case class Sure() extends Response
class ActorNetworkFrontEnd extends Actor {
def receive = {
case d: DoMyHomework =>
busy match {
case true => sender ! NoWay()
case false => sender ! Sure()
}
}
}
case class SomeoneSmarter(actorNetworkFrontEnd:ActorRef) extends SomeoneSmarter {
def wouldYouDoMyHomework : Boolean = {
val future = actorNetworkFrontEnd ? DoMyHomework()
val response = Await.result(future, timeout.duration).asInstanceOf[Response]
response match {
case NoWay() => false
case Sure() => true
}
}
}
所以,我和同事们聊了聊,其中一个反应是“你没有忠于演员模型。”
首先,我非常感谢长期使用演员的人们的指导。所有的信息都变得难以处理了吗?您是否最终隐藏了在接口后面传递的消息
我提议的演员仍然可以选择在他们之间发送消息,订阅事件流,所有你期望从Akka得到的东西。这个界面为你提供了一种经过时间考验的方式来了解你在说什么。在IDE中进行编码时,它会有所帮助,等等。为什么一个参与者的用户需要知道它是一个参与者(除非它也是一个参与者并且与之紧密结合)
我得到的另一个反应是“看起来你想要一个打字机”。但在读了TypedActor之后,我不相信。当然,TypedActor为我省去了创建这些内部消息的麻烦。但是,至少从
我得到的印象是,TypedActor只是用来作为一个代理来处理您想要封装的代码块,或者使线程安全,或者干脆不直接从当前线程调用。您编写的只是实现和接口。您不会弄乱参与者本身(代理)——例如,如果您希望实现执行定期工作或订阅事件流,或者执行与接口无关的任何其他操作
我也读过,没有发现这个例子更有启发性。我可能只是不是在摸索TypedActor(并不是说我已经真正了解了演员)
提前谢谢你的帮助
皮诺免责声明:我不是阿克卡/演员专家。我已经和演员和Akka一起工作了18个月了,我仍然在努力思考某些概念,尤其是在不使用Akka的时候 对于您想知道阿克卡期货的回报类型的特殊且狭义的情况,是的,您应该使用TypedActor。在我使用TypedActor的几次中,它们被用来为Actor系统之外的模块提供API。也就是说,我在Akka上构建了一个系统,它在Akka网络内完成了大部分工作,但在Akka网络外有一个或两个模块,需要访问Akka网络提供的功能。最值得注意的是Scalatra前端,它调用Akka网络,并在响应其客户机之前对Akka网络返回的值进行一些处理。然而,TypedActor实际上只是Akka网络的前端。我将使用TypedActor作为外部(Akka网络外部)模块的API前端,作为另一种关注点分离 总的来说,我同意那些告诉你“你没有忠实于演员模型”的人试图强制其返回类型的视图。在最纯粹的形式中,也是我最成功的方式中,Actor模型是使用fire-and-forget语义实现的。这些消息不会变得笨拙,在许多情况下,它们帮助组织了我的代码并定义了工作边界。把它们放在自己的包里确实有帮助 如果我要实现您描述的功能,它将如下所示:
trait SomeoneSmarter {
def wouldYouDoMyHomework: Future[Boolean]
}
class Parent extends Actor with SomeoneSmarter {
case class DoMyHomework()
def wouldYouDoMyHomework = {
(self ? DoMyHomework()).mapTo(Boolean)
}
def receive = {
case d: DoMyHomework =>
// TODO: If I'm busy schedule a false "No way" reply for a few seconds from now.
// Just to keep their hopes up for a while. Otherwise, say sure right away.
}
}
trait SomeoneSmarter {
def wouldYouDoMyHomework : Boolean
}
class Response()
case class NoWay() extends Response
case class Sure() extends Response
class ActorNetworkFrontEnd extends Actor {
def receive = {
case d: DoMyHomework =>
busy match {
case true => sender ! NoWay()
case false => sender ! Sure()
}
}
}
case class SomeoneSmarter(actorNetworkFrontEnd:ActorRef) extends SomeoneSmarter {
def wouldYouDoMyHomework : Boolean = {
val future = actorNetworkFrontEnd ? DoMyHomework()
val response = Await.result(future, timeout.duration).asInstanceOf[Response]
response match {
case NoWay() => false
case Sure() => true
}
}
}
请记住,我写的方式会影响你的家庭作业,它会在等待答案时阻塞。然而,有一些聪明的方法可以异步完成这项工作。有关更多信息,请参阅
此外,请记住,一旦您的消息进入Akka网络,您就可以完成所有很酷的缩放和远程处理工作,TypedActor API的用户永远不必知道
这样做确实在大型项目中增加了一些复杂性,但是如果您认为它是将API提供给外部模块的责任分离,甚至可能将责任转移到另一个包,那么它很容易管理。
好问题。我迫不及待地想听到更有经验的Akka开发人员的回答。计算的参与者模型与面向对象编程有着巨大的相似性。OO是关于控制权的间接转移。当您调用方法(发送消息)时,您将失去对消息的控制。当然,静态编程语言在所有类型检查方面都有一点帮助,但除此之外,您不知道消息会发生什么。见鬼,也许这个方法永远不会返回,尽管返回类型清楚地表明它会返回(抛出异常、活动锁,你可以命名它…)!同意,当您习惯Java甚至更好的Scala时,放弃静态类型很糟糕,但这并不是说您没有任何好处。动态类型为您提供松散耦合。例如,不需要创建额外的接口来为测试引入模拟参与者
ActorRef
是您唯一需要的API。Actor封装
让我首先回应一点,我认为这是非常重要的。你说:
为什么一个参与者的用户需要知道它是一个参与者(除非它也是一个参与者并且与之紧密结合)
actor是一种与传统OO截然不同的编程范式,主要区别在于一切都是异步的,因此永远不存在真正的“返回值”。这意味着它通常是一个坏id