MIT6.824 lab_1 MapReduce
这个Lab并不是真的让我们去实现MapReduce的分布式并行框架,而是通过本地开启不同的进程(线程),去模拟集群执行MapReduce程序。
课程相关文献
建议优先使用官方的相关资料,进行完成课程,本文章仅供参考。
- MIT 6.824 分布式系统课程官网mit 6.824 Distributed Systems
- MIT6.824 分布式系统实验一 MapReducemit 6.824 Lab1 MapReduce
- 谷歌MapReduce论文MapReduce Paper
源代码
https://gitee.com/eddievim/eddievim_-mit6.824_lab
定义角色
-
Master(调度)
-
Worker(进行具体的Map、Reduce)(默认初始化3个)
流程
- 通过Master角色接收到用户传进来的文件路径,找到需要进行MapReduce处理的原始文件。
- 我们这边简单对输入进行任务切分(一个文件对应一个MapTask)。
- 若输入文件有M个,则开启M个MapTask,Master调度Worker来领取任务并执行Map;
- Map执行后的结果需要进行排序,并写到磁盘中去;
- Shuffle :将MapTask的输出,改造成ReduceTask的输入。
- 直至所有MapTask全部执行完毕后,开启ReduceTask,ReduceTask的任务是到读取MapTask产生的磁盘文件中,抓取出对应分区的Key出来,再将相同的Key与List(value)执行Reduce方法,得出结果后,最终再将结果写出到结果文件中。
初始化工作
定义各种数据结构,及数据的交换格式。
状态的定义
-1: TaskWait
0: JobOver
1: MapPhrase
2: ReducePhrase
const TaskWait, JobOver, MapPhrase, ReducePhrase = -1, 0, 1, 2
Master
type Master struct {
Phrase int // 用于定义当前阶段
NReduce int // ReduceTask并行度
NMap int // MapTask并行度
MapTaskQueue chan MapTask // 用于存储MapTask的队列,线程安全
ReduceTaskQueue chan ReduceTask // 用于存储ReduceTask的队列,线程安全
MapOutFilePaths [][]string // MapTask输出的路径
ReduceOutFilePaths []string // ReduceTask输出的路径
mutex sync.Mutex // 锁,用于防止并发问题的产生
}
MapTask
type MapTask struct {
No int // 编号
Filepath string
}
ReduceTask
type ReduceTask struct {
No int // 编号
Filepaths []string
}
用于RPC通信的实体类
type RPCArgs struct {
// 状态
// 1: MapPhrase
// 2: ReducePhrase
Phrase int
OutPaths []string
}
type RPCReply struct {
// 状态
// 1: MapPhrase
// 2: ReducePhrase
// 3:JobOver
Phrase int
HoldTask bool
MapTask MapTask
ReduceTask ReduceTask
}
任务调度
Worker获取任务
在Worker处于空闲状态(没有任务正在执行时)进行调用。
Worker
通过RPC调用Master
的方法SendTask
,获取任务,如果获取则进行执行,若为reply.HoldTask
为false
,则本次RPC
调用,并没有获取到对应的任务。
// send task to worker
func (m *Master) SendTask(args *RPCArgs, reply *RPCReply) error {
m.mutex.Lock()
reply.Phrase = m.Phrase
m.mutex.Unlock()
reply.HoldTask = true
switch reply.Phrase {
case MapPhrase:
reply.MapTask = <-m.MapTaskQueue
case ReducePhrase:
reply.ReduceTask = <-m.ReduceTaskQueue
case TaskWait:
reply.HoldTask = false
default:
reply.HoldTask = false
}
return nil
}
Worker汇报任务
在Worker完成一次任务的时候进行调用。
Worker
通过RPC调用Master
的ReportTask
,汇报任务。
- 若在
MapTask
阶段,所有任务被处理完毕,则进入shuffle
状态。 - 若在
ReduceTask
阶段,所有任务被处理完毕后,将状态舍为JobOver
,这将会通知Worker
,使其结束进程。
// worker report task to master
func (m *Master) ReportTask(args *RPCArgs, reply *RPCReply) error {
m.mutex.Lock()
switch m.Phrase {
case MapPhrase:
m.MapOutFilePaths = append(m.MapOutFilePaths, args.OutPaths)
// Map阶段完成了所有任务,进入Reduce阶段
if len(m.MapOutFilePaths) == m.NMap {
m.Phrase = TaskWait
m.shuffle()
m.Phrase = ReducePhrase
}
case ReducePhrase:
m.ReduceOutFilePaths = append(m.ReduceOutFilePaths, args.OutPaths[0])
// Reduce阶段结束
if len(m.ReduceOutFilePaths) == m.NReduce {
m.Phrase = JobOver
fmt.Println(m.Phrase)
}
}
m.mutex.Unlock()
return nil
}
阶段的过渡
Shuffle(MapPhrase -> ReducePhrase)
将MapTask
的输出,将其“改造”为ReduceTask
的输入。将MapTask输出的结果,根据不同的组,将同组的数据组合在一起。
// MapPhrase -> ReducePhrase
func (m *Master) shuffle() {
for i := 0; i < m.NReduce; i++ {
task := ReduceTask{
No: i,
}
for j := 0; j < m.NMap; j++ {
task.Filepaths = append(task.Filepaths, m.MapOutFilePaths[j][i])
}
m.ReduceTaskQueue <- task
}
}
Done
用于判断所有任务流程是否结束,只需要判断Phrase
是否是JobOver
即可。
func (m *Master) Done() bool {
return m.Phrase == JobOver
}
Worker的工作模式
持续对Master
进行轮询,从而持续地获取任务及状态,当到达JobOver
阶段时,则结束。
func Worker(mapf func(string, string) []KeyValue,
reducef func(string, []string) string) {
for {
reply := getTask()
switch reply.Phrase {
case MapPhrase:
opaths, ok := MapProcess(mapf, reply)
if ok {
fmt.Printf("mapProcess %v OK.\n", reply.MapTask.No)
call("Master.ReportTask", &RPCArgs{MapPhrase, opaths}, reply)
} else {
fmt.Printf("mapProcess %v failed.\n", reply.MapTask.No)
}
case ReducePhrase:
path, ok := ReduceProcess(reducef, reply)
if ok {
fmt.Printf("ReduceProcess %v OK.\n", reply.ReduceTask.No)
call("Master.ReportTask", &RPCArgs{ReducePhrase, []string{path}}, reply)
} else {
fmt.Printf("ReduceProcess %v Fail.\n", reply.ReduceTask.No)
}
case TaskWait:
time.Sleep(time.Millisecond)
case JobOver:
return
}
}
}
其余的操作
剩下的步骤都是Worker
获取文件路径后,对文件内容进行读取,然后进行相应操作Mapf/Reducef
,然后再输出,持久化到磁盘中,这里不过多阐述,需要的同学可以到我的Gitee代码仓库进行查看。链接🔗