【问题标题】:How to restrict actor messages to specific types?如何将参与者消息限制为特定类型?
【发布时间】:2011-07-29 17:02:18
【问题描述】:

Akka 中,除了使用使用 RPC 样式编程模型的“Typed Actor”API 之外,是否有其他方法可以将发送给 Actor 的消息限制为特定静态类型?

我可以在 Akka 中使用消息传递样式而不丢弃 Actor 边界的静态类型安全性吗?

例如,我想使用这样的代码:

sealed abstract class FooMessage
case object Foo extends FooMessage
case object Bar extends FooMessage

class FooActor extends Actor[FooMessage] {
  def receive = {
    case Foo => () // OK

    // Would raise a compiler error:
    // case s: String => error("Can't happen, String is not a subtype of FooMessage") 

  }
}

val fooActor = actorOf[FooActor]
fooActor ! Foo // OK

// Won't compile:
fooActor ! "Hello"

也许必须扩展一些基本特征或具有类似Either 的构造以允许系统级消息(Exit 等)。

【问题讨论】:

    标签: scala actor akka


    【解决方案1】:

    听起来 Akka 的 Akka's Typed Channel support 应该解决这个问题,但是(根据 the comment has already been removed from Akka in version 2.3)。

    在 Akka 2.2.3 的文档中,有一个很好的 "design background" 部分讨论了支持类型化消息发送和响应的困难。

    还有一个很好的 NEScala 演讲,由 Roland Kuhn,Akka 类型化通道:将类型计算实现为宏 ([YouTube] / [Slides]),讨论了类型化通道的实现。

    【讨论】:

    • 输入的频道已经是removed from Akka in version 2.3。 “类型化通道是我们决定删除的一项实验性功能:它的实现依赖于 Scala 的一项实验性功能,Java 和其他语言对此没有对应关系,而且它的使用不直观。”
    【解决方案2】:

    然后您必须将消息类型编码到 Actor ref 中,这将大大降低 ActorRegistry 之类的值。

    此外,使用强大的机制,如“成为”(这是 Actor 模型的基础),输入消息的价值较低。

    由于 Akka 在消息与当前行为不匹配时不会泄漏内存,因此将“错误”消息发送给“错误”actor 的风险不同。

    此外,Actor 本质上是动态的,所以如果你想让它们成为静态的,请使用 TypedActor(它不是 RPC,它就像 RPC 一样普通的 Actor,void 方法是 ! 调用,Future 返回类型是 !!! 和其他返回类型基于!!)

    通常的做法是在 Actor 的伴生对象中声明 Actor 可以接收哪些消息,这样就更容易知道它可以接收什么。

    这有帮助吗?

    【讨论】:

    • 感谢您的帮助。您(akka 团队)是否曾尝试向参与者的消息添加类型约束,或者这从未被认为是一个有用的想法?
    • 过去曾在列表中讨论过它,但我们总是在同一个地方结束,TypedActor 用于该用例,Actor 用于完全动态行为。如果您想获得更多控制权,您可以在 ActorRef 之上尝试自己的抽象。这有帮助吗?干杯,√
    【解决方案3】:

    实际上将 Actor 限制为只有单一类型作为输入并不是很有用。对我来说更有用的是以严格类型的方式列出可能的输入。

    有一种方法可以用于演员的严格类型输入 (SynapseGrid):

    case class Contact[T](...)
    case class Signal[T](contact:Contact[T], data:T)
    

    在您的情况下,界面由一个输入触点组成:

    val FooInput = contact[FooMessage]("FooInput")
    

    在 SynapseGrid 框架内,信号处理由 Builder 定义:

    class FooActorBuilder extends SystemBuilder {
      inputs(FooInput, OtherInput)
      FooInput.foreach(fooMessage => () //OK
      )
      OtherInput.foreach(...)
    }
    

    显然不能用不兼容的类型构造 Signal。因此我们有编译时检查。在 SynapseGrid 中有一个用于处理信号和联系人的 DSL。例如,从外部发送 Foo 或 Bar:

    val SomeOtherContact = contact[Boolean]("SomeOtherContact")
    SomeOtherContact.map(flag => if(flag) Foo else Bar) >> FooInput
    

    当然可以简单地发送消息:

    val inputMessage = Signal(FooInput, Foo)
    actor ! inputMessage
    

    【讨论】:

      【解决方案4】:

      在 Scala 标准库中有 an excuse 用于使基本演员无类型(这不适用于 Akka,因为我记得它不支持嵌套接收)。 Lift 反过来支持开箱即用的类型化actor。

      但是,使用通道,仍然可以使用 stdlib 创建强类型演员:

      object TypedActor {
      
        def apply[A](fun: PartialFunction[A, Any]): OutputChannel[A] = {
          val sink = new SyncVar[Channel[A]]
          actor {
            val in = new Channel[A](self)
            sink set in
            loop {
              in react { case any => reply(fun(any)) }
            }
          }
          sink.get
        }
      
      }
      
      sealed abstract class FooMessage
      case object Foo extends FooMessage
      case object Bar extends FooMessage
      
      object Test {
      
        val fooActor = TypedActor[FooMessage]{
          case Foo => println("OK")
        }
      
        fooActor ! Foo 
        fooActor ! "Hello!" // doesn't compile -> Type mismatch; found: String("Hello!"); required: FooMessage;
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-10
        • 1970-01-01
        • 2021-07-17
        • 1970-01-01
        • 1970-01-01
        • 2017-08-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多