MIT 6.824分布式系统课程实验笔记Lab-1


前言

  MIT 6.824 是麻省理工大学的一门研究生课程——Distributed Systems,学习这门课程对于了解分布式系统的构建原理、理解分布式程序的运行、优化分布式程序的运行环境会有很大的帮助。课程内容涵盖:分布式、容错、多副本、一致性等议题,附带了 4 个大的实验 Lab 并配套了相关的测试用例,需要基于 Go 语言完成。Lab 会将课程所讲的知识进行实践、贯通,有助于加深我们的理解和记忆。

  Lab-1 主要是实现MapReduce,编写 mr 文件夹下的 master.go、rpc.go、worker.go 三个文件,完成后在main文件加下执行如下命令,来测试你的程序是否能输出正确结果。

bash ./test-mr.sh

一、MapReduce原理

 通过阅读论文,我们可以知道 MapReduce 的结构:
在这里插入图片描述
MapReduce 执行过程:

  1. 用户程序中的 MapReduce 库首先将输入文件分割为 M 块;然后在集群上启动许多该程序的副本;
  2. 然后在集群上启动许多该程序的副本,其中一个副本是 master,其余的是 worker。总共有 M 个 map 任务和 R 个 reduce 任务,需要 master 挑选一个空闲的 worker 进行分配;
  3. 分配了 map 任务的 worker 读取相应输入 split 的内容。它从输入数据中解析 key-value 对,并将每对传递给用户定义的 map 函数。 map 函数生成的中间 key-value 对缓冲在内存中;
  4. 定期将key-value 对写入本地磁盘,并通过分区函数将其分为 R 个区域。这些key-value 对在本地磁盘上的位置被传回 master,主节点负责将这些位置转发给 reduce 工作节点;
  5. 当一个 reduce worker 被通知位置后,通过 RPC 去读取本地磁盘上的 key-value 对,(并排序);
  6. reduce worker 遍历所有的已排序key-value 对,将唯一的 key 和 value 对集合传递给用户的 reduce 函数,函数输出到最终文件(最多 R 个);
  7. 当所有的 reduce 完成任务后,master 唤醒(通知)用户程序。

二、实验过程

1.编写 worker

  每个 worker 只管接受任务、执行计算、返回结果,不需要管别的,所以可以先从 Worker 写起。
  每当 Worker 通过 RPC 向 Master 发送心跳请求的时候,会收到 3 种可能的回应:

  • heat:Master 的回应心跳,代表现在 Master 没有任务可分配,Worker 暂时空闲;
  • maptask:Master 向自己分配了一个 Map 任务,附带的信息有任务编号 X、输入文件路径、分区数 R;
  • reducetask:Master 向自己分配了一个 Reduce 任务,附带的信息有任务编号 Y、所有的中间文件路径;

① 处理map任务

		// 获取任务指定的文件名
		filename := reply.Task
		
		file, err := os.Open(filename)
		if err != nil {
			log.Fatalf("cannot open %v", filename)
		}
		// 读取文件全部内容
		content, err := ioutil.ReadAll(file)
		if err != nil {
			log.Fatalf("cannot read %v", filename)
		}
		file.Close()

		// 使用map函数处理文件内容,生成 key-value 对切片
		kva := mapf(filename, string(content))

		// 初始化buckets,用于存放分组后的键值对,每个bucket对应一个reduce任务
		buckets := make([][]KeyValue, nReduce)
		for i := 0; i < nReduce; i++ {
			buckets[i] = []KeyValue{}
		}

		// 根据 key-value 对的Key进行哈希并分配到对应的bucket中
		for _, kv := range kva {
			buckets[ihash(kv.Key)%nReduce] = append(buckets[ihash(kv.Key)%nReduce], kv)
		}

		// 对每个bucket,创建并写入到中间文件
		for i, bucket := range buckets {
			oname := "mr-" + strings.Replace(reply.Task[3:(len(reply.Task)-4)], "-", "_", -1) + "-" + strconv.Itoa(i)
			// 创建临时文件以写入数据
			ofile, _ := os.CreateTemp("", oname)
			// 使用JSON编码写入键值对
			enc := json.NewEncoder(ofile)
			for _, kv := range bucket {
				if err := enc.Encode(&kv); err != nil {
					log.Fatalf("cannot write %v", oname)
				}
			}
			// 将临时文件重命名为正式的中间文件名
			os.Rename(ofile.Name(), oname)
		}

② 处理 reduce 任务

		// 若没有可分配的Reduce任务,则等待一秒后重新尝试执行Worker
		if reply2.ReduceNum < 0 {
			time.Sleep(1 * time.Second)
			Worker(mapf, reducef)
			return
		}
		// 减一操作,因为reduceNum是从0开始的,但分配时减1以匹配任务编号
		reply2.ReduceNum -= 1
		reduceNum := reply2.ReduceNum

		// 初始化变量和目录路径,准备读取中间文件
		dir := "./"
		suffix := strconv.Itoa(reduceNum)
		kva := []KeyValue{}          // 用于存储读取的所有键值对
		intermediate := []KeyValue{} // 中间结果存储
		var countA = 0

		// 遍历指定目录下以reduceNum为后缀的文件
		err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			// 只处理文件且文件名以reduceNum为后缀
			if !info.IsDir() && strings.HasSuffix(path, suffix) {
				file, err := os.Open(path)
				defer file.Close() // 确保文件最终关闭
				if err != nil {
					return err
				}
				dec := json.NewDecoder(file) // 创建解码器读取JSON文件

				// 读取并解析文件中的每一个Key-Value
				for {
					var kv KeyValue
					if err := dec.Decode(&kv); err != nil {
						if err == io.EOF { // 文件结束,正常退出循环
							break
						}
						return err // 其他错误则直接返回
					}
					if kv.Key == "A" { // 特殊处理键为"A"的情况(示例特定逻辑)
						countA++
					}
					kva = append(kva, kv) // 添加到键值对列表
				}
			}
			return nil // 继续遍历
		})
		// 将收集到的键值对添加到中间结果列表
		intermediate = append(intermediate, kva...)
		if err != nil {
		}
		// 排序中间结果,便于后续reduce操作
		sort.Sort(ByKey(intermediate))

		// 准备输出文件
		oname := "mr-out-" + strconv.Itoa(reduceNum)
		ofile, _ := os.CreateTemp("", oname)

		// 遍历排序后的键值对,执行reduce操作并写入输出文件
		i := 0
		for i < len(intermediate) {
			j := i + 1
			// 查找相同键的连续区间
			for j < len(intermediate) && intermediate[j].Key == intermediate[i].Key {
				j++
			}
			values := []string{} // 存储相同键的值
			for k := i; k < j; k++ {
				values = append(values, intermediate[k].Value)
			}
			// 应用reduce函数处理相同键的值,得到输出
			output := reducef(intermediate[i].Key, values)

			// 将处理结果写入文件,遵循Reduce输出的格式
			fmt.Fprintf(ofile, "%v %v\n", intermediate[i].Key, output)

			// 移动到下一个不同的键
			i = j
		}
		os.Rename(ofile.Name(), oname)

2.编写 msater

Msater的数据结构,Msater主要的功能是分配map任务和reduce任务

// 协调者,就是 Msater
type Coordinator struct {
	mutex       sync.Mutex
	mapIndex    int
	reduceIndex int
	files       []string
	map1        map[int]bool
	reducePhase bool
	nReduce     int
}

总结

  测试全部通过

在这里插入图片描述

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MIT 6.824 是麻省理工学院的一门高级课程,全称为 "Distributed Systems"(分布式系统),它通常会在计算机科学和工程领域中教授。这门课程深入探讨了分布式计算环境中的核心概念和技术,包括: 1. **分布式计算模型**:学习如何设计和实现分布式系统,如客户端-服务器、对等网络、云计算等架构。 2. **通信协议**:理解像TCP/IP、HTTP、UDP等基础通信协议在分布式系统中的作用以及它们如何确保数据的可靠传输。 3. **一致性模型**:研究不同的一致性级别,如强一致性、最终一致性,以及如何在分布式环境中维护数据一致性。 4. **分布式算法**:涉及分布式任务调度、数据分片、共识算法(如 Paxos、 raft 等)等,这些算法对于分布式系统的高效运作至关重要。 5. **安全性与隐私**:讨论如何保护分布式系统免受恶意攻击,如身份验证、授权、加密等安全措施。 6. **容错与可靠性**:处理节点故障的恢复策略,以及冗余和备份技术的重要性。 7. **大规模分布式系统案例**:可能涵盖云计算平台的设计、大规模数据库系统、分布式文件系统(如Hadoop)或分布式计算框架(如Spark)。 如果你想深入了解这门课程,可以考虑查找相关的课程资料、观看讲座视频,或者阅读经典的分布式系统教材,如《分布式系统:概念与设计》( Distributed Systems: Concepts and Design)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值