Lab 1链接:https://pdos.csail.mit.edu/6.824/labs/lab-1.html

Part I: Map/Reduce input and output

Part I需要补充两个关键功能:为map函数分解输出的功能和为reduce函数收集输入的功能,这两个功能对应的函数分别在common_map.go的doMap()函数和common_reduce.go的doRedce()函数。

本人首先梳理程序运行流程,其次补充代码,最后测试结果。

程序运行流程简述如下:

  1. Sequential首先获取Master对象的指针,然后利用函数闭包运行Master.run()。
  2. Master.run()会依次运行mapPhase和reducePhase。
  3. 在mapPhase中,doMap会依次处理每一个输入文件;在reducePhase中,doReduce会依次处理nReduce(论文中为R)个区域。

为实现doMap函数,需要实现以下功能:

  1. 读取inFile。
  2. 通过mapF函数,将inFile转换成key/value的切片形式。
  3. 将上一步得到的结果切割为nReduce个切片,并使用hash函数将结果分配到对应的切片中。
  4. 将上一步得到的结果转换为Json格式,并存储于文件中。
  1. func doMap(
  2. jobName string, // the name of the MapReduce job
  3. mapTask int, // which map task this is
  4. inFile string,
  5. nReduce int, // the number of reduce task that will be run ("R" in the paper)
  6. mapF func(filename string, contents string) []KeyValue,
  7. ) {
  8. // Your code here (Part I).
  9. // read file
  10. data, err := ioutil.ReadFile(inFile)
  11. if err != nil{
  12. log.Fatal("common_map.doMap: fail to read the file. The error is ", err)
  13. }
  14.  
  15. // transfer file
  16. slice := mapF(inFile, string(data))
  17.  
  18. // initialize reduceKv
  19. var reduceKv [][]KeyValue
  20. for i := ; i < nReduce; i++{
  21. temp := make([]KeyValue, )
  22. reduceKv = append(reduceKv, temp)
  23. }
  24.  
  25. // get reduceKv
  26. for _, s := range slice{
  27. index := ihash(s.Key) % nReduce
  28. reduceKv[index] = append(reduceKv[index], s)
  29. }
  30.  
  31. // get intermediate files
  32. for i:= ; i < nReduce; i++{
  33. file, err := os.Create(reduceName(jobName, mapTask, i))
  34. if err != nil{
  35. log.Fatal("common_map.doMap: fail to create the file. The error is ", err)
  36. }
  37. enc := json.NewEncoder(file)
  38. for _, kv := range(reduceKv[i]){
  39. err := enc.Encode(&kv)
  40. if err != nil{
  41. log.Fatal("common_map.doMap: fail to encode. The error is ", err)
  42. }
  43. }
  44. file.Close()
  45. }
  46. }

为实现doReduce函数,需要实现如下功能:

  1. 读取文件中存储的key/value对,并对其进行排序。
  2. 将key值相同的value发送至用户定义的reduceF()中,reduceF()会返回一个新的value值。
  3. 将新的key/value对写入文件。
  1. func doReduce(
  2. jobName string, // the name of the whole MapReduce job
  3. reduceTask int, // which reduce task this is
  4. outFile string, // write the output here
  5. nMap int, // the number of map tasks that were run ("M" in the paper)
  6. reduceF func(key string, values []string) string,
  7. ) {
  8. // Your code here (Part I).
  9.  
  10. // get and decode file
  11. var slices []KeyValue
  12. for i := ; i < nMap; i++{
  13. fileName := reduceName(jobName, i, reduceTask)
  14. file, err := os.Open(fileName)
  15. if err != nil{
  16. log.Fatal("common_reduce.doReduce: fail to open the file. The error is ", err)
  17. }
  18. dec := json.NewDecoder(file)
  19. var kv KeyValue
  20. for{
  21. err := dec.Decode(&kv)
  22. if err != nil{
  23. break
  24. }
  25. slices = append(slices, kv)
  26. }
  27. file.Close()
  28. }
  29.  
  30. sort.Sort(ByKey(slices))
  31.  
  32. //return the reduced value for the key
  33. var reducedValue []string
  34. var outputValue []KeyValue
  35. preKey := slices[].Key
  36. for i, kv := range slices{
  37. if kv.Key != preKey{
  38. outputValue = append(outputValue, KeyValue{preKey, reduceF(preKey, reducedValue)})
  39. reducedValue = make([]string,)
  40. }
  41. reducedValue = append(reducedValue, kv.Value)
  42. preKey = kv.Key
  43.  
  44. if i == (len(slices) - ){
  45. outputValue = append(outputValue, KeyValue{preKey, reduceF(preKey, reducedValue)})
  46. }
  47. }
  48.  
  49. //write and encode file
  50. file, err := os.Create(outFile)
  51. if err != nil{
  52. log.Fatal("common_reduce.doReduce: fail to create the file. The error is ", err)
  53. }
  54. defer file.Close()
  55.  
  56. enc := json.NewEncoder(file)
  57. for _, kv := range outputValue{
  58. err := enc.Encode(&kv)
  59. if err != nil{
  60. log.Fatal("common_reduce.doReduce: fail to encode. The error is ", err)
  61. }
  62. }
  63. }

