任务切分
DataX完成单个数据同步的作业,称之为Job,这个Job会做split阶段,拆分成一个个的Task,每个Task都会有一个Reader和Writer的任务。
拆分成多个Task是为了并发的执行,这个并发数,就是之前的Channel数量。
我们假设这个Job在split阶段,有100个Task,而Channel数量是5,那实际的Channel数量取100和5的最小值,也就是5。
每个并发执行的Task数量并不是简单地100/5=20,而是先把Task放入TaskGroup中,然后把并发分给TaskGroup。
比如每个TaskGroup的并发数量是5,通过core.container.taskGroup.channel进行设置,那TaskGroup的数量就是总的Channel数/TaskGroup需要的数量,所有TaskGroup的数量就是20/5=4。
由于有100个Task,一共4个TaskGroup,所有每个TaskGroup就有100/4=25个Task。
任务启动
首先是启动线程池,这个线程池具有一个固定的数量,即taskGroup的数量。每个线程都启动一个TaskGroupContainer,每个TaskGroupContainer都包含JobId、taskGroupId、channelClazz以及configuration等信息。
TaskGroupContainer启动的时候,就会把25个任务,存放在taskQueue集合中。另外会创建一个大小为5的runTasks集合中,存放着Task执行器TaskExecutor。
Task执行器的创建,依赖着taskQueue集合,每次从taskQueue集合中拿出一个task创建Task执行器并启动,就需要从taskQueue集合移除这个task。
由于每个TaskGroup的并发数量是5,所以Task执行器最多同时存在5个,也就是说,25个task,先执行5个,等某个或者多个执行完后,再执行剩下的task。直至taskQueue集合为空以及每个Task执行器都执行完,那这个25个task也就执行完了。
Task执行器
每个Task都对应一个Task执行器,Task执行器包括任务的运行配置、taskId、channel、用来读写的线程等。
读线程启动的是ReaderRunner,里面有插件(比如StreamReader$Task)、RecordSender等属性。
写线程启动的是WriterRunner,里面有插件(比如StreamWriter$Task)、RecordReceiver等属性。
Task执行器启动的时候,首先是启动WriterRunner的线程,然后是ReaderRunner的线程。
WriterRunner和ReaderRunner的执行顺序如下,左边是Reader右边是Writer。
主要的部分是Reader的startRead和Writer的startWriter。
首先是Reader把数据读取出来,然后封装在Record中,这个Record包含了数据集合Column、byteSize、以及需要的内存memorySize。Column就是我们读取的每条数据,包括数据的类型,数据的内容等。
读取出来的数据,发送给Channel,目前默认的是基于内存的MemoryChannel,也可以自己扩展。MemoryChannel维护着Record的阻塞队列queue。当有数据存进的queue时候,就会唤醒Writer的读取操作。
由于Channel是共用的,所以Writer就会从Channel的queue里读取Reader存入的数据,进行业务操作。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。