如何设计具有扇出能力的反应流动作系统

时间:2016-07-18 14:34:20

标签: reactive-programming actor akka-stream akka-cluster backpressure

我正在尝试实现具有背压功能的基于actor的系统。作为要求,主进程接收JSON格式的流数据。但是,每个JSON事件都有几个字段,例如{ip:'123.43.12.1',country:'US',... etc}。 JSON的结构事先是已知的。

现在,我必须以某种方式将JSON结构展平为(key,value)对。例如,上面的数据可以展平为(ip,freq),(country,freq),其中freq是数据流中出现ip(例如'123.43.12.1')的时间。

一种非常自然的方式是将每个(键,值)对转发到相应的子/远程actor以进行进一步评估。例如,('123.43.12.1',1)被发送到IP-Actor; ('US',1)被发送到Country-Actor等等。

我想确保整个系统背压。在这种情况下事情更复杂,因为事件{ip:'123.43.12.1',country:'US'}仅在IP-Actor和Country-Actor都已完成处理扁平对('123.43)时被视为已处理。 12.1',1),('US',1)。每个actor可能具有不同的处理速度(例如,IP-Actor比Country-Actor快得多)。在这种情况下,我希望接收流的主进程将等待/阻塞,直到有需求信号(当两个actor都完成处理其邮箱中的现有数据时发生)。否则,某些演员可能在邮箱中充满了消息(Country-Actor - slow one)但是消息仍然存在,因为其他演员邮箱是空的(IP-Actor - 更快)。

有人可以建议反应流规范是否提供此类功能。如果没有,无论如何都要以最有效的方式实现功能。

感谢。

1 个答案:

答案 0 :(得分:0)

您描述的Actors之间的同步类型正是您希望在Actor模型中避免的。任何"等待/阻止"与反应式编程是对立的。我建议使用单个流Flow进行更新。

首先需要处理json数据:

import akka.stream.scaladsl._

//your original source of json strings
val jsonSrc : Source[String, NotUsed] = ???

case class JsonObject(ip : String, country : String)

//use your favorite json parser
def jsonParser(jsonStr : String) : JsonObject = ???

val parserFlow = Flow[String] map jsonParser

接下来定义计数器逻辑并使用{​​{1}}生成具有递增值的计数器:

Flow.scan

最后,将所有内容组合在一起:

type IPCounter = Map[String,Int]
val emptyIPCounter = Map.empty[String,Int] withDefaultValue 0

type CountryCounter = Map[String, Int]
val emptyCountryCounter = Map.empty[String,Int] withDefaultValue 0

type Counters = Tuple2[IPCounter, CountryCounter]
val emptyCounters = (emptyIPCounter, emptyCountryCounter)

def updateCounters(counters : Counters, jsonObj : JsonObject) : Counters = {
  (counters._1.updated(jsonObj.ip, counters._1(jsonObj.ip) + 1),
   counters._2.updated(jsonObj.country, counters._2(jsonObj.country) + 1))
}

val counterFlow = Flow[JsonObject].scan(emptyCounters)(updateCounters)

结果正是您所要求的:当所有计数器都已更新时,仅在计数器值上转发的背压流。