整个SeaTunnel设计的核心是利用设计模式中的控制翻转
或者叫依赖注入
,主要概括为以下两点:
对于整个数据处理过程,大致可以分为以下几个流程:输入 -> 转换 -> 输出,对于更复杂的数据处理,实质上也是这几种行为的组合:
SeaTunnel将数据处理的各种行为抽象成Plugin
,并使用SPI
技术进行动态注册,设计思路保证了框架的灵活扩展,在以上理论基础上,数据的转换与处理还需要做统一的抽象,譬如比较有名异构数据源同步工具DataX
,也同样对数据单条记录做了统一抽象。
在SeaTunnel V1架构体系中,由于背靠Spark和Flink两大分布式计算框架,框架已经为我们做好了数据源抽象的工作,Flink的DataStream、Spark的DataFrame已经是对接入数据源的高度抽象,在此基础上我们只需要在插件中处理这些数据抽象即可,同时借助于Flink和Spark提供的SQL接口,还可以将每一次处理完的数据注册成表,方便用SQL进行处理,减少代码的开发量。
实际上SeaTunnel最后的目的是自动生成一个Spark或者一个Flink作业,并提交到集群中运行。
目前在项目dev
分支下,SeaTunnel连接器V1 API所在的模块如图所示:
在基础模块中,有以下代码:
为了更清晰的理解这些类之间的关系,笔者这里制作了一张简单的UML类图:
整个API的组成可以大体分为三部分:
构建层接收命令参数构建执行器,执行器初始化上下文,上下文注册插件并启动插件,至此,整个作业开始运行。
在Spark引擎API层有以下代码:
同样,笔者也整理了一张UML类图来表示它们之间的关系:
整个流程与Base模块一致,在这里笔者不过多赘述,有兴趣的读者可以自行观看源码。
在Flink引擎API层有以下代码:
同样,笔者也整理了一张UML类图来表示它们之间的关系:
整个流程与Base模块一致,在这里笔者不过多赘述,有兴趣的读者可以自行观看源码。
整个项目的最外层的启动类都放在以下模块中:
跟连接器V1有关的模块如下:
为了更好的理解SeaTunnel V1的启动流程,笔者在这里制作了一张简单的时序图:
程序最外层的启动由start-seatunnel-${engine}.sh
开始,用户根据将配置文件从脚本传入,脚本调用org.apache.seatunnel.core.spark.SparkStarter
或者org.apache.seatunnel.core.flink.FlinkStarter
,实际上这个类只做一个工作:将所有参数拼接成spark-submit
或者flink
命令,而后脚本接收到spark-submit
或者flink
命令并提交到集群中;提交到集群中真正执行job的类实际上是org.apache.seatunnel.spark.SeatunnelSpark
或是org.apache.seatunnel.flink.SeatunnelFlink
,读者如果想直接深入了解作业启动核心流程的话推荐阅读这两个类的源码。
目前在项目dev分支下,SeaTunnel连接器V2 API所在的模块如图所示:
SeaTunnel连接器V2 API在数据层面做了抽象,定义了自己的数据类型,这是与连接器V1最大的不同点,连接器V1使用的是引擎数据抽象的能力,但是连接器V2自己提供的这个异构数据源统一的能力:
在所有的Source连接器和Sink连接器中,处理的都是SeaTunnelRow
类型数据,同时SeaTunnel也对内设置了数据类型规范,所有通过Source接入进来的数据会被对应的连接器转化为SeaTunnelRow
送到下游。
在API common包下有以下接口的定义:
在这里由于篇幅关系只介绍比较核心的几个接口:
具体接口中有哪些方法读者可以自行阅读对应类的源码,在这里笔者将不过多赘述。
在API source包下有以下接口的定义:
在这里由于篇幅关系只介绍比较核心的几个接口:
在API sink包下有以下接口的定义:
在这里由于篇幅关系只介绍比较核心的几个接口:
SinkWriter#prepareCommit
返回的数据信息,包含需要提交的事务信息,连接器V2在Sink设计上提供二阶段提交的接口,从而使连接器有了实现Exactly-Once的可能性SinkWriter#prepareCommit
返回的数据信息,包含需要提交的事务信息等,用于在单节点多任务一起提交事务信息,这样可以避免提交阶段二部分失败导致状态不一致的问题(注:在实现连接器时优先实现这个接口,这样会兼容性更强)连接器V2在架构分层上与计算引擎进行解耦,定义了自己的元数据定义以及数据类型定义,在API层和计算引擎层增加了翻译层,将SeaTunnel自定义的数据源通过翻译层接入到引擎中,从而真正实现接口和引擎分离的目的。
整个项目的最外层的启动类都放在以下模块中:
跟连接器V2有关的模块如下:
为了更好的理解SeaTunnel V2的启动流程,笔者在这里制作了一张简单的时序图:
程序最外层的启动由start-seatunnel-${engine}-new-connector.sh
开始,用户根据将配置文件从脚本传入,脚本调用org.apache.seatunnel.core.spark.SparkStarter
或者org.apache.seatunnel.core.flink.FlinkStarter
,实际上这个类只做一个工作:将所有参数拼接成spark-submit
或者flink
命令,而后脚本接收到spark-submit
或者flink
命令并提交到集群中;提交到集群中真正执行job的类实际上是org.apache.seatunnel.spark.SeatunnelSpark
或是org.apache.seatunnel.flink.SeatunnelFlink
,读者如果想直接深入了解作业启动核心流程的话推荐阅读这两个类的源码,连接器V2和连接器V1的启动流程基本一致。
SeaTunnel Source连接器V2将异构数据源接入,生成以SeaTunnelRow
为基本单位的数据源,在翻译层实现了Spark DataSource API V2
,翻译层使得Spark可以接入以SeaTunnelRow
为基本单位的数据源,从而实现无缝接入Spark的目的。
关于Spark DataSource API V2
的详细信息,读者可以参考:https://www.databricks.com/session/apache-spark-data-source-v2,由于这篇文章的主题并不是介绍Spark的特性,所以笔者在这里不过多赘述。
SeaTunnel Source连接器V2将异构数据源接入,生成以SeaTunnelRow
为基本单位的数据源,同时在翻译层实现了Flink source function
和Flink sink function
,翻译层使得Flink可以接入以SeaTunnelRow
为基本单位的数据源,从而实现无缝接入Flink的目的。
关于Flink source Function
和Flink sink function
的详细信息,读者可以参考:https://nightlies.apache.org/flink/flink-docs-release-1.15/docs/dev/datastream/sources/#the-data-source-api,由于这篇文章的主题并不是介绍Flink的特性,所以笔者在这里不过多赘述。
Source连接器接入数据源为SeaTunnelRow,Translation层转换SeaTunnelRow数据源为各种计算引擎内部的数据源,Sink 连接器接收计算引擎内部转换好的SeaTunnelRow数据源并写入到目标数据源中。
特征 | 连接器V1 | 连接器V2 |
---|---|---|
引擎依赖 | 强依赖Spark、Flink | 无依赖 |
连接器实现 | 针对不同引擎要实现多次 | 只实现一遍 |
引擎版本升级难易程度 | 较难,连接器与引擎高度耦合 | 较易,针对不同版本开发不同翻译层即可 |
连接器参数是否统一 | 针对不同引擎可能会有不同参数 | 参数统一 |
自定义分片逻辑 | 依赖Spark、Flink已经实现好的数据Connector,分片逻辑不可控 | 分片逻辑可自定义 |
目前社区正在做的事情:
未来目标:
最终目标:成功从Apache孵化器毕业,成为世界一流的诞生于中国的数据集成平台工具
目前社区正在蓬勃向前发展,大量feature
需要去开发实现,毕业之路道阻且艰,期待更多的有志之士参与到社区共建,欢迎热爱开源的小伙伴加入SeaTunnel社区,有意者可发邮件至[email protected]
或微信tyrantlucifer
联系我咨询相关事宜,让我们一起用开源点燃璀璨的程序人生。