Lab 1: MapReduce
Lab2:Raft
Lab2A:Leader Election
Lab2B:日志复制
Lab2C :持久化机制 persistence
Lab2D:日志压缩 log compaction
我的代码实现(附带详细代码注释)
直达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任务编号。
流程解读
-
首先构建wc.so:
go build -race -buildmode=plugin ../mrapps/wc.go
-
然后启动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 分配任务。- 如果处于
FinishSchedule
,结束任务,Worker退出;设置 reply.Status 为 FinishTask,通知 Worker 任务结束。 MapSchedule
:如果无任务,Worker静置等待。否则,创建Map任务,设置reply属性;创建一个 Task 对象(描述在执行的任务的文件名、状态(JobType)、开始时间),将其加入 TaskQueue;更新 FileQueue,移除已分配的文件。ReduceSchedule
:无任务,Worker静置等待。否则,创建Reduce任务,设置reply属性;创建 Task 对象,将其加入 TaskQueue;更新 FileQueue,移除已分配的文件索引。
- 如果处于
-
**
Coordinator.WorkerDone
**方法:Worker中的WorkerMap和WorkerReduce中的WorkerDoneCall函数调用该函数,表示完成了自己的任务。- 需要加锁 Lock 防止出现访问
TaskQueue
和FileQueue
,以及调用PrepareForReduce
和PrePareForEnd
方法时的线程不安全问题。 - 如果当前完成的任务的状态是 MapTask,删除 TaskQueue 中对应的文件名表示任务已完成。判断未开始的任务队列 FileQueue 和正在执行的任务队列 TaskQueue 是否都为空,如果都为空则表示 Map 任务已完成,调用 PrepareForReduce 开始进行 Reduce 任务。
- 如果当前完成的任务的状态是 ReduceTask,删除 TaskQueue 中对应的中间文件索引表示任务已完成。判断未开始的任务队列 FileQueue 和正在执行的任务队列 TaskQueue是否都为空,如果都为空则表示 Reduce 任务已完成,调用 PrePareForEnd 表示所有任务已完成。
- 需要加锁 Lock 防止出现访问
-
-
在一个或者多个终端启动
worker
:go 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
方法。
- MapTask:调用**
- **
-
**
Worker.WorkerMap
**方法:每个Worker会读取输入文件,调用Map函数处理内容,并将结果分发到临时文件中,然后重命名这些文件以便进一步处理。入参是map函数和任务task。每个Map任务都需要将分配给自己的文件处理,并创建nReduce个中间文件给Reduce任务使用。- 打开文件,读取文件内容。
- 调用用户定义的Map函数,将文件内容传递进去,获取中间结果 intermediate(键值对切片)。
- 创建存储临时文件和对应的JSON编码器的两个数组,数组大小都是NReduce,每个Reduce任务对应一个临时文件以及写文件的编码器。
- 遍历中间结果中的每个键值对,计算其哈希值并对NReduce取模,写入对应的临时文件,将其分配给对应的Reduce任务。
- 为了确保在崩溃时没有人观察到部分写入的文件,MapReduce论文提到使用临时文件并在完全写入后原子重命名的技巧。遍历临时文件,将其重命名为特定格式的新文件名“mr-<task.idx>-<reduce.idx>”
- 调用 WorkerDoneCall 函数,通知Coordinator这个Map任务已经完成。最终WorkerDone方法会调用 PrepareForReduce 方法将Coordinator的调度状态设置为 ReduceSchedule。
-
**
WorkerDoneCall
**方法:发送一个RPC请求给Coordinator去调用WorkerDone方法,通知其当前Worker已完成任务。通过call
方法调用Coordinator.WorkerDone
方法, -
**
Worker.WorkerReduce
**方法:读取 Map 任务的输出文件,解码中间结果,排序并合并相同键值对,调用 reduce 函数计算结果,将结果写入文件,并通知 Coordinator 任务完成。- 遍历所有 Map 任务的输出文件 mr-*-*,取出自己需要NMap份的 mr-*-Y文件。
- 使用 JSON 解码器解码文件中的 KeyValue 对象,并将其添加到 intermediate 切片中。
- 排序 intermediate 切片,对 intermediate 切片按键排序,方便后续合并相同键值对。
- 合并相邻相同键值对,并存入文件,命名为mr-out-Y。
- 调用 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.go
和 main/mrworker.go
中,不要修改这些文件。你应该将你的实现放在 mr/coordinator.go
、mr/worker.go
和 mr/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 调用。
实验规则:
- Map阶段的分桶:Map阶段应将中间键分成nReduce个桶,每个桶对应一个Reduce任务,nReduce是main/mrcoordinator.go传递给MakeCoordinator()的Reduce任务数量。因此,每个Mapper需要创建nReduce个中间文件供Reduce任务使用。
- Reduce任务的输出:Worker 的实现应该将第X个Reduce任务的输出放在文件mr-out-X中。
- 输出格式:mr-out-X文件应包含每个Reduce函数输出的一行。该行应使用Go的“%v %v”格式生成,并使用键和值调用。请查看main/mrsequential.go中注释为“this is the correct format”的行。如果实现的格式与此格式相差太大,测试脚本将失败。
- 代码修改限制:您可以修改mr/worker.go、mr/coordinator.go和mr/rpc.go。可以暂时修改其他文件进行测试,但请确保代码在原始版本上也能运行;我们将使用原始版本进行测试。
- 中间Map输出:Worker应将中间Map输出放在当前目录中,之后您的Worker可以将其作为Reduce任务的输入读取。
- 完成标志:main/mrcoordinator.go期望mr/coordinator.go实现一个Done()方法,该方法在MapReduce作业完全完成时返回true;此时,mrcoordinator.go将退出。
- 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 -race
和go 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 RPC:Go 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
相关链接