我是该项目的新手,我正在尝试在Dataflow和数据库之间创建一个连接器。
文档明确指出我应该使用Source和Sink但我看到很多人直接使用与PInput或PDone关联的PTransform。
源/接收器API处于实验阶段(使用PTransform解释所有示例),但似乎更容易将其与自定义运行器集成(例如:spark)。
如果我参考代码,则使用这两种方法。我看不到任何使用PTransform API会更有趣的用例。
Source / Sink API是否应该重新设置PTranform API?
我是否遗漏了明确区分这两种方法的内容?
Source / Sink API是否足够稳定,被认为是编码输入和输出的好方法?
谢谢你的建议!
答案 0 :(得分:12)
Dataflow的理念是 PTransform
是抽象和可组合性的主要单元,即任何自包含数据处理任务都应该封装为PTransform
。这包括连接到第三方存储系统的任务:从某个地方摄取数据或将其导出到某个地方。
以Google Cloud Datastore为例。在代码段中:
PCollection<Entity> entities =
p.apply(DatastoreIO.readFrom(dataset, query));
...
p.apply(some processing)
.apply(DatastoreIO.writeTo(dataset));
DatastoreIO.readFrom(dataset, query)
的返回类型是PTransform<PBegin, PCollection<Entity>>
的子类,DatastoreIO.writeTo(dataset)
的类型是PTransform<PCollection<Entity>, PDone>
的子类。
确实,这些函数是使用Source
和Sink
类实现的,但对于只想向Datastore读取或写入内容的用户来说,这通常是一个实现细节不应该重要(但是,请参阅本答案末尾有关公开Source
或Sink
类)的说明。任何连接器,或者就此而言,任何其他数据处理任务都是PTransform
。
注意:目前从某处读取的连接器往往是PTransform<PBegin, PCollection<T>>
,而写入某处的连接器往往是PTransform<PCollection<T>, PDone>
,但我们正在考虑选项,以便更容易使用连接器以更灵活的方式(例如,从PCollection
文件名中读取)。
然而,当然,这个细节对于想要实现新连接器的人来说很重要。特别是,您可能会问:
问:如果我可以将连接器实现为PTransform,为什么我需要Source
和Sink
类?
答:如果您只需使用内置转换(例如ParDo
,GroupByKey
等)即可实现连接器,那么这是开发连接器的完美有效方式。 但是,Source
和Sink
类提供了一些低级功能,如果您需要它们,将会很麻烦或无法自行开发。
例如,BoundedSource
和UnboundedSource
提供了用于控制并行化发生方式的挂钩(初始和动态工作重新平衡 - BoundedSource.splitIntoBundles
,BoundedReader.splitAtFraction
),而这些挂钩不是目前暴露于任意DoFn
s。
您可以通过编写以文件名作为输入的DoFn<FilePath, SomeRecord>
从技术上实现文件格式的解析器,读取文件并发出SomeRecord
,但此DoFn
将无法实现如果文件在运行时变得非常大,则动态地将文件的读取部分并行化到多个工作者上。另一方面,FileBasedSource
内置了此功能,以及处理glob文件模式等。
同样,您可以尝试通过实现DoFn
来实现连接到流系统,该ProcessingContext.output()
将虚拟元素作为输入,建立连接并将所有元素流式传输到DoFn
,但是{{1}目前,我们不支持从单个捆绑包中编写无限量的输出,也不支持明确支持Dataflow为流管道提供的强一致性保证所需的检查点和重复数据删除机制。另一方面,UnboundedSource
支持所有这些。
Sink
(更确切地说,Write.to()
PTransform
)也很有趣:它只是一个复合变换,如果你愿意,你可以自己编写(即它没有硬 - 数据流运行器或后端中的编码支持),但它是在考虑到并行将数据写入存储系统时出现的典型分布式容错问题而开发的,并且它提供了钩子,迫使您记住这些问题:例如,因为数据捆绑是并行编写的,并且某些捆绑包可能会被重试或重复以实现容错,因此只需“提交”成功完成捆绑包的结果(WriteOperation.finalize
)。
总结一下:使用Source
或Sink
API开发连接器可帮助您以一种在分布式处理设置中运行良好的方式构建代码,并且源API使您可以访问框架的高级功能。但是如果你的连接器是一个非常简单的连接器,那么你可以自由地从其他内置变换中组装你的连接器。
问:假设我决定使用Source
和Sink
。然后,我如何将连接器打包为库:我应该只提供Source
或Sink
类,还是应该将其包装到PTransform
?
答:您的连接器最终应打包为PTransform
,,以便用户可以在其管道中p.apply()
。但是,在引擎盖下,您的转换可以使用Source
和Sink
类。
一种常见的模式是公开Source
和Sink
类,使用Fluent Builder模式,并让用户将它们包装成Read.from()
或{{1变换自己,但这不是一个严格的要求。