首先我们要理清master启动要干哪些事儿。
- 首先master得按照nMap的数量进行文件分割
- 得创建master的RPC服务并发布
- master还得监听worker状态
- 给合适的worker分发任务,指定每个worker处理哪些文件
1. master数据结构
- address:master的rpc地址,当然也是socket地址(文件描述符fd)
- doneChannel:任务完成的信号通知通道
- newCond:当worker注册添加到workers时的条件信号
- workers:存储每个注册后的worker的socket地址,也可以说是rpc地址,rpc是对socket的又一层包装
- shutdown:关闭worker的信号通知通道
- l:监听worker的连接器
- status:每个worker的当前状态
2. master创建
- makeInputs(nMap):按照nMap的数量进行文件分割
- master := port(“master”):声明master端口名
- mr := Distributed(“test”, files, nReduce, master):master进行分发调度的总控
2.1 Distributed详解
- mr = newMaster(master):用于创建master的数据结构
2.2 开启master的RPC服务
关于go的RPC,这里不做过多叙述,后面会写一篇文章专门讲一下go的rpc源码及过程分析。首先对于server端,这里主要就是master对象:
- rpcs := rpc.NewServer(): 创建一个server端的rpc对象
- rpcs.Register(mr):发布server对象(这里就是master)的调用方法,即暴露server的api供client调用
- os.Remove(mr.address) // only needed for “unix”
- l, e := net.Listen(“unix”, mr.address):创建并返回一个监听unix域(本地通信,前面socket谈到过)的socket队列的listener
- mr.l = l:将listener绑定到master上
接下来便是不断accept连接,并响应请求。 - 其中mr.l.Accept()会从socket队列中获取一个fd(即socket),然后根据fd生成一个对应file的读写流()-coon并返回。如果socket队列中没有client连接fd,则其会一直处于阻塞状态,直到等到下一个连接的到来,由于前面函数使用了go关键字,此协程遇到阻塞则会切换到其它任务上去。
所以后面可以从coon里面读取client的数据,或者向里面写入到客户端的数据,这正是我们前面socket讲的——accept返回的是客户端socket在server端分配的一个新的文件描述符fd,相当于server端是通过和这个新fd来与client通信。于是后面要传入到client的数据可以使用相应的I/O函数进行写入。
- 请求响应:conn中包含了client传递过来的一些参数,比如请