我使用Akka构建我的运行时消息系统。但它常常遇到尴尬的情况:许多消息都与!
一起发送,并且很难跟踪业务逻辑。
从Programming in Erlang
一书中,不建议向其他人公开消息,而是使用函数封装actor消息并使用export([func1/1])
或其他内容将其导出。
那么,是否可以在Akka中使用函数调用模式?
当使用Akka构建大型系统时,如何阅读包含这么多消息的代码?
答案 0 :(得分:1)
新的实验性Akka类型支持在帮助理解和跟踪业务逻辑方面有很长的路要走:
https://doc.akka.io/docs/akka/2.5/scala/typed.html
这样做可以让您对协议进行编码,以便与类型系统中的演员交谈,这样可以更容易理解您在阅读代码时系统的工作方式。
另一种选择是使用Cinnamon(Lightbend商业库)来跟踪消息,您可以在运行时了解控制流。这是关于它的博客文章:
https://developer.lightbend.com/blog/2017-05-08-cinnamon-2-4-with-opentracing-integration/
如果你对演员提出了很多要求,一个常见的模式就是把它放在返回未来的方法之后。例如:
class MyService(actor: ActorRef) {
def doSomething(msg: Something): Future[Result] = {
(actor ? msg).mapTo[Result]
}
}
当然,除了你的返回类型是Unit
之外,你可以将相同的东西应用于常规告诉。
最后,Akka中的一个常见模式是将所有消息类放在actor的伴随对象中:
object MyActor {
/** This message does something */
case class MessageOne(foo: String)
/** This message does something else */
case class MessageTwo(bar: String)
}
class MyActor extends Actor {
import MyActor._
override def receive = {
case MessageOne(foo) =>
...
case MessageTwo(bar) =>
...
}
}
虽然这可能对后续的业务逻辑没有帮助,但它确实让系统新手更容易通过将所有消息(带文档)放在一个地方来了解如何与演员交谈。
关于构造代码的一个词 - 演员应该对定义良好的协议负有有限的责任。一个精心设计的基于Akka的系统不需要你一次到处理解整个消息流,它应该直接推理每个演员单独。仔细使用actor层次结构来提取子进程作为子进程可以在这里提供帮助。换句话说,如果为了理解你的系统的行为方式,你需要了解数百个演员之间数以千计的消息是如何流动的,那么你肯定有设计问题,需要重新思考如何打破你的演员进入孤立的单位,可以像积木一样容易地组合在一起。
答案 1 :(得分:0)
你可以做同样的事情,Erlang会隐藏你在函数后面传递的所有信息。或者换句话说,不应该处理actor,而应该处理一般(域抽象)类,并仅将actor用作底层实现。
case class Cat(name: String)
trait Shelter {
def leave(cat: Cat): Unit
}
class ActorShelter extends Shelter with Actor {
var cats = List[Cat]()
def leave(cat: Cat) =
this.self ! cat
def receive = {
case cat: Cat =>
cats = cat :: cats
}
}
这样您就可以在代码中使用Shelter
而无需知道它是演员。
当然,您仍然需要决定是否会使用Unit
对所有演员表返回!
,或者只是引用自己。如果您使用Future
拨打?
,或者等待实际值。