Apache spark apachebeam中的嵌套管道

Apache spark apachebeam中的嵌套管道,apache-spark,stream,google-cloud-platform,google-cloud-dataflow,apache-beam,Apache Spark,Stream,Google Cloud Platform,Google Cloud Dataflow,Apache Beam,我的Apache Beam管道接收无限的消息流。每条消息扇出N个元素(N约为1000,每个输入不同)。然后,对于前一阶段生成的每个元素,都有一个map操作,该操作生成新的N个元素,应该使用top 1操作来减少这些元素(元素按照从队列读取的原始消息进行分组)。top 1的结果保存到外部存储器中。在Spark中,我可以通过从流中读取消息并为每一条map+reduce消息创建RDD来轻松做到这一点。因为ApacheBeam没有嵌套的管道,所以我看不到在带有无限流输入的Beam中实现它的方法。例如: I

我的Apache Beam管道接收无限的消息流。每条消息扇出N个元素(N约为1000,每个输入不同)。然后,对于前一阶段生成的每个元素,都有一个map操作,该操作生成新的N个元素,应该使用top 1操作来减少这些元素(元素按照从队列读取的原始消息进行分组)。top 1的结果保存到外部存储器中。在Spark中,我可以通过从流中读取消息并为每一条map+reduce消息创建RDD来轻松做到这一点。因为ApacheBeam没有嵌套的管道,所以我看不到在带有无限流输入的Beam中实现它的方法。例如:

Infinite stream elements: A, B

Step 1 (fan out, N = 3): A -> A1, A2, A3
                (N = 2): B -> B1, B2

Step 2 (map): A1, A2, A3 -> A1', A2', A3'
              B1, B2, B3 -> B1', B2'

Step 3 (top1): A1', A2', A3' -> A2'
               B1', B2' -> B3'

Output: A2', B2'

A和B元素之间没有依赖关系。A2'和B2'是组中的顶级元素。这条小溪是无限的。映射操作可能需要几秒钟到几分钟。为执行映射操作所需的最长时间创建窗口水印将使快速映射操作的总体管道时间慢得多。嵌套管道会有帮助,因为这样我可以为每条消息创建一个管道。

看起来您不需要“嵌套管道”来实现这一点。让我向您展示Beam Python SDK中的情况(类似于Java):

例如,尝试在字符串中添加数字和撇号的伪操作(例如,
“a”
=>
“A1'
),您可以执行以下操作:

def my_fn(value):
  def _inner(elm):
    return (elm, elm + str(value) + "'")  # A KV-pair
  return _inner

# my_stream has [A, B]
pcoll_1 = (my_stream
           | beam.Map(my_fn(1)))
pcoll_2 = (my_stream
           | beam.Map(my_fn(2)))
pcoll_3 = (my_stream
           | beam.Map(my_fn(3)))

def top_1(elms):
  ... # Some operation

result = ((pcoll_1, pcoll_2, pcoll_3)
          | beam.CoGroupByKey()
          | beam.Map(top_1))

这是一种有效的解决方案。我很可能会编辑它,因为我在理解这个问题时可能会犯任何错误。(另外,模板代码是用java编写的)。假设
input
是您的流源

PCollection<Messages> msgs = input.apply(Window.<Messages>into(        
                                    FixedWindows.of(Duration.standardSeconds(1)) 
                                                .triggering(AfterWatermark.pastEndOfWindow()
                                         // fire the moment you see an element 
                                                   .withEarlyFirings(AfterPane.elementCountAtLeast(1))
                                         //optional since you have small window 
                                                   .withLateFirings(AfterProcessingTime.pastFirstElementInPane()))
                                                .withAllowedLateness(Duration.standardMinutes(60))
                                                .discardingFiredPanes()); 
其中
元素
可以是字符串、int、double等

最后,您可以将每个
元素
右转到存储区,其中包括:

top.apply(ParDo.of(new ParsetoString()))
   .apply(TextIO.write().withWindowedWrites()
                        .withNumShards(1)
                        .to(filename));
因此,每个消息大约有一个文件,这可能非常多。但遗憾的是,您不能附加到文件。除非你做了一个窗口,把所有的元素组合到一个列表中并写入其中

当然,有一种不使用窗口的黑客方法,如果这个用例似乎不适合您(或者如果您好奇的话),我将解释这一点


如果我遗漏了什么,请告诉我!:)

在我的例子中,N可以是1000或更多,这取决于输入消息。我编辑了答案以反映这一点。另外,在这种情况下,分组将如何工作?这是一个无限流,映射操作可能需要几分钟。现在,所有属于原始消息的映射操作都已完成,现在是时候进行缩减并将结果保存到外部存储器了吗?您可以在beam中完成此操作,但是,您需要使用窗口和触发器。也就是说,在无限流中,您希望跟踪流或特定窗口中看到的所有数据的顶部吗(在您的情况下,当N=3,稍后当N=6)?如果您能对所需的分组/映射进行更多解释,我可以写一个最小的示例operations@HarisNadeem澄清了这样一个问题,即top应按组计算-A为一个元素,B为一个元素。top结果应在可用时立即发出。从A到A2'的总时间可以是30秒到10分钟。我明白了,您的用例现在更有意义了,因为我对消息的定义感到困惑。这在apachebeam中仍然是可行的,但是有两种方法可以做到。由于每条消息彼此独立,因此只需对消息执行mapreduce。但是,由于要写入存储,因此需要打开窗口,否则将永远无法关闭文件描述符。因此,问题是如何以最少的计算时间和最少的资源浪费来实现它。对的如果是这样,我可以为您扩展一个解决方案。另一种方法(更像是一种黑客行为)是使用存储api并手动写入DoFn中的文件,然后您需要手动关闭DoFn函数末尾的文件描述符。这样,您就不需要窗口,只需要运行一个无限的流。@Harisna认为是的,每个消息上都有map reduce,这使它不是典型的数据流。与窗口相关的开销应尽可能接近于零,这意味着每分钟从窗口刷新一次结果是不可接受的。不过,我对窗口和“黑客”解决方案都很好奇。因此,WithEarlyFireings(AfterPane.ElementCountAtlast(1))为窗口中的每个元素创建一个触发器?@Nutel the
。WithEarlyFireings(AfterPane.ElementCountAtlast(1))
创建一个触发器,只要有一个元素,该触发器就会强制窗口关闭。因此,您有一个包含一个元素的窗口。但是,如果需要,可以选择每隔10个元素激发一次(您可以在实例有大量可用内存和CPU可利用的情况下执行此操作。例如,单个元素触发仅使用30%的CPU或内存,也许您可以将元素增加到10个,然后启动窗口,这样在该框架内,您的使用率为80%,并且您最大化了硬件)@Nutel我认为你不需要黑客解决方案?希望它能帮助你!如果你还有其他问题,请告诉我:)如果我在10个元素后开火,将不会创建一个包含10个元素的窗口,所有后续操作都将在这10个元素上执行,也就是说,TopElement将从ExtractElements所有结果的串联中选择一个top元素,因此将有一个输出,而不是10?出于好奇,黑客解决方案背后的想法是什么?@Nutel这是个好问题。但是这样的事件不会发生在您的用例中,因为您将编写自己的
DoFn
函数(在上面的示例中),该函数将为每个消息运行top的启发式。所以给你一个想法
摘录
top.apply(ParDo.of(new ParsetoString()))
   .apply(TextIO.write().withWindowedWrites()
                        .withNumShards(1)
                        .to(filename));