Akka.NET 提供了一个 F# API,它使得将 Akka actor 定义为 Akka 邮箱上的 F# 函数变得微不足道。只要参与者处理的所有消息都可以使用单个可区分联合来描述,参与者邮箱就是强类型的。问题是将所有消息定义放在单一类型(有区别的联合)中通常会使这种类型变得混乱:参与者经常响应不同类别的消息,例如远程客户端发送的消息和内部通信中使用的消息。例如,actor 可以产生内部作业并得到内部组件的通知。使用不同的(内部)类型定义这些内部消息是有道理的,但是actor的邮箱chages不能再是强类型,actor的函数如下所示:
let rec loop () =
actor {
let! message = mailbox.Receive ()
match box message with
| :? PublicMessage as msg -> handlePublicMessage msg
| :? PrivateMessage as msg -> handlePrivateMessage msg
| _ -> raise (InvalidOperationException(sprintf "Invalid message type %A" message))
return! loop ()
}
loop ()
Run Code Online (Sandbox Code Playgroud)
我在这里不喜欢的是,这种方法剥夺了 F# 的核心优势之一:类型推断。相反,我们必须将消息装箱以将它们转换为 Object 类型,然后将它们转换为我们期望的类型。
这种方法有两种替代方法:
我检查了 Akka.NET 训练营的代码,他们使用的是第一种方法 - 使用消息装箱和投射。这是可以做的最好的事情吗?
好吧,在我看来有两个问题 - 内部消息和装箱/类型安全。
据我了解演员模型,没有私人消息。演员要么可以回复消息,要么不能。如果您确实想要拥有异步私有逻辑,我建议在处理公共消息期间使用异步函数,而不是使用不同的参与者消息,因为它必须是公共的。
为了类型安全,虽然我还没有尝试过,但似乎还有另一种方法可以避免计算表达式生成参与者并使用提供的泛型actorOf函数,如下所示:
let handleMessage message =
match message with
| PublicMessage as msg -> handlePublicMessage msg
| PrivateMessage as msg -> handlePrivateMessage msg
| _ -> raise (InvalidOperationException(sprintf "Invalid message type %A" message))
let actorRef = spawn system "my-actor" <| actorOf handleMessage
Run Code Online (Sandbox Code Playgroud)
它基本上是选项 1 的类型安全替代方案。我个人认为在一个联合类型中列出所有支持的消息类型没有问题。要么参与者知道它的消息(最好有默认的后备),要么你想要完全面向对象并且没有类型,只有盒装动态消息。
更多信息例如文档中的:http://getakka.net/docs/FSharp%20API#creating-actors-with-actor-computation-expression