我正在尝试使用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”,并希望它将保证一次语义。
我想测试“负面”情况:
我实际上得到的是-所有消息都已传递(至少一次实现),但除此之外,还有很多重复项-每1M消息大约30-50K。
有什么解决方案可以实现一次准确投放?
答案 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,这样按键进行分组就不会做太多的额外工作。
让我知道您的想法以及是否可行。另外,如果您决定采用这种策略,请发表您的里程数:)。