DataX的架构设计以及思考

706 阅读5分钟

dataX是阿里开源的一款数据传输框架。不按正常的流程介绍吧,反正也没有文档详细。如果对该框架不了解,建议先看文档。

如何设计一款数据传输框架?可能最初拿到的需求就是能够从一个数据源拉取数据并输出到目标数据源。这里的数据源不限于DB。很自然会联想到可能需要一个拉取数据的抽象层(reader),一个接收数据的抽象层(writer)。可能会需要channel的概念。并且中间可能会有多个channel。这里channel的抽象对应于jdk.nio.channel也就是数据的载体。数据不应该直接从reader到writer上,而是通过管道进行传输。然后可以在channel这一层做数据限流,增加缓冲区。现在最基本的架构已经出来了,之后还需要什么?性能,怎么提升性能,通过提高并行度的方式。那么就涉及到任务的拆分,以及线程隔离。不同任务应该通过单独的线程去执行,可能使用了线程池。也可能单独使用新的线程。那么对任务整体应当需要一个实体去概括。可以认为一个大的任务通过某种拆分策略,分解出很多小的任务,每个任务对应一条线程去执行。任务可能需要某种重试机制,还有相应的重试次数上限。(尴尬了,感觉任务组的作用不是很强,想到的一个点就是总任务在拆解成多个子任务后,并通过一定的分组策略生成多个taskGroup。然后在集群模式下可能可以将任务发送到集群中其他机器。并接收其他机器的处理结果,以判断所有task是否完成。老实说强行增加了复杂度。如果能够将任务拆分成多个组,其中某些组的失败不会影响到整体,那么这层抽象可能还有点意义)

基本的推测是这样的,接下来看看DataX中设计了那些核心组件。

Job:对应总的任务,这个任务是需要拆分的。

Reader:继承于一个插件骨架类,针对不同的源数据源,可能需要使用不同的方式进行数据读取和插入,而这部分逻辑由插件提供者实现。

Reader.Job:Reader的内部类。定义了如何将Job拆解成多个task的逻辑

Reader.Task:Reader的内部类。定义了如何读取数据

Writer: 继承于一个插件骨架类,基于不同的目标数据源,定义了自己的写入逻辑

Writer.Job:Writer的内部类。定义如何将Job拆解成多个task

Writer.Task:Writer的内部类。定义了如何写入数据

Task: 数据传输的最小单位

Task.writeRunner/readerRunner: 每个task内部分别包含读取逻辑和写入逻辑。委托给内部的2个成员变量实现。实际上runner内部包含Reader/Writer插件类。实际上是委托给插件去执行任务的。writeRunner/readerRunner分别对应一条线程。

TaskGroup: Task随机打乱后分配到group中

Sender:实际上reader读取到数据后会暂存到sender中,由sender决定什么时候将数据发往channel。 也就是一个缓冲区。在不知道channel底层实现的情况下,选择通过缓冲区+批量写入的方式是一种良好的设计。也许channel的实现可能使用了存储技术或者其他IO操作,那么批量写入就能够提高效率。 Receiver:与Sender相对。由writer来调用,从channel中搬运数据。同样也使用了缓冲区。写入目标数据源一般都是耗时操作。通过批量写入的思路提高效率。 Channel:数据传输的通道。DataX中默认实现是通过一个阻塞队列。

JobContainer:内部包含了描述Job运行状况的信息,job级别的配置项信息。以及如何加载插件。如何拆分任务,分组。并通过schduler对象启动多个TaskGroupContainer。 TaskGroupContainer:管理每个TG级别的运行信息,配置信息。负责管理每个task的启动,监控任务是否失败,决定是否重试或者终止程序。

大体的运行流程是这样的。(忽略一系列的配置解析,数据统计)。由Engine对象创建JobContainer,对Job进行拆分+分组。生成了一系列的TaskGroupContainer对象。每个TaskGroupContainer会按照分配的channel,创建等量的task并运行。channel的数量算是一种IO资源消耗的描述吧。如果对并发的任务量不做限制,可能会占用过多的IO资源,导致其他程序无法运行。或者瞬间加载过量的数据到内存导致OOM。但是很遗憾的是他不想ES能够对内存使用量严格把控。在byte级别做到限流,实际上无法断定每个任务会产生多少内存消耗。而运行task的逻辑就是同时启动readRunner/writeRunner。通过读插件将数据写入到channel,并通过写插件进行消费。

老是说看代码的时候,感觉DataX就像一个半成品,内部充斥着很多TODO,并且io限流也没有做。也不支持集群处理。而且没有让人耳目一新的感觉,按照正常的思考方式也能把架构猜个7788。不灵活,将太多的逻辑交托给插件。而且无法做到数据的多向传输,比如一个输入源可能下游有很多消费者。参照物就是camel。如何路由,配置路由规则可能才是ETL的精髓。