源码地址:https://github.com/liusir521/mit6.5840
前言
久闻mit6.824大名,一直没找到合适的时机进行实操,最近在工作之余对此课程进行了学习。这篇文章总结一下个人学习lab1时的一些心得体会。
lab1简介
在本实验中,你将构建一个 MapReduce 系统。你将实现一个工作进程,该进程调用应用程序的 Map 和 Reduce 函数并处理文件的读写操作;以及一个协调器进程,该进程将任务分配给工作进程并处理故障的工作进程。
实验中给出了很多细节上的方案与提示,建议先多看几遍实验任务。
流程介绍:我们需要通过多个Map任务去读取系统给出的文件中的单词,并根据单词的key的hash值将其归入指定的中间文件中(mr-X-Y),当所有的Map任务均处理完毕之后,启动Reduce任务,每个Reduce任务会找到自己对应的中间文件并对内部的单词进行统计,最后输出到对应的文件中(mr-out-Y)。
流程图如下:

其中X代表的是map任务的编号,Y代表的是reduce任务的编号
总体设计
总体需要我们来实现的其实并不是关于KeyValue键值对的处理,这部分可以参考示例中给出的代码,无需我们再次实现,只需在原本单机场景之下改写为多任务模式即可。主要需要我们实现的是关于任务的分配处理代码。主要包括三部分:
-
rpc:定义实验中所需要的关于rpc通讯用到的相关结构体
-
Worker:定义工作进程相关的代码
-
Coordinator:起到协调作用,定义关于任务分配等相关的具体实现
具体实现
Coordinator
Coordinator中需要我们定义整体任务分配相关的结构体,从原有的函数中可以看出还需要我们实现rpc被调用方的相关函数,包括获取任务、完成任务以及Coordinator初始化。同时官方给出需要进行超时校验(10s),我们还需要在获取任务时进行超时校验。
关于Coordinator结构体我们需要定义的关键内容有:
-
files:文件数组,用于分配给各个map任务执行,每个map对应一个文件,所以map的总任务数其实就是文件总数
-
nReduce:系统指定的reduce任务的总数
-
mapTasks:map的任务列表
-
reduceTasks:reduce的任务列表
-
isMapFinished:map任务是否全部完成的标志
-
isReduceFinished:reduce任务是否全部完成的标志,只有map任务全部完成之后才会启动reduce任务,reduce任务全部完成之后也就标志的整体流程结束
-
sync.Mutex:同理需要一个锁对象来控制流程(也可以对map和reduce分别设置,这里简便实现只设置一个)
关于Task任务结构体我们需要定义的关键内容有:
-
TaskType:任务类型,map or reduce
-
TaskStatus:任务状态,等待中、运行中、已完成
-
NReduce:系统NReduce变量,用于map任务中对应的key流向哪个文件(hash之后对NReduce取余)
-
MapTaskIndex:如果是map任务,当前map任务的编号
-
ReduceTaskIndex:如果是reduce任务,当前reduce任务的编号
-
FileName:如果是map任务,当前map任务要处理的文件名
我这里只是一个粗略的设计,有很多地方可以进行优化,比如可以添加map、reduce还未完成的数量,通过原子类操作数量的方式会比我这里使用遍历的方式更加的合理。锁也可以设计为两个,在超时校验时或许可以再节省一些时间。
获取任务
在获取任务时,需要首先判断是否存在超时文件,然后接着首先判断是否需要分配map任务,先把map任务分配完毕,然后才是reduce任务,当reduce任务也执行完毕,就返回退出标志。(当map任务都在执行,reduce任务尚未开启,此时返回等待标志。reduce任务都在执行时也是)
关键代码如下:
|
|
刚开始时,我在校验超时时间中犯了个逻辑错误,我先校验了map任务是否都已经完成了,完成之后我直接return了,这个操作就导致了后面超时函数无法校验到reduce任务的状态(是否存在超时任务),就导致最后一个测验一直无法通过。
完成任务
在完成任务中,我们需要判断当前完成的任务的类型,然后判断是否当前所有类型的任务都完成了。关键代码如下:
|
|
rpc
rpc.go文件中主要是对实验中需要用到的rpc相关的请求和响应的相关结构体,关键代码如下:
|
|
其中,GetTaskReply就是获取task任务的响应结构体,根据tasktype字段判断当前任务是map任务还是reduce任务,然后再从里面获取需要使用到的字段。
Worker
在worker中,需要我们通过rpc向调度器获取任务并执行具体的任务,当任务执行完毕时通过rpc向调度器汇报。其中具体的执行任务流程可以参考示例中给出的代码。关于文件的操作,可以使用课程中提示的中间文件的形式。
关键代码如下:
|
|
总结
个人感觉,实验主要还是考验的思维逻辑能力,而不是一些细节实现,毕竟一些关键实现实验中已经提供了。
代码执行结果如下:
