由于最近事情比较多,但是又想刷一下6.824的实验,慕名已久了,于是就看了一下实验手册和大佬分享的代码。在这里写了一点自己的个人感悟。
什么是MapReduce
分布式计算的核心就是有一个主节点,驱动若干个副节点进行计算。MapReduce是一种非常特殊的分布式计算策略,因为所有的副节点都会执行相同的map操作和reduce操作。map操作和reduce操作之间是有一个barrier,做完map才能做reduce。map和reduce操作都可以自己人为定义的。但是需要满足一个条件。map操作需要收集若干条数据,生成若干条key-value对,reduce操作负责合并这些key-value对。对于若干个key-value对,合并成一个key-value对。
RPC连接
master节点和worker节点之间的通信会根据socket协议进行传输。这期间会用到RPC的服务,go语言提供了RPC的服务。这里定义了WorkerArgs和WorkerReply两种信息。客户端执行call
操作。
1 | func call(rpcname string, args interface{}, reply interface{}) bool { |
call操作收取要调用服务端的接口名称(类.函数名)args提供传递的参数,reply提供接受的信息。这是分布式系统能实现的网络基础
Worker
Worker并不需要思考什么问题,它只需要像一个牛马一样完成任务就好了。所以说对于Worker来说,只要做这么一件事。“等待派活-做活”之间循环就好。
等待派活就是向Master(或者叫Coordinator)发送一个请求,Master经过分析后就给Worker派活。派活的形式就如下:
1 | type WorkerReply struct { |
TaskType代表活的形式,下面的两个参数告诉Worker同事的数量。
Worker获得了派活的信息之后,就开始执行Master分发下的活。活的形式只有两种。一种是map,一种是reduce
先说map:
- 获取要分析的文件名
- 进行分析,得到一组key-value对(按照文件数进行分配)
- 把这些key-value对按照Reduce任务的数量大小分到若干个Hash桶中。
- 把Hash桶中的元素记录到中间文件中(文件名是 i-k ,i代表是哪个Map对应做的,应该由第k个Reduce来做)
- 告知Master任务结束
中间文件的记录是按照json文件的形式的,json包会提供Encoder和Decoder进行抽象的文件操作
接着说reduce:
- 从中间文件中获取数据
- 对数据进行排序,排序之后相同key的元素就会在一起
- 获取相同的key组成的元素,做reduce操作
- 写结果
- 告知Master任务结束
再说Master。Master的构建是简化过的,它维护了一个Map池和Reduce池。Map池一共有m个任务,Reduce池一共有n个任务。(m是根据文件数量给定的,n是自己给定的)
当有Worker需要请求任务时:首先判断map是不是已经分配完了,如果分配完了但是还没做完,告知Worker等待,如果没分配完,找到一个没分配的任务,告知其要做这个任务,然后等待任务完成。但是有个问题就是Worker可能自己会退出执行,这就需要Master去“督工”,督工的方式很简单,新建一个匿名线程,然后过10s检查是不是做完了,没做完默认它G了。重新分配给下一个有缘人。
当Map执行完了之后,map任务完成数+1,当完成数=总量的时候,就可以分配Reduce了。这就是简单的MapReduce服务的实现。