UI在GCP Dataflow上具有python流传输管道,该管道从PubSub读取数千条消息,如下所示:
with beam.Pipeline(options=pipeline_options) as p:
lines = p | "read" >> ReadFromPubSub(topic=str(job_options.inputTopic))
lines = lines | "decode" >> beam.Map(decode_message)
lines = lines | "Parse" >> beam.Map(parse_json)
lines = lines | beam.WindowInto(beam.window.FixedWindows(1*60))
lines = lines | "Add device id key" >> beam.Map(lambda elem: (elem.get('id'), elem))
lines = lines | "Group by key" >> beam.GroupByKey()
lines = lines | "Abandon key" >> beam.Map(flatten)
lines | "WriteToAvro" >> beam.io.WriteToAvro(job_options.outputLocation, schema=schema, file_name_suffix='.avro', mime_type='application/x-avro')
管道运行得很好,除非它从不产生任何输出。有什么想法吗?
答案 0 :(得分:3)
您的代码似乎有一些问题。首先,关于null / None(您已修复)和ints / floats(在注释中注明)存在一些格式错误的数据。最后,WriteToAvro转换无法写入无界的PCollections。有一种解决方法,您可以定义一个新的sink并将其与WriteToFiles转换一起使用,该转换能够写入无界的PCollections。
请注意,在撰写本文时(2020-06-18),该方法不适用于Apache Beam Python SDK <= 2.23。这是因为Python选取器无法反序列化选取的Avro模式(请参见BEAM-6522)。在这种情况下,这迫使解决方案改为使用FastAvro。如果您手动升级莳萝,则可以使用Avro >> 0.3.1.1 和 Avro >> 1.9.0,但是请小心,因为目前尚未测试。
请注意,以下是解决方法:
from apache_beam.io.fileio import FileSink
from apache_beam.io.fileio import WriteToFiles
import fastavro
class AvroFileSink(FileSink):
def __init__(self, schema, codec='deflate'):
self._schema = schema
self._codec = codec
def open(self, fh):
# This is called on every new bundle.
self.writer = fastavro.write.Writer(fh, self._schema, self._codec)
def write(self, record):
# This is called on every element.
self.writer.write(record)
def flush(self):
self.writer.flush()
此新接收器的用法如下:
import apache_beam as beam
# Replace the following with your schema.
schema = fastavro.schema.parse_schema({
'name': 'row',
'namespace': 'test',
'type': 'record',
'fields': [
{'name': 'a', 'type': 'int'},
],
})
# Create the sink. This will be used by the WriteToFiles transform to write
# individual elements to the Avro file.
sink = AvroFileSink(schema=schema)
with beam.Pipeline(...) as p:
lines = p | beam.ReadFromPubSub(...)
lines = ...
# This is where your new sink gets used. The WriteToFiles transform takes
# the sink and uses it to write to a directory defined by the path
# argument.
lines | WriteToFiles(path=job_options.outputLocation, sink=sink)