Introduction
本次实验主要是用go语言来实现1个MapReduce库,并且了解分布式系统的容错机制,推荐先看一下MapReduce的论文[MapReduce]
主要机制如下图:
Map worker读入1个文件,处理后生成n(reduce worker数目)个中间文件,然后每个Reduce worker读取其对应的所有中间文件,处理后生成1个结果文件,最后n个结果文件可以merge成1个最终结果文件。
Software
由于实验中的代码是由go语言实现的,所以需要先去看看go的基本语法以及相关教程,链接在这里Go(ps:需要翻墙/(ㄒoㄒ)/~~)
使用git来下载实验的代码,repository为git://g.csail.mit.edu/6.824-golabs-2016。然后再去安装golang的编译器即可,这里实验中使用的是1.5版本。
Preamble: Getting familiar with the source
mapreduce包提供了1个简单的Map/Reduce库的串行实现。正常应用应该调用Distributed函数[master.go]来启动1个任务,但是可以通过调用Sequential函数[master.go]来进行debug。
mapreduce实现流程:
1、应用提供一些输入文件,1个map函数,1个recude函数,reduce worker的数目(nReduce)。
2、建立1个master节点,它启动1个RPC server(master_rpc.go),然后等待workers来注册(使用RPC call Register函数[master.go]). 当worker可用时(在第4、5步骤),schedule函数[schedule.go]决定如何分配任务到worker以及如何处理worker的failures。
3、master节点认为每个输入文件对应1个map任务,为每个任务至少调用1次doMap函数[common_map.go]。每次调用doMap函数会读取合适的文件,并调用map函数来处理文件内容,为每个map文件生成nReduce个文件。
4、master节点接下去为每个reduce任务至少调用1次doReduce函数[common_reduce.go]。doReduce函数收集nReduce个reduce文件,然后调用reduce函数处理这些文件,产生nReduce个结果文件。
5、master节点调用mr.merge函数[master_splitmerge.go],来整合nReduce个结果文件成1个最终文件。
6、master节点发送1个Shutdown的RPC调用到每个worker,来关闭它们的RPC server。
Part I: Map/Reduce input and output
首先是实现Map/Reduce的串行执行,在给出的代码中缺少2个关键的片段:将map任务的输出分割成nReduce个文件的 doMap函数[common_map.go],以及将reduce任务的nReduce个输入整合成1个文件的doReduce函数[common_reduce.go]。
为了帮助测试你的代码是否正确,实验中还提供了test文件[test_test.go]来测试。关于go test命令的说明可以看这里go test。
go test -run Sequential mapreduce/...
run参数表示测试mapreduce文件夹下test_test.go文件中以Test开头包含Sequential的测试函数,即TestSequentialSingle和TestSequentialMany函数。这里针对TestSequentialSingle来分析一下具体流程,有助于编写doMap和doReduce函数。
func TestSequentialSingle(t *testing.T) {
mr := Sequential("test", makeInputs(1), 1, MapFunc, ReduceFunc)
mr.Wait()
check(t, mr.files)
checkWorker(t, mr.stats)
cleanup(mr)
}
func Sequential(jobName string, files []string, nreduce int,
mapF func(string, string) []KeyValue,
reduceF func(string, []string) string,
) (mr *Master) {
mr = newMaster("master")
go mr.run(jobName, files, nreduce, func(phase jobPhase) {
switch phase {
case mapPhase:
for i, f := range mr.files {
doMap(mr.jobName, i, f, mr.nReduce, mapF)
}
case reducePhase:
for i := 0; i < mr.nReduce; i++ {
doReduce(mr.jobName, i, len(mr.files), reduceF)
}
}
}, func() {
mr.stats = []int{
len(files) + nreduce}
})
return
}
首先通过Sequential函数[master.go]来创建1个master结构体,然后运行1个goroutine来执行mr.run函数。其中master结构体的定义如下:
type Master struct {
sync.Mutex
address string
registerChannel chan string
doneChannel chan bool
workers []string // protected by the mutex
// Per-task information
jobName string // Name of currently executing job
f