创建用于并行处理集合元素的Akka流

时间:2016-10-18 20:27:59

标签: akka akka-stream

我正在尝试为包含并行处理流程的Akka流定义一个图形(我使用的是Akka.NET,但这不重要)。想象一下订单的数据源,每个订单都包含订单ID和产品列表(订单商品)。工作流程如下:

  1. 接收并订购
  2. 将订单广播到两个流程,流程A将处理订单项目,渠道B将处理订单ID(一些簿记工作)
  3. 流程A:将订单商品的集合拆分为单独的元素,每个元素都要单独处理
  4. 流程A:对于上一步中拆分产生的每个订单商品,请拨打一些外部服务,查询额外信息(价格,可用性等)。
  5. 流程B:为给定的订单ID做一些额外的簿记
  6. 合并流程A和B
  7. 从前一步骤发送到接收器合并数据,从而产生丰富的订单信息
  8. 步骤1(Source.From),2(广播),4-5(地图),6(合并),7(接收器)看起来没问题。但是如何在Akka或反应流术语中实现集合拆分?这不是广播或扁平化,需要将N个元素的集合分成N个独立的子流,这些子流稍后将合并回来。这是如何实现的?

1 个答案:

答案 0 :(得分:4)

我建议在一个流程中完成。我知道两个流程看起来更酷但是相信我在设计的简单性方面不值得(我试过)。你可以写这样的东西

import akka.stream.scaladsl.{Flow, Sink, Source, SubFlow}

import scala.collection.immutable
import scala.concurrent.Future

case class Item()

case class Order(items: List[Item])

val flow = Flow[Order]
  .mapAsync(4) { order =>
    Future {
      // Enrich your order here
      order
    }
  }
  .mapConcat { order =>
    order.items.map(order -> _)
  }
  .mapAsync(4) { case (order, item) =>
    Future {
      // Enrich your item here
      order -> item
    }
  }
  .groupBy(2, tuple => tuple._1)
  .fold[Map[Order, List[Item]]](immutable.Map.empty) { case (map, (order, item)) => map.updated(order, map.getOrElse(order, Nil) :+ item) }
  .mapConcat { _.map { case (order, newItems) => order.copy(items = newItems)} }

但即使这种做法也很糟糕。上面的代码或你的设计有很多东西可能出错。如果丰富其中一个订单项目失败,您会怎么做?如果订单对象的丰富失败怎么办?您的信息流会发生什么?

如果我是你,我有Flow[Order]并在mapAsync处理其子女,所以至少它保证我没有部分处理过的订单。