Google Dataflow和Pubsub-无法实现一次准确的投放

时间:2018-09-20 10:42:34

标签: google-cloud-platform google-cloud-dataflow apache-beam google-cloud-pubsub

我正在尝试使用Apache Beam SDK 2.6.0使用Google Dataflow和PubSub实现一次准确的投放。

用例非常简单:

“生成器”数据流作业将100万条消息发送到PubSub主题。

GenerateSequence
          .from(0)
          .to(1000000)
          .withRate(100000, Duration.standardSeconds(1L));

“存档”数据流作业从PubSub订阅中读取消息,然后保存到Google Cloud Storage。

pipeline
        .apply("Read events",
            PubsubIO.readMessagesWithAttributes()
                // this is to achieve exactly-once delivery
                .withIdAttribute(ATTRIBUTE_ID)
                .fromSubscription('subscription')
                .withTimestampAttribute(TIMESTAMP_ATTRIBUTE))
        .apply("Window events",
            Window.<Dto>into(FixedWindows.of(Duration.millis(options.getWindowDuration())))
                .triggering(Repeatedly.forever(AfterWatermark.pastEndOfWindow()))
                .withAllowedLateness(Duration.standardMinutes(15))
                .discardingFiredPanes())
        .apply("Events count metric", ParDo.of(new CountMessagesMetric()))
        .apply("Write files to archive",
            FileIO.<String, Dto>writeDynamic()
                .by(Dto::getDataSource).withDestinationCoder(StringUtf8Coder.of())
                .via(Contextful.of((msg, ctx) -> msg.getData(), Requirements.empty()), TextIO.sink())
                .to(archiveDir)
                .withTempDirectory(archiveDir)
                .withNumShards(options.getNumShards())
                .withNaming(dataSource ->
                    new SyslogWindowedDataSourceFilenaming(dataSource, archiveDir, filenamePrefix, filenameSuffix)
                ));

我在Pubsub.IO.Write(“生成器”作业)和PubsubIO.Read(“归档”作业)中都添加了“ withIdAttribute”,并希望它将保证一次语义。

我想测试“负面”情况:

  1. “生成器”数据流作业将100万条消息发送到PubSub主题。
  2. “存档”数据流作业开始工作,但在单击“停止作业”->“排空”的处理过程中我将其停止。某些消息已被处理并保存到Cloud Storage,例如40万条消息。
  3. 我再次开始执行“存档”工作,并希望它将接收未处理的消息(600K),最终我将看到100万条消息保存到存储中。

我实际上得到的是-所有消息都已传递(至少一次实现),但除此之外,还有很多重复项-每1M消息大约30-50K。

有什么解决方案可以实现一次准确投放?

2 个答案:

答案 0 :(得分:1)

数据流使您无法在运行之间保持状态。如果您使用Java,则可以update a running pipeline以不会使其失去现有状态的方式进行操作,从而可以跨管道发行版进行重复数据删除。

如果这对您不起作用,则可能需要使用ATTRIBUTE_ID键入消息的方式来归档消息,例如。 Spanner或GCS将此作为文件名。

答案 1 :(得分:0)

所以,我从来没有自己做过,但是在推理出您的问题时,这就是我的解决方法...

我的解决方案有些复杂,但是我没有找到其他方法来实现此目标而不涉及其他外部服务。所以,这里什么都没有。

您可以让管道从pubsub和GCS中读取数据,然后将它们组合以对数据进行重复数据删除。这里最棘手的部分是,一个将是有界pCollection(GCS),而另一个将是无界pCollection(pubsub)。您可以add timestamps到有界集合,然后对数据进行窗口处理。在此阶段,您可能会删除大约15分钟(您的先例实现中的窗口持续时间)之前的GCS数据。到目前为止,这两个步骤(即正确添加时间戳并删除可能足够旧而不会创建重复项的数据)是最棘手的部分。

此问题解决后,请追加两个pCollection,然后在两个数据集都通用的ID上使用GroupByKey。这将产生一个PCollection<KV<Long, Iterable<YOUR_DATUM_TYPE>>。然后,您可以使用附加的DoFn,该DoFn除去结果Iterable中除第一个元素以外的所有元素,并删除KV <>框。从那里开始,您可以像往常一样继续继续处理数据。

最后,重新启动管道时,仅对于第一个pubsub窗口才需要进行此附加工作。之后,您应该将GCS pCollection重新分配给一个空的pCollection,这样按键进行分组就不会做太多的额外工作。

让我知道您的想法以及是否可行。另外,如果您决定采用这种策略,请发表您的里程数:)。