如何防止来自同一端口上另一个连接上的单个出端阻塞数据包的连接?

时间:2015-01-29 11:35:53

标签: noflo

我正在尝试从单个进程(P1)获取输出,并使用其他进程(P2和P3)对其执行并行任务。到目前为止这么简单。

为此,我将P2和P3连接到P1的单个输出端口。在我看来,这应该意味着P1通过其输出端口同时并发地同时从P2和P3中拾取数据包。

我发现P2和P3不是并行启动的,而是其中一个进程会等到对方完成处理(或者至少对我来说似乎这样)。

例如,这是一个简单的图表,它应该采用JSON输入,然后同时获取时间戳并解析JSON。解析JSON后会采用另一个时间戳,这用作计算JSON解析时间的基本方法。

enter image description here

请注意从ajax/Get输出端口开始的连接顺序(最后添加了时间戳连接)。

在这种情况下,时间戳的差异大约是5ms,这大致与JSON解析在非NoFlo环境中所花费的时间一致(由于某种原因,它实际上在NoFlo中稍长一些)。

现在使用相同的图表,但这次来自ajax/Get输出端口的连接顺序已更改(最后添加了解析连接):

enter image description here

这次时间戳之间的差异大约是40-50ms,这显然是一个巨大的差异,远远大于NoFlo之外的解析。

如果有人能够对以下内容有所了解,我真的很感激:

  • 为什么时间根据连接顺序而有所不同?
  • 如何确保来自ajax/Get的2个连接被触发并且并行运行(即,它们不会相互等待)?

如果有帮助,here's a JSON export of the graph from FlowHub

我还使用CLI整理了一个简单的图表,并设法更好地了解图表的流程,并且可能会对可能导致此问题的原因有所了解:

# This executes in the correct order, though likely by
# coincidence and not due to true parallelisation.
#
# Time1 is run and outputted before Time2.
#
Read(filesystem/ReadFile) OUT -> IN Time1(objects/GetCurrentTimestamp)
Read OUT -> IN Parse(strings/ParseJson)

# This executes the entire Parse path before going back to grab
# and output Time1.
#
# Time1 is run and outputted *after* Time2
# Read doesn't send a disconnect message to Parse until *after*
# Time 1 is outputted.
#
# Read doesn't send a disconnect message to Time1 until *after*
# the Parse path has finished disconnecting.
#
# Read(filesystem/ReadFile) OUT -> IN Parse(strings/ParseJson)
# Read OUT -> IN Time1(objects/GetCurrentTimestamp)

Time1 OUT -> IN Display1(core/Output)

Parse OUT -> IN Time2(objects/GetCurrentTimestamp)
Time2 OUT -> IN Display2(core/Output)

'sample.geojson' -> IN Read

ReadTime1之前定义的ReadParse连接的情况下运行时,网络是有序的,但我注意到Read等到其他一切都完成后才开始断开连接(是吗?):

DATA -> ENCODING Read() CONN
DATA -> ENCODING Read() DATA
DATA -> ENCODING Read() DISC
DATA -> IN Read() CONN
DATA -> IN Read() DATA
DATA -> IN Read() DISC
Read() OUT -> IN Time1() CONN
Read() OUT -> IN Time1() < sample.geojson
Read() OUT -> IN Parse() CONN
Read() OUT -> IN Parse() < sample.geojson
Parse() OUT -> IN Time2() CONN
Parse() OUT -> IN Time2() < sample.geojson
Read() OUT -> IN Time1() DATA
Time1() OUT -> IN Display1() CONN
Time1() OUT -> IN Display1() DATA
1422549101639
Read() OUT -> IN Parse() DATA
Parse() OUT -> IN Time2() DATA
Time2() OUT -> IN Display2() CONN
Time2() OUT -> IN Display2() DATA
1422549101647
Read() OUT -> IN Time1() > sample.geojson
Read() OUT -> IN Parse() > sample.geojson
Parse() OUT -> IN Time2() > sample.geojson
Read() OUT -> IN Time1() DISC
Time1() OUT -> IN Display1() DISC
Read() OUT -> IN Parse() DISC
Parse() OUT -> IN Time2() DISC
Time2() OUT -> IN Display2() DISC

