MIT-6.824学习笔记(二)

这是学习笔记的第二篇,这篇主要是记录我完成MIT-6.824 lab 2 的思路和想法

Lab2 Overview

这部分的实验主要会实现容错的机制并且完成一个K/V service的雏形。容错的机制其实就是用主备系统来实现。这里用一种叫viewservice的server来完成lab。viewservice会不断监控每一个server是否还alive,如果primary server或者backup server出现问题,就采取对应的措施。如果primary挂了,就会让一个backup取代成为primary。如果一个backup挂了,并且“集群”中海油空闲的server,就会让他变成backup。这时候primary会把自己完整的database内容发送给新的backup,并随后通过Put来验证backup的K/V database是否保持一致性。

因为这个lab是在单机上模拟的分布式的一些特性,所以一些地方需要注意,比如说这个databases的数据是放在内存里而不是放在disk当中的。以及各个server和client等之间的通信只能通过RPC,而不能通过直接的golang对象或者文本IO来实现。

因为这是模拟出来的分布式逻辑,所以下面的一些容错机制和限制会导致真正的分布式系统会too weak,比如

  • view service是很脆弱的,因为没有复制。这其实就是类似hadoop中的HA的问题。相当于master节点只有一个的话,是很危险的,容易过载。
  • 像之前说的,一个空闲的sever变成backup的时候,需要从primary复制databses来完成备份的效果,这个是很慢的。尤其当网络不好的时候。
  • server并没有将K/V databases存储在disk中, so they can’t survive simultaneous crashes (e.g., a site-wide power failure).
  • 如果出现暂时性的问题导致primary和backup间的通讯出现延迟或者中断,那么只有两个办法来解决。第一是改变view来将backup移除,或者keep trying。如果通信问题出现的很频繁,那以上两个方法都不好用。
  • 这个实验的primary/backup结构有些类似 Flat Datacenter Storage和Monge DB,还有Chain Replication,当然他们的设计和实现更详细。

Part A

好了首先我们来实现这个View Service,就是完成primary/backup结构。
整体思路大概是这样的,client和server通过Ping()方法,也就是RPC请求来通信。client向view server发送的ping带两个参数,me:string,viewnum:int 也就是client的name和viewnum。其中me用来判断是primary还是backup或者是空闲的server。viewnum用来判断是否现在的primary已经被知晓。

从上面的逻辑我们可以很简单的完成client的代码,其实工程中很多的方法和接口已经写好了,核心部分我们只要完成viewservice/client.go 中的Ping()方法就行。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (ck *Clerk) Ping(viewnum uint) (View, error) {
// prepare the arguments.
args := &PingArgs{}
args.Me = ck.me
args.Viewnum = viewnum
var reply PingReply
// send an RPC request, wait for the reply.
ok := call(ck.server, "ViewServer.Ping", args, &reply)
if ok == false {
return View{}, fmt.Errorf("Ping(%v) failed", viewnum)
}
return reply.View, nil
}

然后是服务端接收到ping并且得到传来的参数后作出的反应。在viewservice/server.go中。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func (vs *ViewServer) Ping(args *PingArgs, reply *PingReply) error {
id, viewnum := args.Me,args.Viewnum
switch id {
case vs.curr.Primary:
if viewnum == vs.curr.Viewnum {
vs.hasPrimaryAcked = true
vs.lastSeen[vs.curr.Primary] = time.Now()
}else {
vs.promoteBackup()
}
case vs.curr.Backup:
if viewnum == vs.curr.Viewnum {
vs.lastSeen[vs.curr.Backup] = time.Now()
} else {
vs.removeBackup()
}
default :
vs.addServer(id)
}
reply.View = vs.curr
return nil
}

主要就是根据me和viewnum来判断情况。如果me是primary,则判断viewnum是否和现在的viewnum相同,相同则说明primary已经被ack了,记下时间。不同则说明现在的primary可能已经挂了。就要将backup变成primary,执行promoteBackup()函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
func (vs *ViewServer) promoteBackup() bool {
if !vs.hasPrimaryAcked {
return false
}
if vs.curr.Backup == "" {
return false
}
vs.changeView(vs.curr.Viewnum+1, vs.curr.Backup, "")
return true
}

若是me是backup,则判断viewnum和现在的是否相等。不等的话就移除这个backup。

Part B

这部分就是在之前primary/backup的基础上,加上K/V service。未完待续!

Golang RPC

未完待续!