Mit6824-lab1笔记

吴嘉豪

Lab1

lab链接:http://nil.csail.mit.edu/6.82...
mapreduce论文链接:http://nil.csail.mit.edu/6.82...

您的任务是实现一个分布式MapReduce,它由两个程序组成,master和worker。只有一个master和一个或多个并行执行的worker。
在一个真实的系统中,工作人员将在一堆不同的机器上运行, 但在这个实验室中,
你将在一台机器上运行所有的工作人员,worker将通过RPC与master进行对话。
每个worker将向master请求一个任务,从一个或多个文件中读取任务的输入,
执行任务,并将任务的输出写入一个或多个文件。
如果一个工人没有在合理的时间内完成他的任务(在这个实验中使用10秒),
master应该注意,并把相同的任务给不同的worker。

master和worker的主要程序在main/mrmaster和main/mrworker.go;不要更改这些文件。
您应该将您的实现放在mr/master、mr/worker、mr/rpc.go。

运行示例:
运行master:

$ rm mr-out*
$ go run mrmaster.go pg-*.txt

运行worker

$ go run mrworker.go wc.so

一些规则:

  1. map阶段应该将中间键划分为nReduce个reduce任务的bucket,其中nReduce是main/mrmaster的参数。
  2. go传递给MakeMaster()。worker运行时应将第x个reduce任务的输出放在mr-out-x文件中。一个mr-out-x文件应该在每个Reduce函数输出中包含一行。这一行应该用Go的“%v %v”格式生成,用键和值调用,在main/mrsequential.go里看一看。
  3. 你可以修改mr/worker.go、mr/master、mr/rpc.go。您可以临时修改其他文件以进行测试,但要确保您的代码与原始版本兼容;我们将用原始版本进行测试。
  4. worker应该将中间Map输出放在当前目录中的文件中,稍后您的worker可以将它们作为Reduce任务的输入读取。main/mrmaster.go希望mr/master去实现Done()方法,当MapReduce任务完成时返回true,mrmaster.go 将退出去。当任务完全完成时,worker应该退出。实现这一点的一个简单方法是使用call()的返回值:如果worker无法联系到master,它可以假设master已经退出,因为作业已经完成,所以worker也可以终止。

可以这样进行简单测试:

$ go build -buildmode=plugin ../mrapps/wc.go
$ go run mrmaster.go pg-*.txt
// 打开一个master进程,输入参数是一系列文件
$ go run mrworker.go wc.so
// 打开以恶worker进程,wc.so是包含map、reduce的链接文件
$ cat mr-out-* | sort | more
// 合并并排序结果文件然后打印

这里是实现一个mapreduce框架,我们先分析一下运行流程:
这里是用动态链接的方式来运行特定的mapreduce函数,整个lab我们不需要写mapreduce函数,只需要开发mapreduce框架。

首先看main/mrmaster.go中做了什么:

func main() {
    log.SetFlags(log.Lshortfile | log.LstdFlags)
    if len(os.Args) < 2 {
        fmt.Fprintf(os.Stderr, "Usage: mrmaster inputfiles...\n")
        os.Exit(1)
    }
    // 启动master进程
    m := mr.MakeMaster(os.Args[1:], 10)
    for m.Done() == false {
        time.Sleep(time.Second)
    }
    time.Sleep(time.Second)
}
func MakeMaster(files []string, nReduce int) *Master {
    m := Master{}
    // your code
    m.server()
    return &m
}
func (m *Master) Done() bool {
    // your code
    return ret
}

两个关键函数,mr.MakeMasterm.Done,这两个函数分别负责创建一个master的实例以及等待master进程工作完成,内部逻辑需要自己补充。进入m.server我们可以发现,这里注册了一个Master的service(lab希望我们通过RPC进行通信)。
然后我们看一下work进程的工作(mr/mrworker.go文件):

func main() {
    log.SetFlags(log.Lshortfile | log.LstdFlags)
    if len(os.Args) != 2 {
        fmt.Fprintf(os.Stderr, "Usage: mrworker xxx.so\n")
        os.Exit(1)
    }
    mapf, reducef := loadPlugin(os.Args[1])
    mr.Worker(mapf, reducef)
}
func Worker(mapf func(string, string) []KeyValue,
    reducef func(string, []string) string) {
}

这里是调用了Worker,并且讲map函数和reduce函数传给了Worker函数。这里信息不多,看完也不知道要做什么。再看一下rpc相关的代码:

func call(rpcname string, args interface{}, reply interface{}) bool {
    //    c, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
    sockname := masterSock()
    c, err := rpc.DialHTTP("unix", sockname)
    if err != nil {
        log.Fatal("dialing:", err)
    }
    defer c.Close()
    err = c.Call(rpcname, args, reply)
    if err == nil {
        return true
    }
    return false
}
func (m *Master) server() {
    err := rpc.Register(m)
    if err != nil {
        log.Fatalf("Rpc register master error=%v\n", err)
    }
    rpc.HandleHTTP()
    // l, e := net.Listen("tcp", ":1234")
    sockname := masterSock()
    os.Remove(sockname)
    l, e := net.Listen("unix", sockname)
    if e != nil {
        log.Fatal("listen error:", e)
    }
    go http.Serve(l, nil)
}

可以发现Master进程是服务端,监听的地址是masterSock的返回值,call函数会像masterSock的地址执行rpc调用。所以我理解这里应该是Master作为服务端,提供rpc接口,然后worker主动调用rpc接口完成工作。
基于以上理解,需要定义这些数据结构:MasterTaskMaster需要提供这些rpc函数(GetTaskTaskDone)。
Worker进程通过调用GetTask获取任务,执行doMap/doReduce函数(自己实现)完成task,并调用TaskDone通知Master该任务已完成。Master需要自己维持任务的执行情况,调用Done函数轮询任务是否全部完成。

大致框架如上,具体实现参考:https://github.com/wujiah251/...

阅读 1.1k

刚刚开始学习写代码的萌新。

4 声望
3 粉丝
0 条评论

刚刚开始学习写代码的萌新。

4 声望
3 粉丝
文章目录
宣传栏