MIT6.824-2022 分布式系统课程实验笔记 Lab 1 MapReduce--xunznux

Lab 1: MapReduce

Lab2:Raft

实验二Raft链接

Lab2A:Leader Election

lab2A链接

Lab2B:日志复制

lab2B链接

Lab2C :持久化机制 persistence

lab2C链接

Lab2D:日志压缩 log compaction

lab2D链接

我的代码实现(附带详细代码注释)

直达gitee链接
https://gitee.com/zhou-xiujun/xun-go/tree/master/com/xun/Mit-6.824-xun

介绍

在本实验中,将构建一个MapReduce系统。实现一个 worker 工作进程,该进程调用应用程序的Map和Reduce函数,并负责读写文件;还实现一个 coordinator 协调器进程,该进程分配任务给工作进程并处理失败的工作进程。将构建的系统类似于MapReduce论文中描述的系统。(注意:本实验使用“coordinator”而不是论文中的“master”。)

流程

基本结构

// RPC:
// 入参
type ExampleArgs struct {
   X int
}

// 响应
type ExampleReply struct {
   Y int
}

// RpcTask 在此处添加RPC定义
type RpcTask struct {
   Filename string  // 传入的保存上一步结果的文件名
   Status   JobType // 当前任务所处状态 MapTask 或 ReduceTask...
   Idx      int     // 当前任务的编码
   NReduce  int     // reduce 的数量
   NMap     int     // map 的数量
}

// 编写一个 unique-ish UNIX-domain 套接字名称
// 在 /var/tmp 中, 用于 coordinator。
// 不能使用当前目录,因为
// Athena AFS不支持UNIX域套接字
func coordinatorSock() string {
   s := "/var/tmp/824-mr-"
   s += strconv.Itoa(os.Getuid())
   return s
}
// JobType 定义一个新的类型 JobType,它是一个 int 类型
type JobType int

// iota 是 Go 语言中的一个常量生成器,可以在每个 const 声明块中自动递增
// 这些常量可以用来表示不同类型的任务状态
const (
	MapTask     JobType = iota // 0
	ReduceTask                 // 1
	WaitingTask                // 2
	FinishTask                 // 3
)

// Task 结构体包含文件存放位置,以及任务的类别
type Task struct {
	Filename  string
	Status    JobType
	StartTime time.Time
}

type Schedule int

const (
	MapSchedule    Schedule = iota // 0
	ReduceSchedule                 // 1
	FinishSchedule                 // 2
)

// ByKey 用于通过key排序
type ByKey []KeyValue

// 用于通过key排序

func (a ByKey) Len() int { return len(a) }
func (a ByKey) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
func (a ByKey) Less(i, j int) bool {
	return a[i].Key < a[j].Key
}

方法调用

mrcoordinator.go -> main() -> MakeCoordinator() -> coordinator.server() -> http.Serve()
																		-> heartbeat()
 												-> for Done() == false
mrworker.go -> mr.Worker(mapF, reduceF) -> 轮询获取任务 for GetMasterTask()
						case MapTask -> WorkerMap(MapF, task) -> mapF()
															  -> WorkerDoneCall(task) -> WorkerDone()
						case ReduceTask -> WorkerReduce(ReduceF, task) -> reduceF()
																	 -> WorkerDoneCall(task) -> WorkerDone()
						case WaitingTask -> time.Sleep(3 * time.Second)
						case FinishTask -> return

WorkerDone() -> case MapTask -> 所有Map任务完成 PrepareForReduce()
							 -> nil
				case ReduceTask -> 所有Reduce任务完成 PrePareForEnd()
								-> nil

PrepareForReduce() -> 所有的Map任务已完成,准备Reduce
				   -> 准备FileQueue,存储的是NReduce个数字,从0->(NReduce-1),作为Reduce任务的idx。Map阶段的FileQueue就是文件名列表。

总结:一个Worker相当于一个工作节点,可以运行多个Map和Reduce任务。在Map阶段,循环获取任务,此时任务的idx是文件对应的下标,保存的中间文件名是 <mr-文件下标(Map任务索引)-要分发的Reduce任务下标>。在Reduce阶段,遍历所有"文件下标/Map任务索引"取出每个Map任务对应的Reduce任务需要处理的中间文件,mr-X-Y,其中X是Map任务编号,Y是Reduce任务编号。