实验结果如下图所示:

Part II: Single-worker word count

Part II需要统计文档中每个单词出现的数目,需要实现的函数为wc.go中的mapF()和reduceF()函数。

mapF()函数需要将文件拆分为单词,并返回mapreduce.KeyValue的形式。reduceF()函数需要统计每一个Key对应的Value出现的数目,并以string的形式返回。

  1. func mapF(filename string, contents string) []mapreduce.KeyValue {
  2. // Your code here (Part II).
  3. f := func(c rune) bool{
  4. return !unicode.IsLetter(c)
  5. }
  6.  
  7. words := strings.FieldsFunc(contents, f)
  8. var result []mapreduce.KeyValue
  9. for _, word := range words{
  10. result = append(result, mapreduce.KeyValue{word,""})
  11. }
  12. return result
  13. }
  14.  
  15. func reduceF(key string, values []string) string {
  16. // Your code here (Part II).
  17. sum :=
  18. for _, value := range values{
  19. i, err := strconv.Atoi(value)
  20. if err != nil{
  21. log.Fatal("wc.reduceF: fail to convert. The error is ", err)
  22. }
  23. sum += i
  24. }
  25. return strconv.Itoa(sum)
  26. }

实验结果如下图所示:

Part III: Distributing MapReduce tasks&&Part IV: Handling worker failures

Part III和Part IV需要将顺序执行的MapReduce框架并行化并处理worker异常。

本人分别介绍worker和master的执行流程。

worker:RunWorker()首先被调用,该函数创建新Worker并通过call()函数向Master.Register()发送RPC。

master:

  1. 在master.go的Distributed()函数中,master通过startRPCServer()启动RPC服务器,然后利用函数闭包运行run()函数。
  2. 在run()函数中,master会依次运行schedule(mapPhase)和schedule(reducePhase)。
  3. 在schedule(phase)函数中,master会开启新协程运行forwardRegistrations()函数,然后运行Part III和Part IV需要实现的schedule.go中的schedule()函数。
  4. 在介绍worker的执行流程时,本人提到worker会向Master.Register()发送RPC。在Register()函数中,master会将新的worker添加至mr.workers中并告知forwardRegistrations()出现了新的worker。
  5. 在forwardRegistrations()函数中,master通过mr.workers的数目判断是否有新的worker。若有新的worker,master通过channel通知schedule.go的schedule()函数。
  6. 在schedule()函数中,master负责为worker分配task。

