6.828 2020 Lab1 MapReduce 实验

1. MapReduce论文

关于这片论文没啥好说的,有地方不懂得可以看课程的视频( B站有 ), MapReduce论文翻译。具体工作过程可参见下图:

2. Lab 大致内容

整个Lab要完成的代码文件有三个:

1. Master.go  即负责分配Map和Reduce任务的主进程

2. RPC.go  由于 Worker 需要通过RPC 与 Master 进程 通信,故在此放置 RPC调用的参数与响应的结构体定义

3. Worker.go  干活的进程, 从Master获取任务并执行

整个程序大致执行流程为

1. 启动 Master 进程,监听 Worker 发过来的 RPC 请求

2. 启动一个或若干Worker进程

3. Worker 进程从 Master 获取 任务,如为 Map / Rduce 任务,则调用对应的Map / Reduce 函数处理,并将结果写到文件中;

如暂时无任务(任务都在执行或完成), 则等待一定时间;如任务都已完成,则退出

对于 Master 进程,需要自己定义管理任务状态的一些数据结构,并且 由于可能有多个Worker产生RPC请求,RPC请求会并发访问或修改这些数据结构,所以一定要加锁Mutex

对于Worker进程,RPC请求可以细分为 1. 请求分配任务  2. 完成分配任务

3. 需注意的细节

1.  整个 Master.go 文件中只有 server函数中才会出现 go xxx(xxx), 而 Worker.go 中也不会出现 go xxx(xxx)。Worker 进程代表着真实分布式系统中的一台主机,我们运行时会启动多个 Worker 进程来模拟。

2. 对每个任务要记录下分配的时间,以便运行超过一定的时间就可以将它分配给另一个Worker

3. 严格注意 RPC 函数的格式问题,可参考下述代码或go的rpc库说明

  • 对于RPC响应函数,最好两个参数均为指针,其中第二个一定要是指针, 返回值一定为 error,调用无错误可以返回 nil。 如不符合上述格式 会出现 "rpc :can't find method xxx" 或者 "rpc :can't find service xxx"
  • 对于RPC调用参数, 参数中的字段名字首字母必须大写,否则写不进去,收到的结果统统为默认值
// 以下代码为Master.go中的RPC响应函数
func (m *Master) Test(args *TestArgs, reply *TestReply) error {
    // 从args中获取调用参数 并将返回结果写回到reply
    // 如 reply.Data = 1
    return nil
}


// 以下代码为RPC.go中的RPC调用参数
type TestArgs struct {
    Data int
}
type TestReply struct {
    Data int
}

// 以下代码为Worker.go中的RPC调用封装函数
func CallTest(...) TestReply {
    // 构造调用参数和返回值
    args := TestArgs{}
    reply := TestReply{}
    // 定义在Worker.go中 调用结果在reply中
    call("Master.Test", &args, &reply)
    return reply
}

4. Worker 中的 Map任务收到的任务信息为要处理的文件名,需要先从文件名中读取所有内容再喂给Map函数(这部分可参考mrsequential.go)。Map函数处理的结果排序后按Key归档成组,并用ihash(key) % reduce_num(要分成的reduce任务数,提前从Master获取)将其分配给不同的reduce任务,并写入到不同的中间文件中。如分成3 个Map任务和4个Reduce任务,中间文件采用

mr-X-Y的文件名格式(X = 0 / 1 / 2, Y = 0 / 1 / 2 /3 ),则对于 Map 0 号任务的 Map函数输出,需要将其分割为 mr-0-0,mr-0-1,mr-0-2, mr-0-4四个文件,注意必须使用 ioutil.TempFile("", "mr-*")先 创建临时文件进行写入,后面再用os.Rename() 修改为最终的文件名,最后调用完成任务的RPC。写入KeyValue 可参考下面

import "encoding/json"  

enc := json.NewEncoder(file)
  for _, kv := ... {
    err := enc.Encode(&kv)

5. Worker 中的Reduce 任务收到要处理的文件名数组。仍以上例说明,对于Reduce 0号任务,会收到 mr-0-0, mr-1-0, mr-2-0, mr-3-0 四个文件名,将收到的文件名对应的文件内容读入汇总到一个数组,再按Key进行排序,遍历数组,将同一个key对应的value数组和Key 交给Reduce函数处理,依次处理并汇总所有输出,采用第四点的临时文件再改名的方法写入,最后调用完成任务RPC。其中从文件读入KeyValue可以参考下面

import "encoding/json"

dec := json.NewDecoder(file)
  for {
    var kv KeyValue
    if err := dec.Decode(&kv); err != nil {
      break
    }
    kva = append(kva, kv)
  }

写入最终数组到文件可以参考下面 (不要自作聪明在最后一行就不加换行符,否则最后的Crash Test过不去,都是从泪中总结出的)

for i := 0; i < len(output); i++ {
	str = output[i].Key + " " + output[i].Value + "\n"
	if _, err := file.WriteString(str); err != nil {
		fmt.Println(err)
	}
}

6. 将KeyValue数组按Key排序可以参考mrsequential.go 或直接使用下面的代码

// 放在 Worker.go 中
// for sorting by key.
type ByKey []KeyValue

// for sorting by 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 }

7. Master 进程没什么要特别注意的,规规矩矩写就可以了

以上就是个人总结的东西和想起来要注意的点,如有疑问,可在下方评论

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值