TheAkkaWay icon indicating copy to clipboard operation
TheAkkaWay copied to clipboard

一个Actor内部消息处理用Future还是piepTo(self)?

Open PipsqueakH opened this issue 8 years ago • 5 comments

一个Actor对一个消息进行一连串的处理,中间都是Future,是一个不断的对这些Future进行map操作还是把这些Future pipeTo(self)? 例如可以这样 1:

def f(a: A): Future[B]
def f(b: B): Future[C]

def receive: Receive {
    case object => case a: Future[A] => a map f map g onComplete println
}

也可以这样2:

def f(a: A): Future[B]
def f(b: B): Future[C]

def procf: Receive {
    case object => case a: A =>  f a pipeTo self
    case _ => error
}

def procg: Receive {
    case object => case b: B =>  g b onComplete println
    case _ => error

像1那样会形成复杂(实际情况可能会员远比示例复杂的多)的链式调用,其中错误处理会十分复杂(尤其是涉及到错误日志打印),像2那样又会多一次消息传递,该如何取舍?

由此引出的另一个问题是,一个消息pipeTo的开销有多少,如果是piepTo self区别大么?

PipsqueakH avatar Apr 22 '17 09:04 PipsqueakH

@PipsqueakH 您好:

这个问题是这样的,无论您使用第一种还是第二种方式,在您完成当前的这个多级的异步动作之前,您的Actor都会收到其他的消息穿插进来。

那么久引发了下一个问题,我们是否能够容忍这样的行为。

如果我们不能够容忍,那么这个问题总一定需要做到都是:

  1. 异步非阻塞等待(使用become
  2. 缓冲当前不感兴趣的消息

如果我们能够容忍,那么这又会出现逻辑割裂的问题,即一部分逻辑发生在Actor内部,一部分又不由您控制。

所以上面的这个问题,剩下的就是个人喜好的问题了。

PipeTo实际上的消耗非常小,但是这样的消耗又可以降低您在处理编码时的心智负担。

下面来说一个标准的工业级的做法:

首先使用类型系统对类型结果对象进行领域建模,成功的结果和失败的结果,然后在把Future转换为自己的领域对象,即在Future完成时,将结果发送给当前的Actor。

当然在这之间,您的Actor可能需要进行异步的非阻塞等待,并且缓冲所有当前不感兴趣的消息。

He-Pin avatar Apr 22 '17 09:04 He-Pin

我个人的建议是,如果是非常核心的,稳固的,可控的逻辑,一定要通过领域建模和上面的这种模式来做到可控可测,而不能在技术上偷懒。当然在一些边边角角的逻辑上,还是可以是当地根据当前的上下文或者心情来进行取舍。

He-Pin avatar Apr 22 '17 09:04 He-Pin

还有一种简单的处理方法是另起一个Actor去做Future chain里面应该做的事情。

Kabie avatar Apr 22 '17 09:04 Kabie

@hepin1989 非常细致的回答,谢谢您的耐心。另外也建议把领域建模的相关主题加入到TheAkkaWay中。

@Kabie 实际上这个问题正是由在一个Actor中处理Future chain还稍嫌复杂才想到的

PipsqueakH avatar Apr 22 '17 10:04 PipsqueakH

@Kabie 这个是很Actor!

He-Pin avatar Apr 22 '17 10:04 He-Pin