本文将记录MIT6.824的学习记录
环境
ubuntu22.04
go 1.13.15
gcc 11.4.0
下载lab1
git clone git://g.csail.mit.edu/6.824-golabs-2020 6.824
测试项目中的word-count程序
$ cd ~/6.824
$ cd src/main
$ go build -buildmode=plugin ../mrapps/wc.go
$ rm mr-out*
$ go run mrsequential.go wc.so pg*.txt
$ more mr-out-0
实验目录结构
main目录
mr目录
实验目的
实现一个MapReduce,他由一个master进程,一个或多个worker进程组成。本实验由单个机器模拟多台服务器,master与worker之间通过RPC对话。
每个工作进程都会向从一个或多个文件中读取任务的输入,执行任务,并将任务的输出写入一个或多个文件。
master需要监视每一个worker是否在合理的时间(本实验中为10s)完成任务,若没有,则将分配给它的任务交付给另一个worker。
实验任务
本实验一共有五个测试任务
- wc test: 验证编写的程序是否得到正确的单词统计结结果
- indexer test: 验证编写的程序结果是否来自正确的文件
- map parallelism test: 验证Map任务是否多线程执行
- reduce parallelism test: 验证Reduce任务是否多线程执行
- crash test: 健壮性测试,测试worker崩溃或执行太慢时,本程序依然可以获得正确的结果
实验要求
- Map 阶段应该将中间键分成 nReduce 个减小任务的桶,其中 nReduce 是 main/mrmaster.go 传递给 MakeMaster() 的参数。
- Worker 的实现应该将第 X 个减小任务的输出放入文件 mr-out-X 中。
- 一个 mr-out-X 文件应该包含一行每个 Reduce 函数输出。这一行应该使用 Go 的 "%v %v" 格式生成,调用时使用键和值。请在 main/mrsequential.go 中查看带有注释 "this is the correct format" 的行。如果您的实现与此格式偏离太多,测试脚本将失败。
- 您可以修改 mr/worker.go、mr/master.go 和 mr/rpc.go。您可以暂时修改其他文件进行测试,但确保您的代码与原始版本一起运行;我们将使用原始版本进行测试。
- Worker 应该将中间 Map 输出放在当前目录的文件中,您的 worker 可以稍后将它们作为 Reduce 任务的输入进行读取。
- main/mrmaster.go 期望 mr/master.go 实现一个 Done() 方法,当 MapReduce 作业完全完成时返回 true;在那时,mrmaster.go 将退出。
- 当工作完全完成时,工作进程应该退出。实现这一点的一种简单方法是使用 call() 的返回值:如果 worker 未能联系 master,它可以假设 master 已经退出,因为工作已经完成,所以 worker 也可以终止。根据您的设计,您可能还会发现有一个由 master 分配给 worker 的“请退出”伪任务也很有帮助。
提示
- 为了开始进行修改,一种方式是修改 mr/worker.go 中的 Worker() 函数,向主节点发送一个 RPC 请求以获取一个任务。然后修改主节点,响应一个尚未启动的 Map 任务的文件名。接着,修改 worker 以读取该文件并调用应用程序的 Map 函数,就像在 mrsequential.go 中一样。
- 应用程序的 Map 和 Reduce 函数是在运行时使用 Go 插件包加载的,文件名以 .so 结尾。
- 如果您在 mr/ 目录中更改了任何内容,您可能需要重新构建您使用的任何 MapReduce 插件,例如使用以下命令:go build -buildmode=plugin ../mrapps/wc.go。
- 这个实验依赖于工作节点共享一个文件系统。当所有工作节点在同一台机器上运行时,这很简单,但如果工作节点在不同的机器上运行,就需要像 GFS 这样的全局文件系统。
- 一个合理的中间文件命名约定是 mr-X-Y,其中 X 是 Map 任务编号,Y 是 Reduce 任务编号。
- Worker 的 Map 任务代码将需要一种方法来以一种在 Reduce 任务期间可以正确读取的方式将中间键/值对存储在文件中。一种可能的方法是使用 Go 的 encoding/json 包。要将键/值对写入 JSON 文件:
- 您的 worker 的 Map 部分可以使用 worker.go 中的 ihash(key) 函数来选择给定键的 Reduce 任务。您可以从 mrsequential.go 中借用一些代码,用于读取 Map 输入文件,对 Map 和 Reduce 之间的中间键/值对进行排序,以及存储 Reduce 输出到文件中。
- 作为 RPC 服务器的主节点将是并发的;不要忘记锁定共享数据。使用 Go 的 race detector,使用 go build -race 和 go run -race。test-mr.sh 中有一个注释,显示了如何在测试中启用 race detector。
- 工作节点有时需要等待,例如,Reduce 不能开始直到最后一个 Map 完成。一种可能性是工作节点定期向主节点请求工作,每个请求之间使用 time.Sleep() 进行休眠。另一种可能性是主节点中相关的 RPC 处理程序具有等待循环,使用 time.Sleep() 或 sync.Cond 进行等待。Go 为每个 RPC 的处理程序在其自己的线程中运行,因此一个处理程序正在等待并不会阻止主节点处理其他 RPC。
- 主节点无法可靠地区分崩溃的工作节点、仍然存活但由于某种原因停滞的工作节点,以及执行但速度太慢以至于无法使用的工作节点。您能做的最好的事情是让主节点等待一段时间,然后放弃并重新将任务分配给另一个工作节点。对于这个实验,让主节点等待十秒钟;之后,主节点应该假设工作节点已经死亡(当然,它可能没有)。
- 为了测试崩溃恢复,您可以使用 mrapps/crash.go 应用程序插件。它在 Map 和 Reduce 函数中随机退出。
- 为了确保在崩溃的情况下没有人观察到部分写入的文件,MapReduce 论文提到了使用临时文件并在完全写入后以原子方式重命名的技巧。您可以使用 ioutil.TempFile 创建临时文件,然后使用 os.Rename 进行原子重命名。
- test-mr.sh 在子目录 mr-tmp 中运行所有进程,所以如果出现问题并且您想查看中间或输出文件,请查看该目录。
实验过程
由于在大二上学期的时候参加过学院里老师关于大数据和人工智能的培训,所以对mapreduce非常熟悉。做过java版的,这次是等于做了go版的。
map阶段
mapf函数返回的是如下图所示的键值对
通过DoMapTask函数来将这些kv键值对保存至mr-tmp-*这些文件中。当map阶段结束后,再由执行reduce函数的worker去读取这些文件里的kv键值对
shuffle阶段
shuffle后的键值对如下
未sorted的kvs如下
分组后的kvs如下
reduce阶段
最后通过reducef函数完成计数
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDaRrImaSl1SttCVsaGrkaCUKk3/Vd2OxauZV/WHV7he5x2dzb63ft+c1K4UutYhHc66fDe99byd2PjwG0ZZfsBVJAFKDQ6s64NdIht76F4cMmlBQTC1InvSNgMU0F8nC9VfhcwP1/Xu/wcfVqZU9n6GTWHgD1Q370kEP4tQshhjP2LYHLTvnXPJhC2LTw+EdDUW0kF5g/tFuLsCfCMKN2qT2OehFIMaLJvK7Sf6jPq2PSzflXNrcF2tz35dlpT3TCWKjXV8yCnZvvehapYjj5uvGVu6LGyqMsSou/gXEaUxB1nXnm9dm10GJtUSMjonPpyphyOCg3NEuwMW4EhYhCJAAR9VXR9OkUM4f0n9mz9TSmW9CkTHD9mRshItcZW4ZJpEuMUQCG0XED/F6Vj2evy2HfPsWTOClkp9jLOFP2/w1+2/rRCX88hnrnYxQMRK/kWMNNrHYonq8Ouphy9Fl2U6hUMWmN+QHRme2VNYE1E43ZaAdVFeCDWHC5CoC6CK1c= vale@ginkgo