当前位置: 首页 > 知识库问答 >
问题:

Akka TypedActor与编写自己的Actor类静态接口

惠诚
2023-03-14

我已经使用Akka和Scala大约一个月了,我有点担心用消息替换显式接口。考虑以下简单的阿卡演员:

case class DoMyHomework()
class Parent extends Actor {
  def receive = {
    case d: DoMyHomework => // do nothing
  }
}

Actor或非Actor代码向这个Actor发送DoMyHomework消息,如下所示:

ActorRef parent = ...
parent.ask(DoMyHomework)

不知道结果会是什么。答案的类型是什么?我会得到答案吗?我能得到一个例外吗?等等

解决方案似乎是记录案例类。。。但如果其他参与者也收到了相同的案例类呢。然后,接收该消息的文档应该在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.
  }
}

所以,我和同事们聊了聊,其中一个反应是“你没有忠于演员模型。”

首先,我真的很感激那些长期使用演员的人的一些指导。所有的信息都变得笨拙了吗?你最终会把消息传递隐藏在接口后面吗?

我提议的演员仍然可以选择在他们之间发送消息,订阅事件流,所有你期望从Akka得到的东西。这个界面为你提供了一种经过时间考验的方式来了解你在说什么。在IDE中进行编码时,它会有所帮助,等等。为什么一个参与者的用户需要知道它是一个参与者(除非它也是一个参与者并且与之紧密结合)?

我得到的另一个反应是“看起来你想要一个打字机”。但是在阅读了关于打字机演员的文章后,我不相信。当然,TypeActor省去了我创建这些内部消息的麻烦。但是,至少从http://doc.akka.io/docs/akka/snapshot/scala/typed-actors.html的代码示例中,我得到的印象是,TypeActor只是作为您想要封装的代码块的代理,或者使线程安全,或者只是不直接从当前代码调用线程。您编写的代码只是实现和接口。您不会干扰参与者本身(代理)——例如,如果您希望您的实现执行周期性工作或订阅事件流,或者执行与接口无关的任何其他操作。

我也读过http://letitcrash.com/post/19074284309/when-to-use-typedactors没有发现这个例子更有启发性。我可能只是不是在摸索TypedActor(并不是说我已经真正了解了演员)。

提前谢谢你的帮助。

皮诺

共有3个答案

赏梓
2023-03-14

计算的Actor模型与面向对象编程有很大的相似之处。OO是关于控制权的间接转移。当您调用一个方法(发送消息)时,您将失去对消息的控制。当然,静态编程语言在所有类型检查方面都有所帮助,但除此之外,您不知道消息会发生什么。见鬼,也许方法永远不会返回,尽管返回类型明确表示它会(抛出异常,活锁,你说吧...)!同意,当你曾经Java甚至更好的Scala时,放弃静态输入很糟糕,但这并不是说你没有任何好处。动态类型为您提供了松散的耦合。例如,不需要创建额外的接口来为您的测试引入模拟演员;ActorRef是您唯一需要的应用编程接口。

别旻
2023-03-14

免责声明:我不是Akka/演员专家。我已经和演员和Akka一起工作了18个月了,我仍然在努力思考某些概念,尤其是在不使用Akka的时候。

对于您想知道Akka未来的返回类型的特殊和狭窄的情况,是的,您应该使用TypeActor。我有几次使用TypeActor,它们被用来为Actor系统之外的模块提供应用编程接口。也就是说,我在Akka之上构建了一个系统,它的大部分工作都是在Akka网络内完成的,但是在Akka网络之外有一两个模块需要访问Akka网络提供的功能。最值得注意的是Scalatra前端,它调用Akka网络,并在响应其客户端之前对Akka网络返回的值做了一些工作。然而,TypeActor实际上只是Akka网络的前端。我认为使用TypeActor作为外部(Akka网络外部)模块的API前端是另一个关注点的分离。

总的来说,我同意那些告诉你“你没有忠实于演员模型”的人试图强制其返回类型的视图。在最纯粹的形式中,也是我最成功的方式中,Actor模型是使用fire-and-forget语义实现的。这些消息不会变得笨拙,在许多情况下,它们帮助组织了我的代码并定义了工作边界。把它们放在自己的包里确实有帮助。

如果我实现您所描述的功能,它将看起来像以下内容:

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
    }
  }

}

记住我写的方式wouldYouDoMyHomework,它会在等待答案时阻塞。然而,有一些聪明的方法可以异步地做到这一点。有关更多信息,请参阅http://doc.akka.io/docs/akka/2.0.3/scala/futures.html。