如果我切换订单以便首先定义ReadParse连接,那么一切都会出错,Time1甚至不会从Read发送数据包直到整个Parse路径已完成(因此Time1实际上是 Time2之后的

DATA -> ENCODING Read() CONN
DATA -> ENCODING Read() DATA
DATA -> ENCODING Read() DISC
DATA -> IN Read() CONN
DATA -> IN Read() DATA
DATA -> IN Read() DISC
Read() OUT -> IN Parse() CONN
Read() OUT -> IN Parse() < sample.geojson
Parse() OUT -> IN Time2() CONN
Parse() OUT -> IN Time2() < sample.geojson
Read() OUT -> IN Time1() CONN
Read() OUT -> IN Time1() < sample.geojson
Read() OUT -> IN Parse() DATA
Parse() OUT -> IN Time2() DATA
Time2() OUT -> IN Display2() CONN
Time2() OUT -> IN Display2() DATA
1422549406952
Read() OUT -> IN Time1() DATA
Time1() OUT -> IN Display1() CONN
Time1() OUT -> IN Display1() DATA
1422549406954
Read() OUT -> IN Parse() > sample.geojson
Parse() OUT -> IN Time2() > sample.geojson
Read() OUT -> IN Time1() > sample.geojson
Read() OUT -> IN Parse() DISC
Parse() OUT -> IN Time2() DISC
Time2() OUT -> IN Display2() DISC
Read() OUT -> IN Time1() DISC
Time1() OUT -> IN Display1() DISC

如果这是正确的行为,那么如何并行运行2个分支而不阻止另一个?

我已经尝试过使每个组件都异步,我已经尝试了这两个组件并使用了WirePattern,我尝试创建多个输出端口并立即通过所有端口发送数据。没有快乐 - 它总是归结为第一条边连接的顺序。我正在把头发拉出来,因为它完全阻止了我对ViziCities使用NoFlo :(

2 个答案:

答案 0 :(得分:0)

由于JavaScript引擎的单线程特性,NoFlo无法并行执行多项操作。 I / O调用在他们自己的线程中运行,但是他们的回调总是将我们返回到NoFlo运行的主线程。

在NoFlo中,只要我们处理同步组件(就像示例图中的ajax/Get之外的其他内容一样),我们就会执行深度优先以确保快速吞吐量。

这意味着从ajax/Get的第一个出站连接的子流首先运行到完成,然后是第二个。

你想要在这里发生的是广度优先执行而不是深度优先。已经有一些关于通过边缘元数据启用它的讨论,但在此之前,一种方法是在感兴趣的连接之间添加core/RepeatAsync个节点。

从长远来看,另一个有趣的方法是通过RemoteSubgraph和Web Workers在自己的线程中运行部分流程。从理论上讲,我们甚至可以在自己的线程中运行每个进程,完全兼容传统的FBP。但这会带来巨大的启动成本。

答案 1 :(得分:0)

我不会考虑浏览器示​​例,它确实是深度优先的,因为浏览器端JavaScript的工作方式正如bergie所解释的那样。

CLI示例更有趣,因为noflo-nodejs广泛使用EventEmitters。它仍然不是真正的并行,但它更加并行。

我们在此处看到的是以下方面的副作用:

  1. 事件按发生顺序处理。
  2. 图表中定义分支的顺序会影响事件发生的顺序。
  3. 大多数组件都是由data事件而不是disconnect触发的。他们不等待disconnect处理data并发送结果。
  4. 在汇总中,它解释了为什么第一个分支在第二个分支之前执行,以及为什么在已经处理完所有数据之后断开连接。

    这可能会给你一个纯粹同步的印象,但尽管上面列出的事实,系统仍然是并发的。如果Read以合适的速度发送多个数据包,您会看到分支1和分支2的事件混合在一起。

    更新

    这是将同步组件转换为异步的常用技巧:

    setTimeout(function() {
      doTheSyncJob(); // actual code here
      callback();
    }, 0);