为实现master对worker的调度,需要在schedule()函数中实现如下功能。

  1. 通过sync.WaitGroup判断全部任务是否完成。
  2. 通过registerChan判断是否有新的worker。若有,开启新协程为此worker分配任务。
  3. 通过带有缓冲的channel输入任务序号,从channel中取出任务序号并分配给worker。若worker异常,则重新输入任务序号。
  4. 通过call()函数向worker发送RPC。
  1. func schedule(jobName string, mapFiles []string, nReduce int, phase jobPhase, registerChan chan string) {
  2. var ntasks int
  3. var n_other int // number of inputs (for reduce) or outputs (for map)
  4. switch phase {
  5. case mapPhase:
  6. ntasks = len(mapFiles)
  7. n_other = nReduce
  8. case reducePhase:
  9. ntasks = nReduce
  10. n_other = len(mapFiles)
  11. }
  12.  
  13. fmt.Printf("Schedule: %v %v tasks (%d I/Os)\n", ntasks, phase, n_other)
  14.  
  15. // All ntasks tasks have to be scheduled on workers. Once all tasks
  16. // have completed successfully, schedule() should return.
  17. //
  18. // Your code here (Part III, Part IV).
  19. //
  20. var wg sync.WaitGroup
  21. wg.Add(ntasks)
  22.  
  23. taskChan := make(chan int, ntasks)
  24. for i := ; i < ntasks; i++{
  25. taskChan <- i
  26. }
  27.  
  28. go func(){
  29. for{
  30. ch := <- registerChan
  31. go func(address string){
  32. for{
  33. index := <- taskChan
  34. result := call(address, "Worker.DoTask", &DoTaskArgs{jobName, mapFiles[index], phase, index, n_other},new(struct{}))
  35. if result{
  36. wg.Done()
  37. fmt.Printf("Task %v has done\n", index)
  38. }else{
  39. taskChan <- index
  40. }
  41. }
  42. }(ch)
  43. }
  44. }()
  45. wg.Wait()
  46. fmt.Printf("Schedule: %v done\n", phase)
  47. }

Part V: Inverted index generation (optional, does not count in grade)

Part V需要实现倒排索引,需要补充的函数为ii.go中的mapF()和reduceF()函数。

mapF()函数需要对输入文件中的单词进行分割,返回以单词为Key,以文件题目为Value的切片。

reduceF()函数需要对相同Key对应的全部Value去重并排序,统计Value的个数。

  1. func mapF(document string, value string) (res []mapreduce.KeyValue) {
  2. // Your code here (Part V).
  3. f := func(c rune) bool{
  4. return !unicode.IsLetter(c)
  5. }
  6. words := strings.FieldsFunc(value, f)
  7. var result []mapreduce.KeyValue
  8. for _, word := range words{
  9. result = append(result, mapreduce.KeyValue{word, document})
  10. }
  11. return result
  12. }
  13.  
  14. func reduceF(key string, values []string) string {
  15. // Your code here (Part V).
  16. fileName := make(map[string]bool)
  17.  
  18. for _, value := range values{
  19. fileName[value] = true
  20. }
  21.  
  22. num :=
  23. var documents []string
  24. for key := range fileName{
  25. num +=
  26. documents = append(documents, key)
  27. }
  28. sort.Strings(documents)
  29.  
  30. var result string
  31. for i, file := range documents{
  32. if i >= {
  33. result += ","
  34. }
  35. result += file
  36. }
  37. return strconv.Itoa(num) + " " + result
  38. }

实验结果如下图所示:

Running all tests