流程解读

  1. 首先构建wc.so:go build -race -buildmode=plugin ../mrapps/wc.go

  2. 然后启动mrcoordinator:go run -race mrcoordinator.go pg-*.txt

    • 功能:启动 MapReduce 协调器进程,并监控其任务状态,直到所有任务完成。

    • 获取输入文件列表,MakeCoordinator创建协调器实例,并通过循环Done和休眠机制等待任务完成。

    • MakeCoordinator功能:创建filesMap映射(fileName->idx)用于Map任务,创建 Coordinator 实例 c。将协调器的调度状态设为 MapSchedule,文件映射属性 FilesMap 设为 filesMap,任务队列 FileQueue 设为输入的文件列表准备开始调度 Map 任务。Map任务大小NMap设为文件个数。最后启动实例的server方法。

    • server功能:注册 master 以处理 RPC 请求,并将 RPC 请求绑定到 HTTP 协议。获取获取 Unix 套接字并监听。对http.Serve方法启动一个新的 Goroutine 以处理传入的 HTTP 请求。对heartbeat方法启动一个新的 Goroutine 检测任务超时机制。

    • **Coordinator.GetTask**方法:对 Coordinator 加锁,根据 coordinator 实例的状态进行不同的调度,为 Worker 分配任务。

      1. 如果处于 FinishSchedule,结束任务,Worker退出;设置 reply.Status 为 FinishTask,通知 Worker 任务结束。
      2. MapSchedule:如果无任务,Worker静置等待。否则,创建Map任务,设置reply属性;创建一个 Task 对象(描述在执行的任务的文件名、状态(JobType)、开始时间),将其加入 TaskQueue;更新 FileQueue,移除已分配的文件。
      3. ReduceSchedule:无任务,Worker静置等待。否则,创建Reduce任务,设置reply属性;创建 Task 对象,将其加入 TaskQueue;更新 FileQueue,移除已分配的文件索引。
    • **Coordinator.WorkerDone**方法:Worker中的WorkerMap和WorkerReduce中的WorkerDoneCall函数调用该函数,表示完成了自己的任务。

      • 需要加锁 Lock 防止出现访问TaskQueueFileQueue,以及调用PrepareForReducePrePareForEnd方法时的线程不安全问题。
      • 如果当前完成的任务的状态是 MapTask,删除 TaskQueue 中对应的文件名表示任务已完成。判断未开始的任务队列 FileQueue 和正在执行的任务队列 TaskQueue 是否都为空,如果都为空则表示 Map 任务已完成,调用 PrepareForReduce 开始进行 Reduce 任务。
      • 如果当前完成的任务的状态是 ReduceTask,删除 TaskQueue 中对应的中间文件索引表示任务已完成。判断未开始的任务队列 FileQueue 和正在执行的任务队列 TaskQueue是否都为空,如果都为空则表示 Reduce 任务已完成,调用 PrePareForEnd 表示所有任务已完成。
  3. 在一个或者多个终端启动workergo run -race mrworker.go wc.so

    • mrworker中调用 loadPlugin 函数加载wc.go中的Map和Reduce函数。

    • 调用 Worker 函数,传入 Map 和 Reduce 函数,启动工作进程。

    • **Worker**方法:工作进程的核心函数。入参是Map和Reduce函数。轮询从Master(Coordinator)获取任务GetMasterTask

      • **Worker.GetMasterTask**功能:通过call函数 rpc 调用Coordinator.GetTask获取任务。
      • 获取到任务后:判断任务的状态(JobType)是MapTask、ReduceTask、WaitingTask、FinishTask中的哪一个。
        • MapTask:调用**WorkerMap**方法,入参是Map方法和任务task。
        • ReduceTask:调用**WorkerReduce**,入参是Reduce方法和任务task。
        • FinishTask:结束Worker方法。
    • **Worker.WorkerMap**方法:每个Worker会读取输入文件,调用Map函数处理内容,并将结果分发到临时文件中,然后重命名这些文件以便进一步处理。入参是map函数和任务task。每个Map任务都需要将分配给自己的文件处理,并创建nReduce个中间文件给Reduce任务使用。

      1. 打开文件,读取文件内容。
      2. 调用用户定义的Map函数,将文件内容传递进去,获取中间结果 intermediate(键值对切片)。
      3. 创建存储临时文件和对应的JSON编码器的两个数组,数组大小都是NReduce,每个Reduce任务对应一个临时文件以及写文件的编码器。
      4. 遍历中间结果中的每个键值对,计算其哈希值并对NReduce取模,写入对应的临时文件,将其分配给对应的Reduce任务。
      5. 为了确保在崩溃时没有人观察到部分写入的文件,MapReduce论文提到使用临时文件并在完全写入后原子重命名的技巧。遍历临时文件,将其重命名为特定格式的新文件名“mr-<task.idx>-<reduce.idx>”
      6. 调用 WorkerDoneCall 函数,通知Coordinator这个Map任务已经完成。最终WorkerDone方法会调用 PrepareForReduce 方法将Coordinator的调度状态设置为 ReduceSchedule。
    • **WorkerDoneCall**方法:发送一个RPC请求给Coordinator去调用WorkerDone方法,通知其当前Worker已完成任务。通过call方法调用Coordinator.WorkerDone方法,

    • **Worker.WorkerReduce**方法:读取 Map 任务的输出文件,解码中间结果,排序并合并相同键值对,调用 reduce 函数计算结果,将结果写入文件,并通知 Coordinator 任务完成。

      1. 遍历所有 Map 任务的输出文件 mr-*-*,取出自己需要NMap份的 mr-*-Y文件。
      2. 使用 JSON 解码器解码文件中的 KeyValue 对象,并将其添加到 intermediate 切片中。
      3. 排序 intermediate 切片,对 intermediate 切片按键排序,方便后续合并相同键值对。
      4. 合并相邻相同键值对,并存入文件,命名为mr-out-Y。
      5. 调用 WorkerDoneCall 函数,通知Coordinator这个Reduce任务已经完成。所有任务完成后,会调用 PrePareForEnd 方法将 Coordinator 的调度状态设置为 FinishSchedule 状态,代表所有任务已完成,可以终止退出。

