研究生转开发历程
第一个lab是实现一个分布式的mapreduce框架。
mapReduce中map指的是把大任务分成小任务,让worker来执行的过程,而reduce指的是将map的执行结果进行汇总,lab中的任务是统计文件中各个单词的出现次数。
题目要求
有一个coordinator作为调度器,很多的worker作为工作进程,worker和coordinator之间使用rpc进行通信,只有worker能发出请求收到回复,而coordinator是无法主动发出请求的。在所有的map任务未完成之前不会做reduce任务,工作进程worker可能会随机挂掉。要处理这种情况。
代码阅读
首先阅读下代码结构,mit的代码都有注释,从文件角度看一共涉及到五个文件,mrcoordinator和mrworker分别是coordinator和worker的入口文件。
在coordinator.go文件中,启动了一个监听进程,一旦有worker的请求,就会跳转到RPCHandler函数中,RPCArgs是worker传入的参数,RPCReply是coordinator返回的参数。
在worker.go文件中,可以在worker函数中写代码,通过一个for循环不断的使用call调用来向coordinator发出请求。
系统实现
对于系统的实现,规定有两种类型的任务(map和reduce),每种任务有三个状态(未分配,已分配,已完成),三种状态的转换如下。
coordinator端
而在coordinator中,需要记录这些全局的信息,如下所示。每次请求检查
type Coordinator struct {
// Your definitions here.
files []string //文件名字,需要解析的文件数,每个map负责一个文件
maps []bool// map任务分配的数组,map[i]=false表示第i个map任务未分配。
map_num int // 未完成的map任务的数量
map_finished []bool// map任务完成的数组,map[i]=false表示第i个map任务未完成。
reduces []bool//与map类似,不过是reduce任务
ReduceNum int
reduce_finished []bool
}
在worker端
定义:worker发出的请求有两种类型:
- 第一种是发出请求,让coordinator来分配任务。
- 第二种是报告coordinator,该worker已经处理完任务了。
因此使用在coordinator处写两个处理函数,来处理不同的worker请求。
程序流程如下:
- 在coordinator端,初始时,所有任务存到map里,均为未分配。
- 每初始化一个worker,就先向coordinator发送请求分配任务信号。
- coordinator检查下当前的map_num,如果为0,代表map任务已经完成,则发送reduce任务,如果不为0,则发送map任务。
- 无论是map任务还是reduce任务,返回给worker信号时,都新建一个协程,sleep10s,之后检查任务是否完成,如果未完成,就回收该任务,更改任务状态。
-
go func() { time.Sleep(time.Duration(10 * time.Second)) lock.Lock() fmt.Println("kill map task ", i) if !(c.map_finished[cur]) { c.maps[cur] = false } lock.Unlock() }()
- worker处理完任务后,向coordinator发送任务完成信号。
- coordinator修改任务状态为完成相应的未完成任务数减一。
大致思路就是这样,代码就不贴了。