- 用户从client端提交一个任务,此任务指定了运行的jar包,除java jre外依赖的jar包,待处理的数据文件以及输出文件夹的位置和名称
- client端首先检查此任务输出的文件夹是否存在,然后向JobTracker为此任务申请一个id,然后在hdfs中创建一个对应此任务的文件夹,将这个任务依赖的外部jar包以及任务要运行的jar包放入此任务的文件夹中;然后调用任务的InputFormat中的getSplit方法来计算inputSplit,其中每一个inputSplit是由一个mapper来处理的,inputSplit对象在Hadoop的定义中仅仅有两个属性Length和Locations,用户可以定义自己的inputSplit对象来包含更多的属性。计算完毕的inputSplit文件放到Hdfs中job所对应的folder里面
- client端调用Submit方法来提交任务,通过RPC调用JobTracker的Submit方法,JobTracker的Submit方法首先生成Hadoop任务,然后将此任务根据此任务的配置来初始化生成对应的Map和Reduce Task,其中Map Task的数量跟InputSplit的数量相同,而Reduce Task的数量是由用户在配置文件中指定的,如果用户不指定,则默认值为1。
- 初始化后的Map和Reduce Task会放到队列中,直到JobTracker收到TaskTracker的心跳,如果JobTracker发现TaskTracker的心跳中声明目前TaskTracker是空闲并且能够执行任务的,那么,JobTracker会将Mapper和Reducer Task分配给TaskTracker执行,TaskTracker收到心跳的回应中如果携带了要执行的Mapper或者Reducer的信息,则TaskTracker会开始执行分配到的Task
- TaskTracker执行Mapper的时候,会首先去HDFS中取出相应的Split描述信息和任务依赖的jar包以及任务执行的jar包,然后根据Split信息读取相应的数据文件中的部分。首先调用RecordReader中的getCurrentKey和getCurrentValue读取下一个Key,Value然后将此Key, Value传递给Mapper的Map函数,在Map函数中将其转换成为MapOutputKey,MapOutputValue。
- Mapper的输出,如果没有Reducer,会直接排序输出到硬盘上,如果有Reducer,Mapper的输出会首先经过Partitioner的计算,计算输出的Key,Value是要分到哪一个Reducer,然后会首先存储在内存中,如果内存中放不下后,会对这一部分结果进行spill out,在将内存中的数据spill out到硬盘的时候,会调用SortAndSpill对输出结果进行排序,以便保证一个文件中结果是有序的。每次spill out会产生一个文件,这个文件中包含多个partition的数据,当产生多个文件后,会对这多个有序文件进行归并排序,即合并操作。此过程会重复直到Mapper所有的值输出完毕为止,Mapper会一个文件,此文件中会先按partition对元组进行排序,然后相同partition中的元祖,会按照key来进行排序。
- Reducer启动之后,会不断从JobTracker返回的heartbeat response中获取每个Mapper是否完成的信息。会启动默认的几个copy线程,默认值是5,从每个Mapper的输出结果中拷贝数据,拷贝数据使用的是Http协议,当从每个Mapper中获取到应该处理的数据之后,会对输入的值进行一个归并排序,然后对每一个输入的Key,Set<Value>应用Reducer中的Reduce函数,输出后的结果会直接写到硬盘上,作为输出结果。
Comment:
- JobTracker选择哪一个任务进行初始化是由Scheduler的逻辑实现来决定的,用户可以提供自定义的Scheduler
- TaskTracker会启动一个Jvm来执行Mapper或者Reducer,除非用户指定需要重用之前启动的Jvm
- 用户可以提供Combiner来对Mapper的输出进行初步的合并,Combiner会在Partitioner之后执行,主要是在Mapper的结果Spillout到文件的时候进行combine操作。
- 用户可以提供Partitioner来确定Mapper输出的Key,Value 被分到哪一个Reducer,可以用来防止数据倾斜。
- 用户可提供自定义InputFormat来定义如何产生Split
- 用户可提供自定义RecordReader来定义如何从输入中产生Key,Value对
- 用户可提供自定义OutputFormat和RecordWriter来定义如何输出。