MIT6.824 Lab2

MIT6.824 Lab2

设计思路
  • Server :
    • Server每隔PingInterval(85ms)时间获取View视图
      • Primary : 设置自身属性为Primary
      • Backup : 设置自身属性为Backup
    • Get : 当Server为Primary时返回相应值
    • Put/Append :
      • 第一阶段 : 将数据传入handler
      • 第二阶段 : 等待直到handler处理完毕,返回
    • Confirm : 当Server为Primary时,删除TagMap中对应的Tag,表示此次操作已被Client确认
    • Sync : 当Server为Backup时,将参数中的dataMap写入Server自身的dataMap.
    • handler :
      • 模式一 : 接收Put/Append数据并写入tempMap
      • 模式二 : 同步至Backup后将tempMap写入dataMap
  • Client :
    • Client每隔PingInterval(100ms)时间获取Primary地址
    • Client持续调用Get直到RPC返回结果(value or empty)
    • Client持续调用Get/Put/Append直到RPC返回结果(过程中更新函数内Primary)
    • Client调用Put/Append时生成RandomKey,当RPC正确返回时,通过RPC函数Comfirm通知Server,Client已经获知成功状态
  • 同步 :
    • Server之间的同步并不是逐条信息的同步,而是在短时间内接收Client请求放入handler中
    • handler在收集信息后先同步到Backup,随后写入自身dataMap,此时将所有请求返回.
实现
  • Client的实现很简单,检查返回值状态后调用Comfirm即可

  • Server :
    • handler :
      • 通过select实现上述模式的切换.
      • case1 : 接收PutAppendChannel(同步模式)中的数据并写入tempMap
      • case2 : 通过时间戳对tempMap中的数据进行排序,并处理Append方法(将已有数据与要处理的数据合并).如果backup不为空则调用syncToBack来同步数据,同步完成后将tempMap中的数据写入Server自身的dataMap中.清空临时变量,并释放barrier(关于barrier后面会详细描述).
    • PutAppend :
      • 检查Server是否为Primary
      • 检查Tag是否存在
      • 将数据写入PutAppendChannel
      • 等待barrier并返回
    • Confirm : 删除之前被Client注册的PutAppend Tag.
    • Sync :
      • 检查Server是否为Backup
      • 检查Tag是否存在
      • 将数据写入dataMap
      • 异步Confirm To Primary
    • barrier :
      • 实际上是阻塞管道加读写锁来实现的
      • 因为我们要实现一段时间内同步一次数据,所以我们将整个行为分为三个部分
      • Step1:通过Channel将数据传输至handler中并尝试获得读锁(此时已存在写锁,故阻塞).如果此时Channel是阻塞的,则说明handler正在同步,这些数据将等待直到Channel可写
      • Step2:handler同步数据至Backup,释放写锁(我在代码中使用了Gosched,写锁解锁时会唤醒等待的读锁,但是为了保险起见,我还是加上了).
      • Step3:所有PutAppend goroutines 获得读锁后马上解除.此时因为Channel仍然在阻塞,所以handler在所有读锁释放完毕后继续执行,添加写锁并回到初始状态.
需要注意的问题
  • Tag :
    • 为什么需要Tag : 当因为网络原因,Server收到并处理了Client的数据但是并没有成功返回的时候,Client需要重新调用RPC.此时Server无法区分是新的请求还是没收到返回值的重新调用
    • Tag需要注意什么 : Tag同样需要同步到Backup中,理由是当Primary处理完数据后突然宕机,Client并没有收到正确回复.此时Client更新视图后发现Primary地址发生变化将会重新将请求发至新的Primary.此时Primary无法区分这是新的请求还是重新调用.
  • Confirm :
    • 为什么需要Confirm : 通知Server对端已经收到成功回复并删除Server中的Tag
    • Confirm需要注意什么 : 确认时注意更新视图,否则有可能造成阻塞
  • View :
    • 为什么需要View : Server需要获知当前运行的Primary和Backup.如果ViewService选举自身成为Primary或者Backup则要提供响应的服务.
    • View需要注意什么 :
      • 调用Ping的时候需要发送ViewNum,如果此时Server是Backup,ViewNum并不会从收到的View中获得,而是缓存下来直到Primary同步完成后才更新.原因是当Backup更新了ViewNum后,ViewService就会在Primary宕机以后将Backup选举为Primary,但此时Backup中可能并没有数据.
      • 当Primary/Backup发送Ping失败时,应取消自身的Primary/Backup状态.理由是如果此时发生了分区,Primary对于ViewService不可达,但对于某些Client仍可达,则此时就会造成错误的处理,比如Get到脏数据,PutAppend返回成功结果但是Server已经不再是Primary/Backup
  • handler :
    • 为什么需要handler : Primary需要同步数据到Backup中,如果我们采用逐条同步的方式则会造成性能下降,如果加锁的话,我们无法控制goroutines被唤醒的顺序,这时候就可能造成了先收到的请求放在后面执行,此时就会造成脏数据.所以我们将请求数据写入Channel由handler统一处理
    • 为什么使用同步Channel : 为了在handler同步过程中阻塞后续的PutAppend请求,直到handler重新获取了写锁
    • 为什么使用RWMutex : RWMutex在和我们的行为要求吻合,允许多个goroutines获取某种状态(RLock).允许在一个goroutines释放(Unlock)后唤醒等待的goroutines.此时配合着同步管道handler goroutine恢复执行,重新设置状态(Lock)后开始新一轮的处理

    • handler需要注意什么 :
      • Server在退出时执行Unlock操作,释放被阻塞的PutAppend并将其结果设置为NotPrimary
  • Other :
    • 所有RPC操作都应该在失败状态后重新获取本地视图(我通过atomic.Value来实现的无锁)

转载于:https://www.cnblogs.com/xinglong/p/6942578.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值