【MIT-6.824】Lab 1: MapReduce的更多相关文章

  1. 【MIT 6.824 】分布式系统 课程笔记(一)

    Lecture 02 Infrastructure: RPC & threads 一.多线程挑战 共享数据: 使用互斥信号量.或者避免共享 线程间协作: 使用channels 或者 waitg ...

  2. 【MIT 6.824 】分布式系统 课程笔记(二)Lecture 03 : GFS

    Lecture 03 : GFS 一.一致性 1, 弱一致性 可能会读到旧数据 2, 强一致性 读到的数据都是最新的 3, 一致性比较 强一致性对于app的写方便, 但是性能差 弱一致性有良好的性能, ...

  3. MIT 6.824(Spring 2020) Lab1: MapReduce 文档翻译

    首发于公众号:努力学习的阿新 前言 大家好,这里是阿新. MIT 6.824 是麻省理工大学开设的一门关于分布式系统的明星课程,共包含四个配套实验,实验的含金量很高,十分适合作为校招生的项目经历,在文 ...

  4. 【甘道夫】官方网站MapReduce代码注释具体实例

    引言 1.本文不描写叙述MapReduce入门知识,这类知识网上非常多.请自行查阅 2.本文的实例代码来自官网 http://hadoop.apache.org/docs/current/hadoop ...

  5. 【大数据系列】hadoop核心组件-MapReduce

    一.引入 hadoop的分布式计算框架(MapReduce是离线计算框架) 二.MapReduce设计理念 移动计算,而不是移动数据. Input HDFS先进行处理切成数据块(split)   ma ...

  6. MIT 6.824学习笔记1 MapReduce

    本节内容:Lect 1 MapReduce框架的执行过程: master分发任务,把map任务和reduce任务分发下去 map worker读取输入,进行map计算写入本地临时文件 map任务完成通 ...

  7. 【hadoop2.6.0】一句话形容mapreduce

    网上看到的: We want to count all the books in the library. You count up shelf #1, I count up shelf #2. Th ...

  8. MIT 6.824 lab1:mapreduce

    这是 MIT 6.824 课程 lab1 的学习总结,记录我在学习过程中的收获和踩的坑. 我的实验环境是 windows 10,所以对lab的code 做了一些环境上的修改,如果你仅仅对code 感兴 ...

  9. 【hadoop代码笔记】Mapreduce shuffle过程之Map输出过程

    一.概要描述 shuffle是MapReduce的一个核心过程,因此没有在前面的MapReduce作业提交的过程中描述,而是单独拿出来比较详细的描述. 根据官方的流程图示如下: 本篇文章中只是想尝试从 ...

随机推荐

  1. [BZ4923][Lydsy1706月赛]K小值查询

    K小值查询 题面 维护一个长度为n的正整数序列a_1,a_2,...,a_n,支持以下两种操作: 1 k,将序列a从小到大排序,输出a_k的值. 2 k,将所有严格大于k的数a_i减去k. Input ...

  2. luogu P3197 [HNOI2008]越狱

    构造长度为n的串,给定m种颜色,求使得相邻两位的颜色相同的方案数 显然可以看出长度为n的串染m种颜色的总方案数为$m^{n}$ 然后来考虑相邻两位颜色不同的方案 对于第一位,有m种选择 对于剩余的n- ...

  3. spring boot + vue + element-ui全栈开发入门——项目部署

     前言 常用的部署方式有两种: 1.是把生成好的静态页面放到spring boot的static目录下,与打包后的spring boot项目一起发布,当spring boot运行起来后,自然而然就能访 ...

  4. C#-----集合List<T>的常用方法

        雇员实体类 using System; using System.Collections.Generic; using System.Linq; using System.Text; usin ...

  5. Class打包成jar

    Class打包成jar 现在我的文件夹的目录在: C:\Users\linsenq\Desktop\cglibjar 我要把位于这个目录下的所有文件夹以及这个文件夹下的.class文件打成jar包 第 ...

  6. CentOS7上手动部署入门级kubernetes

    前言 翻看了很多的kubernetes的安装教程,也反复做了一些实验,深感教程之复杂,所以决定写一个极简版本的安装教程,目标在于用尽可能少的参数启动服务,并且剖析各组件关系,然后再在此基础上逐步添加参 ...

  7. 对比剖析Swarm Kubernetes Marathon编排引擎

    Docker Native Orchestration 基本结构 Docker Engine 1.12 集成了原生的编排引擎,用以替换了之前独立的Docker Swarm项目.Docker原生集群(S ...

  8. vue 脚手架关于路由的一点理解

    https://router.vuejs.org/zh/ 可以先翻翻文档看看介绍啊,一般我不怎么喜欢看文档,都是直接看人家案例,在回头看文档的,所以学习速度慢很多,希望我以后成为一个爱学习的妹子,比较 ...

  9. 2.获取指定目录及子目录下所有txt文件的个数,并将这些txt文件复制到F盘下任意目录

    package cn.it.text; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import ...

  10. Fizz Buzz 面试题

    在CSDN上看到一篇文章<软件工程师如何笑着活下去>,本来是想看对这个行业的一些评价和信息,不曾检索到关于Fizz Buzz的面试题,上网搜了一下,顿感兴趣.留下此文,以表回忆. java ...