6.824 Lab01 MapReduce

一些废话:学6.824是因为实在是喜欢存储,不想再做监控运维之类无聊的工作,想真正成长为一个专业的分布式存储工程师,也是看网上说6.824是分布式学习很好的入门课程,于是就利用空余时间开始学习这门课程。
这门课程的实验都是推荐用golang实现的。其实用其他语言也是可以的,不过我觉得作为初学者,我还是用推荐语言去写,后期遇到问题,也方便查询参考资料。而且语言不应该是障碍,于是,在完成实验中,我也用的是golang。一些go语言使用中,初学者会遇到的坑,也一一记录下来。

背景

首先了解一下MapReduce的背景,这个就直接搬课程内容啦
Google需要在TB级别的数据上做大量计算,比如,为所有网页创建索引,分析整个互联网的链接路径并得出最重要或最权威的网页。Google需要一种框架,可以让它的工程师能够进行任意的数据分析,例如排序,网络索引器,链接分析器以及任何的运算。工程师只需要实现应用程序的核心,就能将应用程序运行在数千台计算机上,而不用考虑如何将运算工作分发到数千台计算机,如何组织这些计算机,如何移动数据,如何处理故障等等这些细节。

原理

图1 MapReduce 示意

重点理解:纯函数式
Map函数使用一个key和一个value作为参数。入参中,key是输入文件的名字,通常会被忽略,因为我们不太关心文件名是什么,value是输入文件的内容content。
Reduce函数的入参是某个特定key的所有实例,Reduce函数的入参是某个特定key的所有实例(Map输出中的key-value对中,出现了一次特定的key就可以算作一个实例)。所以Reduce函数也是使用一个key和一个value作为参数,其中value是一个数组,里面每一个元素是Map函数输出的key的一个实例的value。


图2 MapReduce原理

master给workers分配任务并记录进度

  1. master将map任务给workers知道所有map任务完成, 将中间数据写入磁盘,按哈希值将分割的输出映射到每个Reduce任务的一个文件中。
  2. map任务结束后,master来处理reduce任务,每个reduce任务获得map任务的中间输出,每个Reduce任务在GFS上写一个单独的输出文件。

实验准备

1.安装go

【参考】https://www.jianshu.com/p/49588471af18

$ wget https://golang.google.cn/dl/go1.15.6.linux-amd64.tar.gz
$ tar xz -C /usr/local -xzf go1.15.6.linux-amd64.tar.gz
创建工作目录
$ mkdir -p ~/code/go
$ vim ~/.bashrc
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/root/code/go
$ source ~/.bashrc
测试环境是否生效
$ go env

2.下载实验参考代码

6.824提供了完成实验必要的一些通讯相关的代码框架,可以帮助我们完成实验

$ git clone git://g.csail.mit.edu/6.824-golabs-2020 6.824
$ cd 6.824
$ ls
Makefile src
$ cd ~/6.824
$ cd src/main
$ go build -buildmode=plugin ../mrapps/wc.go
$ rm mr-out*
$ go run mrsequential.go wc.so pg*.txt

mrsequential.go是非分布式实现的单词计数任务,可以用来与分布式实现的mapreduce进行对比
课程说明提到开发一个分布式的mapreduce系统,由两个程序组成:master和worker。一个master进程,多个并行的worker进程。workers通过RPC和mater交互。每个worker进程问master要一个task,从这个task中的input读取一个或多个files,执行这个task,将task的output写入一个或多个文件,master需要注意一个worker是否在一段时间内(10s)完成了task,如果没完成,则把这个task给其他的worker。
课程设计已经给了一段代码帮助我们开始,The "main" routines for the master and worker are in main/mrmaster.go and main/mrworker.go,不要改这些files。应该把要实现的应用放在mr/master.go, mr/worker.go, and mr/rpc.go。
编写完mr里的代码之后,如何run your code在word-count app上?

1.首先确认wc.so已经创建
$ go build -buildmode=plugin ../mrapps/wc.go
2.在main目录,run master
$ rm mr-out*
$ go run mrmaster.go pg-*.txt
pg-*.txt参数是mrmaster.go的输入文件,每个文件对应一个“split”,是一个map task的输入
3.在一个或多个窗口执行workers
$ go run mrworker.go wc.so
4.通过a test script in main/test-mr.sh检查wc和indexer
测试脚本检查输出是否正确,是否并行执行,当crash的worker恢复后tasks是否可以恢复
$ cd ~/6.824/src/main
$ sh test-mr.sh
*** Starting wc test.

开始实验

虽然网上好多大神的经验贴都说lab01是洒洒水的,很好实现,但学习这个事就是小马过河,作为golang小白,我觉得是不太容易的,我做了整整1周。期间因为语言的不熟悉带来一些磕磕绊绊,也记录下来,帮助自己更快学会这门编程语言,为后面的实验打基础。

Lab01的任务是实现一个简单的mapreduce system,我根据6.824给的代码,自己根据学习进度做了一些简单的调整。

1.mrsequential.go是一个例子,告诉我们,这个实验不通过分布式,正常编码是怎么样的

1.1关于loadPlugin