此外,请记住,一旦您的消息进入Akka网络,您就可以完成所有很酷的缩放和远程处理工作,TypedActor API的用户永远不必知道。

在大型项目中,这样做确实增加了一些复杂性,但是如果您认为这是将向外部模块提供应用编程接口的责任分离开来,甚至可能将该责任转移到另一个包,那么管理起来非常容易。

好问题。我迫不及待地想从更有经验的Akka开发人员那里听到答案。

糜帅
2023-03-14

让我首先回应一点,我认为这是非常重要的。你说:

为什么一个参与者的用户需要知道它是一个参与者(除非它也是一个参与者并且与之紧密结合)?

actor是一种与传统OO截然不同的编程范式,主要区别在于一切都是异步的,因此永远不存在真正的“返回值”。这意味着隐藏自己是演员这一事实通常是个坏主意,因为例外情况请参阅我的TypedActors博客文章。actor最好的地方是,它们完全封装在Akka中,位于ActorRef后面,而不是面向对象语言的弱封装。为了最大限度地利用它,尽可能公开ActorRefs,这使客户端代码有机会以最合适的方式使用它们(根据上下文,可能使用tellask)。

编写参与者时,应该将有关该参与者的所有信息放在一个位置,包括接口契约描述。它可能看起来有点像这样:

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 {
  …
}

使用普通的非类型化演员可以让您充分利用演员模型的全部功能,包括动态更改行为,并且不会被诱惑将自己限制在“同步”调用的超时保护的笼子中(简而言之,在实现传统同步时,TypeActors最有用使用幕后演员的界面)。我同意IDE对消息类型的支持会很好,但这是一个工具问题(我一直在和ScalaIDE团队讨论添加一些魔法,但这必须等到它可以获得优先级)。在一个地方定义角色的所有属性是重要的部分。

 类似资料:
  • 对于简单的定制操作,我们或许可以通过使用layers.core.Lambda层来完成。但对于任何具有可训练权重的定制层,你应该自己来实现。 这里是一个Keras层应该具有的框架结构(1.1.3以后的版本,如果你的版本更旧请升级),要定制自己的层,你需要实现下面三个方法 build(input_shape):这是定义权重的方法,可训练的权应该在这里被加入列表`self.trainable_weigh

  • 对于简单的定制操作,我们或许可以通过使用layers.core.Lambda层来完成。但对于任何具有可训练权重的定制层,你应该自己来实现。 这里是一个Keras2的层应该具有的框架结构(如果你的版本更旧请升级),要定制自己的层,你需要实现下面三个方法 build(input_shape):这是定义权重的方法,可训练的权应该在这里被加入列表`self.trainable_weights中。其他的属性

  • 对于简单、无状态的自定义操作,你也许可以通过 layers.core.Lambda 层来实现。但是对于那些包含了可训练权重的自定义层,你应该自己实现这种层。 这是一个 Keras 2.0 中,Keras 层的骨架(如果你用的是旧的版本,请更新到新版)。你只需要实现三个方法即可: build(input_shape): 这是你定义权重的地方。这个方法必须设 self.built = True,可以通

  • 我有下面的类,我与之成为朋友,现在我正试图与cin成为朋友,但我得到了一个错误。。。谁能帮帮我,或者告诉我做错了什么? 错误: c: \mingw\bin/lib/gcc/mingw32/4.6.1/include/c/bits/stl\u-algo。h: 2215:4:错误:将“const RAngle”作为“int RAngle::operator”的“this”参数传递 等级

  • 问题内容: 我需要在cfml页面中使用自己的java类。 文档中的此项听起来不错,但没有说明我必须创建哪些文件。 我试图在网站根目录下创建一个页面。然后将+ 放在同一路径中。但这会导致错误“找不到类”! 你能帮我么? 问题答案: 同一路径中的TestClass.java + TestClass.class。 您不能仅将文件放置在任何地方。CF服务器启动时, 仅 检查类/ jar的特定位置。这些位置

  • 本文向大家介绍静态HTML元素直接用SAPUI5编写,包括了静态HTML元素直接用SAPUI5编写的使用技巧和注意事项,需要的朋友参考一下 SAPUI5允许开发人员在某些区域使用UI5控件来使用自己的静态HTML元素。它不是封闭的框架,允许您使用sap.ui.core.HTML控件在UI5渲染的区域内编写任意数量的纯HTML,因此可以轻松混合HTML和UI5控件。 但是,由UI5控件创建的HTML