我正在尝试从PubSub订阅中读取消息,进行处理(通过使JSON扁平化以撤消某些数组),然后将它们插入具有PubsubIO和JdbcIO的Postgres和BigQuery数据库中。
我在两个数据库中都看到很多重复项,我的意思是:在一天中,单个设备(我们正在从IoT设备接收数据)的操作中,有200万行中的重复项约为140K。
PubsubIO收到消息后,立即执行确认。或者至少在下面的接收器中,它现在是一个简单的ParDo,可以打印消息(实际上只是消息的内存ID)
通过整天检查NodeJs App Engine ingestor 中的请求正文,我们验证了设备没有发送重复的消息。 因此,我们知道问题在于,转换器没有及时确认PubSub的消息(即使最后期限在300秒内确定),因此PubSub重新插入了消息,我们再次对其进行处理。
我们的第一个流程非常简单:PubsubIO可以读取-> PTransform函数,将消息分为两个不同的PCollectionTuple.TuppleTags中的两个对象(用于Postgress和BigQuery)-> 2个并行转换器(BigQueryIO和JdbcIO)插入数据库
在第二个消息中,我们向消息添加了两个属性:时间戳记和唯一ID,以遵循我们在网上找到的许多建议以避免重复。然后我们的PubsubIO使用了 withTimestampAttribute 和 withIdAttribute 函数。没有成功,我们添加了一个简单的ParDo来像接收器一样工作,从而强制执行了确认,但是没有成功。
val transform =
pipeline
.apply("ReadPubSubMessages",
PubsubIO.readMessagesWithAttributes()
.withIdAttribute("uniqueID")
.withTimestampAttribute("timestamp")
.fromSubscription(options.pubSubInput)
)
.apply("LogPubSubMessages", ParDo.of(object: DoFn<PubsubMessage, PubsubMessage>() {
@ProcessElement
fun processElement(context: ProcessContext) {
LOG.info(context.element().toString())
context.output(context.element())
}
}))
.apply("ConvertMessageToTableRow&Postgres",
DisarmPubsubMessage(tableRowMapper, postgresMapper))
...
const customAttributes: Publisher.Attributes = {
device_id: validationResult.value.device_id.toString(),
timestamp: Date.now().toString(),
uniqueID: validationResult.value.device_id.toString() + "-" + uuidv1()
}
...
const publishReadings = async (readingRequest: ReadingRequest, customAttributes: Publisher.Attributes): Promise<any> => {
const pubsub = PubSub()
const payload = JSON.stringify(readingRequest)
const dataBuffer = Buffer.from(payload)
return pubsub
.topic(process.env.NEW_READINGS_TOPIC as string)
.publisher()
.publish(dataBuffer, customAttributes)
}
...
我知道由于其他原因可能会有重复,并且拥有清除守护程序将是一个合适的解决方案,但是这里的问题是ack花费的时间太长。我看到了一些有关使用Windowing和Reshuffle的帖子,但是我不知道如何为我的案例实现它们。这些是一些帖子:
-> Google Dataflow and Pubsub - can not achieve exactly-once delivery
-> How to deduplicate messages from GCP PubSub in DataFlow using Apache Beam's PubSubIO withIdAttribute
-> Why is GroupByKey in beam pipeline duplicating elements (when run on Google Dataflow)?
谢谢!