做这个实验,最能学习的地方就是调试和思考的过程了,如果你直接参考了别人的思路或者代码,那么对于你来说,这个实验能学习到的东西则会大大减少

记录 MIT 6.824 Lab 2 中 Part A的一些想法以及思路,如果错误,还请指出,谢谢

Lab 2 的链接如下http://nil.csail.mit.edu/6.824/2015/labs/lab-2.html,其中 Part A 要求实现一个 ViewService,根据 Server的状态,进行相应的 View 切换(这里 View 表示当前能提供服务的 Server 以及相应的状态组合,ViewService 提供 View 的增删改查功能),这里将该 Lab 的两个部分分开来写。

Part A 实现 ViewService 的整个功能,ViewService 需要保证如下几点:

  1. 在以下几种情况中的任何一种发生的时候才进行 View 的切换

    1. primary 和 backup 都没有 ack
    2. primary 或者 backup 重启
    3. backup 为空,且有闲置的 Server(会发送 Ping 命令给 ViewService)
  2. 只有在 primary ack 过了当前的 View 之后,才能进行 View 的切换,换句话说,如果 primary 收到一个新的 View,然后挂了,那么 ViewService 就不应该切换 View(根据页面的介绍,必须 ack 当前 View,那么这里有一个问题,如果 primary 收到一个新的 View,然后重启了,这种情况做何处理?),这个限制简化了架构以及实现,但是可能导致一直不能更换 View

  3. 如果 primary 或 backup 在约定好的时间内没有发送 Ping 命令,则认为该 Server 挂了,需要做相应的操作
  4. View 的 primary 只能是当前 View 的 primary 或者前一个 View 的 backup(在 ViewService 初始化的时候,primary 是第一个连接进来的 Server)
  5. View 的 backup 可以是除 primary 之外的任何 Server,可为空
    Part A 的要求实现如下三个函数:
    func (vs ViewServer) Ping(args PingArgs, reply PingReply) error {}
    func (vs
    ViewServer) Get(args GetArgs, reply GetReply) error {}
    func (vs *ViewServer) tick() {}

    其中 Ping 接受 Server 发送过来的信息,并更新 View 的相应情况,Get 获取当前的 View,tick 则是一个回调函数,在固定时间内调用一次,检查 primary 和 backup 是否已经宕机,这里我实现的 Get 很简单,直接返回当前 View(在 ViewServer 里面定义一个字段 curView 用来表示当前 View),其他两个才是重点

先把我定义的 ViewServer 贴一下(这个应该不算贴代码吧),下面能够更好的进行描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
type ViewServer struct {
mu sync.Mutex
l net.Listener
dead int32 // for testing
rpccount int32 // for testing
me string
// Your declarations here.
lastPing map[string]time.Time //记录 server 上次请求的时间,用来判断是否宕机
curView View //当前 View
hasView bool //当前是否有 View 存在
hasAcked bool //Primary 是否已经 ack 了当前 View
secondBackup string //将要被提升为 backup 的server
}

先说 tick,在 tick 中,首先我们需要知道 primary 是否已经 ack 了当前 View,如果没有 ack,那么就直接返回即可,如果 ack 过了当前 View,那么就继续进行下面的操作(下面的操作必须在 primary ack 过了当前 View 之后才能进行

  1. 判断 Primary 是否超时

    1. 如果超时,则判断当前 View 是否存在 backup

      1. 存在 backup,则将 backup 提升为 primary,然后将 primary 从 lastPing 中删除,并且将 hasAcked 置为 false
      2. 不存在,将 hasView 置为 false
    2. 不超时,不做操作

  2. 判断 backup 是否超时

    1. 超时,则将 curView 中的 backup 置为 “”,然后 hasAcked 置为 false
    2. 不超时,不做操作
      然后接下来是 Ping 函数
  3. 判断当前是否有 View(通过 hasView)

    1. 没有,就将当前发送 Ping 的 Server 当成 Primary,然后返回
    2. 有当前 View

      1. 发送 Ping 的 Server 是 什么角色?

        1. 是 primary,考虑 primary 是否重启

          1. 重启(通过 ping 命令 请求参数是否为 0 判断),则判断当前 View 是否有 backup

            1. 有,将 backup 提升为 primary,然后将 primary 设置成 secondBackup(会在下次请求的时候加入到 View 中),这样的实现,是否合理,是否需要直接将 primary 设置为 backup?
            2. 没有,则将当前 View 的 Viewnum 加 1 即可
          2. 不是重启,则 ack 当前 View(请求参数可能是 当前 View 的 Viewnum 和 0 之外的第三个值吗?)

            1. backup,考虑是否重启

              1. 重启,将 backup 的角色切换到 secondBackup
              2. 不是重启,则不做操作
            2. 闲置的 Server,考虑当前 View 是否有 backup

              1. 有,不做操作(这里是否需要将当前 Server 加入到 secondBackup?
              2. 没有,则将当前 Server 加入到 secondBackup,等待下一次 primary 发送 ping 的时候,提升为 backup

当然,Ping 和 tick 函数 需要考虑加锁的问题,如果只为了通过测试,可以不加锁,测试都是串行的请求(有 goroutine),如果不加锁,可能会遇到很诡异的问题

思路整理之后发现也不是太难,不过过程中还是有不少细节需要注意,如果可以,最好是自己进行思考,然后不断的调试,通过打印日志,思考是否符合自己的理解,然后进行代码的调整

 

Comments