示例过程

需要设置Go编程环境来完成实验。

使用 git 获取初始实验软件。

$ git clone git://g.csail.mit.edu/6.824-golabs-2022 6.824
$ cd 6.824
$ ls
Makefile src

提供了一个简单的顺序MapReduce实现,位于 src/main/mrsequential.go。这个实现一次运行一个map和reduce,在一个单独的进程中完成。还提供了几个MapReduce应用程序:位于 mrapps/wc.go 的词频统计程序和位于 mrapps/indexer.go 的文本索引器。可以按以下步骤顺序运行词频统计程序:

$ 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 选项启用了 Go 的竞争检测器(race detector)。建议在开发和测试 6.824 实验代码时使用竞争检测器。虽然在评分时不会使用竞争检测器,但如果代码存在竞争条件,即使没有竞争检测器,在测试中也有很大可能会失败。)

  • -buildmode=plugin: 这个标志指定构建模式为插件。插件是一个 Go 语言中的特殊包,允许在运行时进行动态加载。插件可以实现扩展功能,可以在应用程序运行时加载和调用。
  • go build -race -buildmode=plugin ../mrapps/wc.go 这个命令会生成一个 .so 文件(共享对象文件),可以在运行时被 Go 程序加载和使用。例如,如果 wc.go 文件中包含一个 func Map() 函数,编译后的插件可以在运行时被加载并调用这个函数。
  • more 是 Unix 和 Linux 系统中的一个命令,用于逐页查看文件内容。当文件内容很长时,more 命令会分屏显示,方便用户逐页查看,而不是一次性显示全部内容。

mrsequential.go 的输出文件是 mr-out-0,输入文件则是命名为 pg-xxx.txt 的文本文件。

可以看看 mrsequential.go 中的代码和 mrapps/wc.go 文件,以了解 MapReduce 应用程序。

具体任务

任务是实现一个分布式 MapReduce 系统,包括两个程序:协调器(coordinator)和工作者(worker)。系统将有一个协调器进程和一个或多个并行执行的工作者进程。在现实系统中,工作者会运行在许多不同的机器上,但在这个实验中,只在一台机器上运行它们。工作者将通过 RPC 与协调器通信。每个工作者进程会向协调器请求任务,从一个或多个文件中读取任务的输入,执行任务,并将任务的输出写入一个或多个文件。协调器应注意如果某个工作者在合理时间内(对于这个实验,使用十秒)未完成其任务,应将同一任务交给另一个工作者。

