我正在尝试使用actor实现消息处理管道。管道的步骤包括诸如读取,过滤,扩充以及最后存储到数据库中的功能。 类似于此的内容:http://sujitpal.blogspot.nl/2013/12/akka-content-ingestion-pipeline-part-i.html
问题在于,阅读,过滤和增强步骤比存储步骤快得多,这导致拥挤的商店演员和不可靠的系统。
我正在考虑以下选项:让商店演员拉出处理过的并准备好存储消息。这是一个不错的选择吗?更好的建议?
谢谢
答案 0 :(得分:3)
您可以考虑以下几个选项:
如果消息顺序无关紧要 - 只需执行单独的actor(或将来)内的每个存储操作。它将导致所有数据存储并行执行 - 我建议使用单独的线程池。如果某些消息是对其他消息的修改或参与同一事务 - 您可以仅为每个messageId / transactionId创建单独的actor,以避免悲观/乐观的锁定问题(不要忘记在事务端或超时时杀死此类actor)。
使用有界邮箱(背压) - 如果旧的仍未处理,您将阻止来自输入的新消息(例如,您可能阻止接收线程直到消息将被链中的最后一个演员确认)。它将责任转移到源系统。它在JMS耐用品方面做得非常好 - 消息以可靠的方式存储在JMS代理端,直到您的系统最终处理完毕。
结合前两个
答案 1 :(得分:3)
我使用的方法与此类似:Akka Work Pulling Pattern(源代码:WorkPullingPattern.scala)。它的优势在于可以在本地和与Akka Cluster 。此外,整个方法完全异步,完全没有阻塞。
如果您处理了#34;对象"不是所有人都适合记忆,或者其中一个步骤很慢,这是一个很棒的解决方案。如果你产生N个工人,那么N"任务"将一次处理。放置"步骤"可能是一个好主意。进入BalancingPools也是并行N(或更少)。
我不知道你的处理和#34;管道"是顺序还是不顺序,但如果是,就在几个小时前,我开发了一个基于上述+ Shapeless库的类型安全抽象。在与WorkPullingPattern合并之前,代码的一瞥是:Pipeline。
它需要任何功能管道(正确匹配签名),在BalancingPools中生成它们,创建Workers并将它们链接到主actor,可用于调度任务。
答案 2 :(得分:0)
新的AKKA流(仍处于测试阶段)有背压。它旨在解决这个问题。
答案 3 :(得分:0)
您还可以在演员身上使用接收管道:
class PipelinedActor extends Actor with ReceivePipeline {
// Increment
pipelineInner { case i: Int ⇒ Inner(i + 1) }
// Double
pipelineInner { case i: Int ⇒ Inner(i * 2) }
def receive: Receive = { case any ⇒ println(any) }
}
actor ! 5 // prints 12 = (5 + 1) * 2
http://doc.akka.io/docs/akka/2.4/contrib/receive-pipeline.html
它最符合您的需求,因为您在演员处理消息之前/之后有小的流水线操作。它也是阻止代码,但这对你的情况很好,我相信