由于最近事情比较多,但是又想刷一下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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func call(rpcname string, args interface{}, reply interface{}) bool {
// c, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
sockname := coordinatorSock()
c, err := rpc.DialHTTP("unix", sockname)
if err != nil {
log.Fatal("dialing:", err)
}
defer c.Close()

err = c.Call(rpcname, args, reply)
if err == nil {
return true
}

fmt.Println(err)
return false
}

call操作收取要调用服务端的接口名称(类.函数名)args提供传递的参数,reply提供接受的信息。这是分布式系统能实现的网络基础

Worker

Worker并不需要思考什么问题,它只需要像一个牛马一样完成任务就好了。所以说对于Worker来说,只要做这么一件事。“等待派活-做活”之间循环就好。

等待派活就是向Master(或者叫Coordinator)发送一个请求,Master经过分析后就给Worker派活。派活的形式就如下:

1
2
3
4
5
6
7
8
9
10
type WorkerReply struct {
Tasktype int // 0: map task, 1: reduce task, 2: waiting, 3: job finished
NMap int // number of map task
NReduce int // number of reduce task

MapTaskNumber int // map task only
Filename string // maptask only

ReduceTaskNumber int // reducetask only
}

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服务的实现。