已经提供了一些起始代码。协调器和工作者的“main”程序分别在 main/mrcoordinator.gomain/mrworker.go 中,不要修改这些文件。你应该将你的实现放在 mr/coordinator.gomr/worker.gomr/rpc.go 中。

以下是如何在 word-count MapReduce 应用程序上运行你的代码。首先,确保 word-count 插件是新构建的:

$ go build -race -buildmode=plugin ../mrapps/wc.go

main目录下运行 coordinator:

$ rm mr-out*
$ go run -race mrcoordinator.go pg-*.txt

mrcoordinator.go 中的 pg-*.txt 参数是输入文件;每个文件对应一个“分片”,是一个 Map 任务的输入。-race 标志会使用 Go 的竞争检测器运行 go 程序。

在一个或多个其他窗口中,运行一些工作者:

$ go run -race mrworker.go wc.so

coordinator窗口成功输出如下:

[xun@xun main]go run -race mrcoordinator.go pg-*.txt
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
[xun@xun main]$ ls
diskvd.go  mr-0-3  mr-0-9  mr-1-5  mr-2-1  mr-2-7  mr-3-3  mr-3-9  mr-4-5  mr-5-1  mr-5-7  mr-6-3  mr-6-9  mr-7-5         mrcoordinator.go  mr-out-5      mrsequential.go      pg-dorian_gray.txt       pg-tom_sawyer.txt
lockc.go   mr-0-4  mr-1-0  mr-1-6  mr-2-2  mr-2-8  mr-3-4  mr-4-0  mr-4-6  mr-5-2  mr-5-8  mr-6-4  mr-7-0  mr-7-6         mr-out-0          mr-out-6      mrworker             pg-frankenstein.txt      test-mr-many.sh
lockd.go   mr-0-5  mr-1-1  mr-1-7  mr-2-3  mr-2-9  mr-3-5  mr-4-1  mr-4-7  mr-5-3  mr-5-9  mr-6-5  mr-7-1  mr-7-7         mr-out-1          mr-out-7      mrworker.go          pg-grimm.txt             test-mr.sh
mr-0-0     mr-0-6  mr-1-2  mr-1-8  mr-2-4  mr-3-0  mr-3-6  mr-4-2  mr-4-8  mr-5-4  mr-6-0  mr-6-6  mr-7-2  mr-7-8         mr-out-2          mr-out-8      pbc.go               pg-huckleberry_finn.txt  viewd.go
mr-0-1     mr-0-7  mr-1-3  mr-1-9  mr-2-5  mr-3-1  mr-3-7  mr-4-3  mr-4-9  mr-5-5  mr-6-1  mr-6-7  mr-7-3  mr-7-9         mr-out-3          mr-out-9      pbd.go               pg-metamorphosis.txt     wc.so
mr-0-2     mr-0-8  mr-1-4  mr-2-0  mr-2-6  mr-3-2  mr-3-8  mr-4-4  mr-5-0  mr-5-6  mr-6-2  mr-6-8  mr-7-4  mrcoordinator  mr-out-4          mrsequential  pg-being_ernest.txt  pg-sherlock_holmes.txt

安装使用bash 5.0,否则无法识别 wait -n命令

# 1.下载安装包
wget http://ftp.gnu.org/gnu/bash/bash-5.0.tar.gz
# 2.解压安装依赖
tar -zxvf bash-5.0.tar.gz -C  /usr/local/
 
yum -y install gcc make
# 3.进入目录
cd /usr/local/bash-5.0
# 4.编译安装
./configure && make && make install
# 5.创建软连接
mv /bin/bash /bin/bash.bak
ln -s /usr/local/bin/bash /bin/bash
# 6.查看版本信息
bash  --version

当您完成后,测试脚本输出应如下所示:

[root@xun main]$ sh test-mr.sh 
*** Starting wc test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- wc test: PASS
*** Starting indexer test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- indexer test: PASS
*** Starting map parallelism test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- map parallelism test: PASS
*** Starting reduce parallelism test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- reduce parallelism test: PASS
*** Starting job count test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- job count test: PASS
*** Starting early exit test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- early exit test: PASS
*** Starting crash test.
Master Start!
../pg-sherlock_holmes.txt Task Timeout,Restart it!
../pg-dorian_gray.txt Task Timeout,Restart it!
../pg-dorian_gray.txt Task Timeout,Restart it!
MapTask Finish!	Master will go to Reduce!
2 Task Timeout,Restart it!
9 Task Timeout,Restart it!
2 Task Timeout,Restart it!
9 Task Timeout,Restart it!
2 Task Timeout,Restart it!
All Tasks Finish!	Master will stop work!
--- crash test: PASS
*** PASSED ALL TESTS

我的错误输出(通过安装bash 5.0解决):

[root@xun main]$ sh test-mr.sh 
*** Starting wc test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- wc test: PASS
*** Starting indexer test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- indexer test: PASS
*** Starting map parallelism test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- map parallelism test: PASS
*** Starting reduce parallelism test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- reduce parallelism test: PASS
*** Starting job count test.
Master Start!
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
--- job count test: PASS
*** Starting early exit test.
Master Start!
test-mr.sh: line 246: wait: -n: invalid option
wait: usage: wait [id]
sort: cannot read: mr-out*: No such file or directory
MapTask Finish!	Master will go to Reduce!
All Tasks Finish!	Master will stop work!
cmp: EOF on mr-wc-all-initial
--- output changed after first worker exited
--- early exit test: FAIL
*** Starting crash test.
Master Start!
../pg-sherlock_holmes.txt Task Timeout,Restart it!
MapTask Finish!	Master will go to Reduce!
2 Task Timeout,Restart it!
2 Task Timeout,Restart it!
All Tasks Finish!	Master will stop work!
--- crash test: PASS
*** FAILED SOME TESTS

可能还会看到一些来自 Go RPC 包的错误消息,看起来像这样:

2019/12/16 13:27:09 rpc.Register: method "Done" has 1 input parameters; needs exactly three

请忽略这些消息;将协调器注册为 RPC 服务器会检查其所有方法是否适用于 RPC(是否具有 3 个输入);我们知道 Done 方法不会通过 RPC 调用。

实验规则:

  1. Map阶段的分桶:Map阶段应将中间键分成nReduce个桶,每个桶对应一个Reduce任务,nReduce是main/mrcoordinator.go传递给MakeCoordinator()的Reduce任务数量。因此,每个Mapper需要创建nReduce个中间文件供Reduce任务使用。
  2. Reduce任务的输出:Worker 的实现应该将第X个Reduce任务的输出放在文件mr-out-X中。
  3. 输出格式:mr-out-X文件应包含每个Reduce函数输出的一行。该行应使用Go的“%v %v”格式生成,并使用键和值调用。请查看main/mrsequential.go中注释为“this is the correct format”的行。如果实现的格式与此格式相差太大,测试脚本将失败。
  4. 代码修改限制:您可以修改mr/worker.go、mr/coordinator.go和mr/rpc.go。可以暂时修改其他文件进行测试,但请确保代码在原始版本上也能运行;我们将使用原始版本进行测试。
  5. 中间Map输出:Worker应将中间Map输出放在当前目录中,之后您的Worker可以将其作为Reduce任务的输入读取。
  6. 完成标志:main/mrcoordinator.go期望mr/coordinator.go实现一个Done()方法,该方法在MapReduce作业完全完成时返回true;此时,mrcoordinator.go将退出。
  7. Worker进程退出:作业完全完成后,Worker进程应退出。一个简单的实现方法是使用call()的返回值:如果Worker未能联系到协调器,可以假定协调器已退出,因为作业已完成,因此Worker也可以终止。根据您的设计,您可能还会发现使用一个“请退出”的伪任务让协调器分配给Workers有帮助。

