一个Actor内部消息处理用Future还是piepTo(self)?
一个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 您好:
这个问题是这样的,无论您使用第一种还是第二种方式,在您完成当前的这个多级的异步动作之前,您的Actor都会收到其他的消息穿插进来。
那么久引发了下一个问题,我们是否能够容忍这样的行为。
如果我们不能够容忍,那么这个问题总一定需要做到都是:
- 异步非阻塞等待(使用
become) - 缓冲当前不感兴趣的消息
如果我们能够容忍,那么这又会出现逻辑割裂的问题,即一部分逻辑发生在Actor内部,一部分又不由您控制。
所以上面的这个问题,剩下的就是个人喜好的问题了。
PipeTo实际上的消耗非常小,但是这样的消耗又可以降低您在处理编码时的心智负担。
下面来说一个标准的工业级的做法:
首先使用类型系统对类型结果对象进行领域建模,成功的结果和失败的结果,然后在把Future转换为自己的领域对象,即在Future完成时,将结果发送给当前的Actor。
当然在这之间,您的Actor可能需要进行异步的非阻塞等待,并且缓冲所有当前不感兴趣的消息。
我个人的建议是,如果是非常核心的,稳固的,可控的逻辑,一定要通过领域建模和上面的这种模式来做到可控可测,而不能在技术上偷懒。当然在一些边边角角的逻辑上,还是可以是当地根据当前的上下文或者心情来进行取舍。
还有一种简单的处理方法是另起一个Actor去做Future chain里面应该做的事情。
@hepin1989 非常细致的回答,谢谢您的耐心。另外也建议把领域建模的相关主题加入到TheAkkaWay中。
@Kabie 实际上这个问题正是由在一个Actor中处理Future chain还稍嫌复杂才想到的
@Kabie 这个是很Actor!