麻省理工——分布式系统 MIT 6.824 lab_1 MapReduce

605 阅读4分钟

MIT6.824 lab_1 MapReduce

这个Lab并不是真的让我们去实现MapReduce的分布式并行框架,而是通过本地开启不同的进程(线程),去模拟集群执行MapReduce程序。

课程相关文献

建议优先使用官方的相关资料,进行完成课程,本文章仅供参考。

源代码

https://gitee.com/eddievim/eddievim_-mit6.824_lab

定义角色

  • Master(调度)

  • Worker(进行具体的Map、Reduce)(默认初始化3个)

流程

  • 通过Master角色接收到用户传进来的文件路径,找到需要进行MapReduce处理的原始文件。
  • 我们这边简单对输入进行任务切分(一个文件对应一个MapTask)。
  • 若输入文件有M个,则开启M个MapTask,Master调度Worker来领取任务并执行Map;
  • Map执行后的结果需要进行排序,并写到磁盘中去;
  • Shuffle :将MapTask的输出,改造成ReduceTask的输入。
  • 直至所有MapTask全部执行完毕后,开启ReduceTask,ReduceTask的任务是到读取MapTask产生的磁盘文件中,抓取出对应分区的Key出来,再将相同的Key与List(value)执行Reduce方法,得出结果后,最终再将结果写出到结果文件中。

初始化工作

定义各种数据结构,及数据的交换格式。

状态的定义

  • -1: TaskWait
  • 0: JobOver
  • 1: MapPhrase
  • 2: ReducePhrase
const TaskWait, JobOver, MapPhrase, ReducePhrase = -1, 0, 1, 2

Master

type Master struct {
	Phrase  int // 用于定义当前阶段
	NReduce int // ReduceTask并行度
	NMap    int // MapTask并行度

	MapTaskQueue    chan MapTask	// 用于存储MapTask的队列,线程安全
	ReduceTaskQueue chan ReduceTask // 用于存储ReduceTask的队列,线程安全

	MapOutFilePaths    [][]string	// MapTask输出的路径
	ReduceOutFilePaths []string		// ReduceTask输出的路径

	mutex sync.Mutex	// 锁,用于防止并发问题的产生
}

MapTask

type MapTask struct {
	No       int	// 编号
	Filepath string
}

ReduceTask

type ReduceTask struct {
	No        int	// 编号
	Filepaths []string
}

用于RPC通信的实体类

type RPCArgs struct {
	// 状态
	// 1: MapPhrase
	// 2: ReducePhrase
	Phrase   int
	OutPaths []string
}

type RPCReply struct {
	// 状态
	// 1: MapPhrase
	// 2: ReducePhrase
	// 3:JobOver
	Phrase   int
	HoldTask bool

	MapTask    MapTask
	ReduceTask ReduceTask
}

任务调度

Worker获取任务

在Worker处于空闲状态(没有任务正在执行时)进行调用。

Worker通过RPC调用Master的方法SendTask,获取任务,如果获取则进行执行,若为reply.HoldTaskfalse,则本次RPC调用,并没有获取到对应的任务。

// send task to worker
func (m *Master) SendTask(args *RPCArgs, reply *RPCReply) error {
	m.mutex.Lock()
	reply.Phrase = m.Phrase
	m.mutex.Unlock()

	reply.HoldTask = true
	switch reply.Phrase {
	case MapPhrase:
		reply.MapTask = <-m.MapTaskQueue
	case ReducePhrase:
		reply.ReduceTask = <-m.ReduceTaskQueue
	case TaskWait:
		reply.HoldTask = false
	default:
		reply.HoldTask = false
	}

	return nil
}

Worker汇报任务

在Worker完成一次任务的时候进行调用。

Worker通过RPC调用MasterReportTask,汇报任务。

  • 若在MapTask阶段,所有任务被处理完毕,则进入shuffle状态。
  • 若在ReduceTask阶段,所有任务被处理完毕后,将状态舍为JobOver,这将会通知Worker,使其结束进程。
// worker report task to master
func (m *Master) ReportTask(args *RPCArgs, reply *RPCReply) error {
	m.mutex.Lock()
	switch m.Phrase {
	case MapPhrase:
		m.MapOutFilePaths = append(m.MapOutFilePaths, args.OutPaths)
		// Map阶段完成了所有任务,进入Reduce阶段
		if len(m.MapOutFilePaths) == m.NMap {
			m.Phrase = TaskWait
			m.shuffle()
			m.Phrase = ReducePhrase
		}
	case ReducePhrase:
		m.ReduceOutFilePaths = append(m.ReduceOutFilePaths, args.OutPaths[0])
        // Reduce阶段结束
        if len(m.ReduceOutFilePaths) == m.NReduce {
            m.Phrase = JobOver
            fmt.Println(m.Phrase)
        }
    }
    
	m.mutex.Unlock()

	return nil
}

阶段的过渡

Shuffle(MapPhrase -> ReducePhrase)

MapTask的输出,将其“改造”为ReduceTask的输入。将MapTask输出的结果,根据不同的组,将同组的数据组合在一起。

// MapPhrase -> ReducePhrase
func (m *Master) shuffle() {
	for i := 0; i < m.NReduce; i++ {
		task := ReduceTask{
			No: i,
		}
		for j := 0; j < m.NMap; j++ {
			task.Filepaths = append(task.Filepaths, m.MapOutFilePaths[j][i])
		}
		m.ReduceTaskQueue <- task
	}
}

Done

用于判断所有任务流程是否结束,只需要判断Phrase是否是JobOver即可。

func (m *Master) Done() bool {
	return m.Phrase == JobOver
}

Worker的工作模式

持续对Master进行轮询,从而持续地获取任务及状态,当到达JobOver阶段时,则结束。

func Worker(mapf func(string, string) []KeyValue,
	reducef func(string, []string) string) {
	for {
		reply := getTask()
		switch reply.Phrase {
		case MapPhrase:
			opaths, ok := MapProcess(mapf, reply)
			if ok {
				fmt.Printf("mapProcess %v OK.\n", reply.MapTask.No)
				call("Master.ReportTask", &RPCArgs{MapPhrase, opaths}, reply)
			} else {
				fmt.Printf("mapProcess %v failed.\n", reply.MapTask.No)
			}
		case ReducePhrase:
			path, ok := ReduceProcess(reducef, reply)
			if ok {
				fmt.Printf("ReduceProcess %v OK.\n", reply.ReduceTask.No)
				call("Master.ReportTask", &RPCArgs{ReducePhrase, []string{path}}, reply)
			} else {
				fmt.Printf("ReduceProcess %v Fail.\n", reply.ReduceTask.No)
			}
		case TaskWait:
			time.Sleep(time.Millisecond)
		case JobOver:
			return
		}
	}
}

其余的操作

剩下的步骤都是Worker获取文件路径后,对文件内容进行读取,然后进行相应操作Mapf/Reducef,然后再输出,持久化到磁盘中,这里不过多阐述,需要的同学可以到我的Gitee代码仓库进行查看。链接🔗

微信公众号