当使用Akka构建程序时,如何避免传递原始消息?

时间:2017-10-18 12:35:56

标签: erlang akka

我使用Akka构建我的运行时消息系统。但它常常遇到尴尬的情况:许多消息都与!一起发送,并且很难跟踪业务逻辑。 从Programming in Erlang一书中,不建议向其他人公开消息,而是使用函数封装actor消息并使用export([func1/1])或其他内容将其导出。

那么,是否可以在Akka中使用函数调用模式?
当使用Akka构建大型系统时,如何阅读包含这么多消息的代码?

2 个答案:

答案 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拨打?,或者等待实际值。