实验提示:

  • 参考指导页面:参考指导页面提供了一些开发和调试的技巧。

  • 开始步骤:一个好的开始方法是修改mr/worker.go中的Worker()函数,向协调器发送一个RPC请求,请求获取一个任务。然后修改协调器,响应一个尚未启动的Map任务的文件名。接着,修改Worker来读取该文件并调用应用的Map函数,就像在mrsequential.go中一样。

  • 加载应用程序函数:应用程序的Map和Reduce函数是在运行时使用Go插件包从以.so结尾的文件中加载的。

  • 重新构建插件:如果您更改了mr/目录中的任何内容,可能需要重新构建任何您使用的MapReduce插件,例如使用以下命令:go build -race -buildmode=plugin ../mrapps/wc.go

  • 共享文件系统:此实验依赖于Workers共享文件系统。当所有Workers都在同一台机器上运行时,这很简单,但如果Workers运行在不同的机器上,则需要一个全局文件系统,如GFS。

  • 中间文件命名规范:中间文件的一个合理命名约定是mr-X-Y,其中X是Map任务编号,Y是Reduce任务编号。

  • Map任务代码:Map任务代码需要一种方法来以正确的方式将中间键/值对存储到文件中,以便在Reduce任务期间正确读取。一种可能的方法是使用Go的encoding/json包。将键/值对以JSON格式写入打开的文件:

  • **工作器任务分配:**使用ihash函数:worker的map部分可以使用worker.go中的ihash(key)函数为给定的键选择reduce任务。

  • 代码借用:可以借用mrsequential.go中的一些代码,用于读取Map输入文件、在Map和Reduce之间排序中间键值对以及存储Reduce的输出到文件中。

  • 协调器并发:作为RPC服务器,协调器将是并发的;不要忘记锁定共享数据。

  • 使用Go的竞态检测器:使用go build -racego run -race来构建和运行代码。test-mr.sh默认使用竞态检测器运行测试。

  • 工作器等待:有时工作器需要等待,例如,reduce任务不能在最后一个map任务完成之前开始。一种可能性是工作器周期性地向协调器请求工作,在每次请求之间使用time.Sleep()休眠。另一种可能性是协调器中的相关RPC处理程序使用循环等待,使用time.Sleep()sync.Cond。Go为每个RPC运行处理程序分配一个线程,所以一个处理程序的等待不会阻止协调器处理其他RPC。

  • 任务重新分配:协调器无法可靠地区分崩溃的工作器、因某种原因停止但还存活着的工作器和执行但速度过慢的工作器。最好的办法是让协调器等待一段时间,然后放弃并将任务重新分配给另一个工作器。对于这个实验,让协调器等待十秒;之后协调器应假定工作器已经死亡(当然,也可能没有死亡)。

  • 实现备用任务:如果选择实现备用任务(Backup Task)(3.6节),请注意我们测试代码时不会安排额外的任务,除非工作器崩溃。备用任务只应在相对较长的时间(如10秒)之后安排

  • 崩溃恢复测试:可以使用mrapps/crash.go应用插件进行测试。它会随机在Map和Reduce函数中退出。

  • 使用临时文件为了确保在崩溃时没有人观察到部分写入的文件,MapReduce论文提到使用临时文件并在完全写入后原子重命名的技巧。可以使用ioutil.TempFile创建临时文件,并使用os.Rename进行原子重命名。

  • 查看中间文件test-mr.sh在子目录mr-tmp中运行所有进程,所以如果出问题并且想查看中间或输出文件,可以在那里查看。可以临时修改test-mr.sh以在失败的测试后退出,这样脚本就不会继续测试(并覆盖输出文件)。

  • 多次运行测试test-mr-many.sh提供了一个基本脚本,用于带超时时间地运行test-mr.sh(这也是我们将测试代码的方式)。它接收一个参数,表示运行测试的次数。不应并行运行多个**test-mr.sh**实例,因为协调器会重用同一个套接字,导致冲突。

  • Go RPCGo RPC仅发送名称以大写字母开头的结构字段。子结构也必须有大写字段名

  • 指针传递:传递指向回复结构的指针给RPC系统时,*reply指向的对象应零分配。RPC调用的代码应始终如下所示:

    reply := SomeType{}  
    call(..., &reply)
    

    在进行RPC调用之前,不要预先设置**reply中的任何字段**。如果不遵循此要求,当您将reply字段预初始化为该数据类型的非默认值,而RPC执行的服务器将该reply字段设置为默认值时,会出现问题;您会发现写操作似乎没有生效,并且在调用方一侧,非默认值依然存在。

我的代码实现(附带详细代码注释)

直达gitee链接
https://gitee.com/zhou-xiujun/xun-go/tree/master/com/xun/Mit-6.824-xun

相关链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值