实验指导上在go run之前先执行go build -buildmode=plugin ../mrapps/wc.go来创建wc.so作为程序的plugin,可是在操作过程中,发现windows对这个plugin的支持不是很好的样子,然后看代码,这一步的必要性是啥?原来是涉及到一个函数loadPlugin

mapf, reducef := loadPlugin(os.Args[1])

上面这句其实就是要从wc.so中加载Map和Reduce函数,那如果不想用插件呢,也是容易的,直接函数赋值就好了。

//mapf, reducef := loadPlugin(os.Args[1])
var mapf func(string) []KeyValue
var reducef func(string, []string) string
mapf = Map
reducef = Reduce

接着看下mrsequential里有什么内容

*KeyValue [stuct]
    //属性
    Key、Value
    //要实现sort.Interface
    Len、Less、Swap
*Map [func]
*Reduce [func]
*main [func]

Map:输入参数contents string,将contents分割成words,返回[]KeyValue{}数组。
e.g
输入:"This is a cat. That is a dog."
输出:[{"This", "1"}, {"is", "1"},{"a", "1"},{"cat", "1"},{"That", "1"},{"is", "1"},{"a", "1"},{"dog", "1"}]

Reduce:输入参数values []string,返回values数组的长度
*values就是"1"的数组

接下来看下main里面是怎么用Map和Reduce函数的,

Usage:mrsequential pg-*.txt

(1)intermediate是一个KeyValue的数组,每个key出现一次,就增加一个{word,"1"}
遍历每一个pg输入文件,将文件内容读入content,调用mapf,加入intermediate数组
(2)将intermediate数组按照key排序,这样,相同的word会挨着
(3)遍历intermediate数组
将拥有相同的key(word)的键值对合并,一个key(word)的values传递给一个reducef
将key(word)和它的值输出到文件mr-out-0

2.lab01需要的代码
我们把这个任务分成三个部分main, mr, wc
main:

package main

mr:

package mr

wc:

package wc

每个package要导入其他package中的代码一般用import来实现,这边有个golang的方法

$ cd /d/My/project/go/workspace/lab01/

$ go mod init lab01

这样lab01目录下就有一个go.mod

module lab01

go 1.15

这个时候代码里再import就不用担心GOROOT/GOPATH配置的问题

import lab01/mr
import lab01/wc

这边也是为了在windows上调试方便,对代码做出的小调整

3.golang rpc基本框架使用

知道了我们程序的最终目标,并且已经把代码框架提取出来,那分布式涉及到的rpc是怎么玩的呢?

main/mrmaster:
调用mr.MakeMaster启动一个master的server,通过Done函数来监听,每次等1s

main/mrworker:
获取wc定义的Map和Reduce函数,调用mr.Worker来执行mapf和reducef

那么接下来看下三个重要的步骤mr.MakeMaster,mr.Done,mr.Worker

都在mr里,好的,那先看下mr的代码结构
mr/master

*Master [stuct]
    //属性…自己写
    //方法
    Example [func] //用来测试rpc
    server [func] //启动一个server端,并通过go 开启一个线程监听
    Done [func] //…自己写

*MakeMaster [func] //定义一个Master,…自己写,调用server()

mr/worker

*Worker [func]
    //…自己写
    CallExample()//用来测试rpc

<pre style="background:white">其中args和reply都是用interface{}这个空接口类型定义的,这是为什么呢?原来go 语言规定,如果希望传递任意类型的变参,变参类型应该制定为空接口类型,并且空接口可以指向任何数据对象,所以可以使用interface{}定义任意类型变量。</pre>

rpc测试中master监听,worker调用master中的Example方法,成功!

*CallExample [func]
    call("Master.Example", &args, &reply) //调用Master中的Example方法
*call [func]
特别说明下这个函数
func call(rpcname string, args interface{}, reply interface{}) bool {
}

rpcname是要调用的Master中定义的函数名
args是rpcname的输入参数
reply是rpcname的返回值
其中args和reply都是用interface{}这个空接口类型定义的,这是为什么呢?原来go 语言规定,如果希望传递任意类型的变参,变参类型应该制定为空接口类型,并且空接口可以指向任何数据对象,所以可以使用interface{}定义任意类型变量。

测试一下:

$ cd /d/My/project/go/workspace/lab01/main
$ go build mrmaster.go
$ go build mrworker.go
$./mrmaster.exe ../pg-*.txt
2021/01/06 16:10:24 rpc.Register: method "Done" has 1 input parameters; needs exactly three
$./mrworker.txt
reply.Y 100

rpc测试中master监听,worker调用master中的Example方法,成功!

4.mapreduce实现
我画了一个结构图便于自己以后想起来再看自己的代码能懂的快一些(经常看不懂自己写的代码星人的自我拯救)

6.824-lab01 代码结构说明

reference

第一个实验,确实上手很不容易,参考很多大神的资料,在此感谢!
https://github.com/yzongyue/6.824-golabs-2020
https://zhuanlan.zhihu.com/p/187473895
https://blog.csdn.net/bysui/article/details/52128221
https://pdos.csail.mit.edu/6.824/schedule.html
【MIT公开课】6.824 分布式系统 · 2020年春(完结·中英字幕·机翻)https://www.bilibili.com/video/BV1qk4y197bB?p=2
我的代码都放在-》https://github.com/mengmayang/6.824-2020

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容