【问题标题】:Read and Write serialized protobuf in Beam在 Beam 中读取和写入序列化的 protobuf
【发布时间】:2018-06-30 14:02:56
【问题描述】:

我想将序列化的 protobuf 消息的 PCollection 写入文本文件并将它们读回应该很容易。但经过几次尝试,我没有这样做。如果有人有任何意见,将不胜感激。

// definition of proto.

syntax = "proto3";
package test;
message PhoneNumber {
  string number = 1;
  string country = 2;
}

我有下面的 python 代码,它实现了一个简单的 Beam 管道,将文本写入序列化的 protobufs。

# Test python code
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions
import phone_pb2

class ToProtoFn(beam.DoFn):
  def process(self, element):
    phone = phone_pb2.PhoneNumber()
    phone.number, phone.country = element.strip().split(',')
    yield phone.SerializeToString()

with beam.Pipeline(options=PipelineOptions()) as p:
  lines = (p 
      | beam.Create(["123-456-789,us", "345-567-789,ca"])
      | beam.ParDo(ToProtoFn())
      | beam.io.WriteToText('/Users/greeness/data/phone-pb'))

流水线可以成功运行,并生成一个包含内容的文件:

$ cat ~/data/phone-pb-00000-of-00001 


123-456-789us


345-567-789ca

然后我编写另一个管道来读取序列化的 protobuf 并使用 ParDo 解析它们。

class ToCsvFn(beam.DoFn):
  def process(self, element):
    phone = phone_pb2.PhoneNumber()
    phone.ParseFromString(element)
    yield ",".join([phone.number, phone.country])

with beam.Pipeline(options=PipelineOptions()) as p:
  lines = (p 
      | beam.io.ReadFromText('/Users/greeness/data/phone*')
      | beam.ParDo(ToCsvFn())
      | beam.io.WriteToText('/Users/greeness/data/phone-csv'))

我在运行时收到此错误消息。

  File "/Library/Python/2.7/site-packages/apache_beam/runners/common.py", line 458, in process_outputs
  for result in results:
  File "phone_example.py", line 37, in process
phone.ParseFromString(element)
  File "/Library/Python/2.7/site-packages/google/protobuf/message.py", line 185, in ParseFromString
  self.MergeFromString(serialized)
  File "/Library/Python/2.7/site-packages/google/protobuf/internal/python_message.py", line 1069, in MergeFromString
  raise message_mod.DecodeError('Truncated message.')
  DecodeError: Truncated message. [while running 'ParDo(ToCsvFn)']

所以看起来无法解析序列化的 protobuf 字符串。我错过了什么吗?感谢您的帮助!

【问题讨论】:

    标签: python protocol-buffers apache-beam apache-beam-io


    【解决方案1】:

    我通过实现的tfrecordio.py找到了一个临时解决方案。

    下面的代码正在运行。但我仍然愿意接受任何可以解决上述问题的评论。

    import apache_beam as beam
    from apache_beam.options.pipeline_options import PipelineOptions
    import phone_pb2
    
    def WriteTextToTFRecord():
      class ToProtoFn(beam.DoFn):
        def process(self, element):
          phone = phone_pb2.PhoneNumber()
          phone.number, phone.country = element.strip().split(',')
          yield phone
      with beam.Pipeline(options=PipelineOptions()) as p:
        lines = p | beam.Create(["123-456-789,us", "345-567-789,ca"])
        processed = (
            lines
            | beam.ParDo(ToProtoFn())
            | beam.io.WriteToTFRecord('/Users/greeness/data/phone-pb',
                                      coder=beam.coders.ProtoCoder(phone_pb2.PhoneNumber().__class__)))
    
    def ReadTFRecordAndSaveAsCSV():
      class ToCsvFn(beam.DoFn):
        def process(self, element):
          yield ','.join([element.number, element.country])
      with beam.Pipeline(options=PipelineOptions()) as p:
        lines = (p
          | beam.io.ReadFromTFRecord('/Users/greeness/data/phone-pb-*',
                                     coder=beam.coders.ProtoCoder(phone_pb2.PhoneNumber().__class__))
          | beam.ParDo(ToCsvFn())
          | beam.io.WriteToText('/Users/greeness/data/phone-csv'))
    
    if __name__ == '__main__':
      WriteTextToTFRecord()
      ReadTFRecordAndSaveAsCSV()
    

    【讨论】:

      【解决方案2】:

      TFRecord 是这里的一个细节,这意味着你仍然可以让它与 TextIO 一起使用。

      这里的技巧是 Coder,它用于在管道运行期间对类型进行编码和解码。通常你应该使用它们,除非类型是内置的/微不足道的。在 protobuf 类中,使用ProtoCoder 是正确的做法。

      from google.protobuf.timestamp_pb2 import Timestamp
      import apache_beam as beam
      from apache_beam.options.pipeline_options import PipelineOptions
      
      class ToProtoFn(beam.DoFn):
        def process(self, element):
          timestamp = Timestamp()
          timestamp.seconds, timestamp.nanos = [int(x) for x in element.strip().split(',')]
          print(timestamp)
          yield timestamp
      
      with beam.Pipeline(options=PipelineOptions()) as p:
        lines = (p 
            | beam.Create(["1586753000,222333000", "1586754000,222333000"])
            | beam.ParDo(ToProtoFn())
            | beam.io.WriteToText('time-pb',
                                  coder=beam.coders.ProtoCoder(Timestamp().__class__)))
      
      class ToCsvFn(beam.DoFn):
        def process(self, element):
          print(element)
          yield ",".join([str(element.seconds), str(element.nanos)])
      
      with beam.Pipeline(options=PipelineOptions()) as p:
        lines = (p 
            | beam.io.ReadFromText('time-pb-00000-of-00001',
                                    coder=beam.coders.ProtoCoder(Timestamp().__class__))
            | beam.ParDo(ToCsvFn())
            | beam.io.WriteToText('time-csv'),
            )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-23
        • 2013-06-22
        相关资源
        最近更新 更多