你将在这个实验构建MapReduce系统。你将实现一个worker进程,它能够调用Map和Reduce函数,处理对文件的读写;同时你也要实现一个coordinator,它能够给各个worker分配任务,并且处理出错了的worker进程。在这个实验中你构建的系统类似于MapReduce论文中所实现的。(注:这个实验中使用了概念“coordinator”而不是论文中的“master”)
注:论文链接需,需要者可自行查询MapReduce论文。
为了这个实验,你需要安装Go语言:setup Go。(可自行查找安装)
你可以使用git获取这个实验的初始版本的代码。如下git clone命令获取代码:
$ git clone git://g.csail.mit.edu/6.824-golabs-2021 6.824
$ cd 6.824
$ ls
Makefile src
$
我们给你提供了mapreduce的顺序(非并发)实现,是文件src/main/mrsequential.go。它在单一进程运行了maps和reduces(注:也就是只运行一个进程,把map和reduce的工作按顺序完成了)。我们还给你提供了MapReduce的应用实现:文件mrapps/wc.go实现了 word-count;文件mrapps/indexer.go实现了a text indexer。想看一下我们最终实现的实验结果,你可以运行一下如下命令:
$ cd ~/6.824
$ cd src/main
$ go build -race -buildmode=plugin ../mrapps/wc.go
$ rm mr-out*
$ go run -race mrsequential.go wc.so pg*.txt
$ more mr-out-0
A 509
ABOUT 2
ACT 8
...
(注:如果你编译时没有使用 -race ,在运行时也不要使用 -race。)
对于mrsequential.go,它的输出文件是mr-out-0,它的输入文件是pg-xxx.txt。(xxx为任意单词)
你可以放心地从文件mrsequential.go中copy你需要的代码(这个文件本来就是给你做参考的)。你想知道的更多,也可以看一下mrapps/wc.go中的代码,来看看map和reduce函数相关的代码。
你要实现分布式MapReduce,具体实现分为两部分程序:coordinator程序和worker程序。运行时,有一个coordinator进程,一个或多个worker进程同时运行。在真正的系统中,你的worker进程应该运行在不同的机器上,不过这里你只是运行在同一个机器上。worker进程们通过RPC和coordinator通信。每一个worker进程向coordinator要任务,然后根据这个任务读入一个或多个文件,再执行这个任务,执行后输出一个或多个文件。coordinator应该关注这个worker是否在规定的合理时间内完成了这个任务(这个实验中规定的合理时间是10秒),如果超过这个时间没完成,就把这个worker要做的任务分配给其他worker。
我们已经给了你一些代码作为框架来开始你的工作。在main文件夹下,coordinator有关的代码文件为main/mrcoordinator.go,worker有关代码为main/mrworker.go,不要改变这两个文件(包括里面的内容)。你的代码实现应该在mr/coordinator.go,mr/worker.go和mr/rpc.go中。
下面讲一下如何运行这个Map Reduce系统。首先,你要保证相关plugin是最新编译的,运行如下命令:
$ go build -race -buildmode=plugin ../mrapps/wc.go
之后再main目录下,运行coordinator。
$ rm mr-out*
$ go run -race mrcoordinator.go pg-*.txt
其中pg-*.txt作为参数,是coordinator.go的输入文件;这些文件就是作为Map任务的输入文件。而 -race 是用来检测代码中的race的。
另外,在一个或多个窗口,运行worker:
$ go run -race mrworker.go wc.so
当worker和coordinator都结束后,查看一下输出文件mr-out-*。当年完成实验,你的输出文件的所有内容应该和之前运行main/mrsequential.go的输出文件的内容相匹配。像下面这样:
$ cat mr-out-* | sort | more
A 509
ABOUT 2
ACT 8
...
我们给你提供了测试脚本,是文件main/test-mr.sh。该测试脚本测试对于输入文件pg-xxx.txt,测试wc和indexer在Map Reduce应用程序中是否产生正确输出。同时该脚本能够测试你的实现是否正确,测试你的实现是否是并行运行,测试你的实现是否克服了worker进程的意外崩溃。
如果你现在运行这个测试脚本,它会停住,因为coordinator永不结束:
$ cd ~/6.824/src/main
$ bash test-mr.sh
*** Starting wc test.
你可以修改mr/coordinator.go中的Done函数,修改其中的ret := false改为ret := true,这样coordinator就能马上退出。接着运行测试脚本会看到如下:
$ bash test-mr.sh
*** Starting wc test.
sort: No such file or directory
cmp: EOF on mr-wc-all
--- wc output is not the same as mr-correct-wc.txt
--- wc test: FAIL
$
这个测试脚本会查看每一个reduce任务的输出文件mr-out-X(X为数字)。目前你还没实现mr/coordinator.go和mr/worker.go,所以还没产生这些输出文件,因此所有测试都会像上面一样失败。
当你完成你的工作,运行测试脚本后它的输出应该包含如下内容:
$ bash test-mr.sh
*** Starting wc test.
--- wc test: PASS
*** Starting indexer test.
--- indexer test: PASS
*** Starting map parallelism test.
--- map parallelism test: PASS
*** Starting reduce parallelism test.
--- reduce parallelism test: PASS
*** Starting crash test.
--- crash test: PASS
*** PASSED ALL TESTS
$
你可能也会被提示一些关于Go RPC包有关的如下错误:
2019/12/16 13:27:09 rpc.Register: method "Done" has 1 input parameters; needs exactly three
忽略这个提示,因为注册的coordinator作为RPC服务端会被检查其methods是否应该有RPC固定格式(3个输入,一个error输出),但是我们这里Done不是用于RPC通信,所以无关紧要。
enc := json.NewEncoder(file)
for _, kv := ... {
err := enc.Encode(&kv)
同时提供了如下函数,来读取key/value键值对:
dec := json.NewDecoder(file)
for {
var kv KeyValue
if err := dec.Decode(&kv); err != nil {
break
}
kva = append(kva, kv)
}