前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >6.824 Lab 3: Fault-tolerant Key/Value Service Part-AIntroduction实际设计中出现的问题

6.824 Lab 3: Fault-tolerant Key/Value Service Part-AIntroduction实际设计中出现的问题

作者头像
zhuanxu
发布于 2018-08-23 04:48:44
发布于 2018-08-23 04:48:44
89700
代码可运行
举报
文章被收录于专栏:进击的程序猿进击的程序猿
运行总次数:0
代码可运行

Introduction

该实验是mit 6.824课程的第3个实验,基于raft协议完成一个key-value系统

实验分为A和B两个部分,在Part A中:我们不考虑日志的大小,在Part B中会完成快照功能

完整的代码地址 课程地址 实验地址 已经有的实验地址: Lab 1: MapReduce6.824 Lab 1: MapReduce(2016) Lab 2: Raftraft 系列解读(2) 之 测试用例 Lab 3: KV Raft Part-A6.824 Lab 3: Fault-tolerant Key/Value Service Part-A

Part A: Key/value service without log compaction

支持3个操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Put(key, value):改变key的值

Append(key, arg):给key的值新增value

Get(key):返回值

任务

当没有丢包和servers fail的情况下进行实现,需要提供客户端顺序一致性的api,调用Put,Append和Get3个api,在所有的server以相同的顺序执行,并且具有at-most-once的语义 一个建议的计划是:先完成server.go中的Op结构,然后完成server.go中的PutAppend()Get()操作,在操作中,应该先调用Start(),当日志commit的时候,回复客户端

提示

  1. 调用Start()后,kvraft servers 会等待raft log达成一致,通过applyCh获取一致的命令,我们需要考虑怎么安排代码,才能持续读取applyCh,而其他命令也能执行
  2. 我们需要处理case:leader调用了Start(),但是在log commit之前,丢失了leadership,这种情况下,代码应该将请求重新发送给新的leader。一种方式是,server需要检测出自己已经不是leader了,通过查看相同的start在index上返回一个不用的请求,另一种方式是通过调用GetState(),但是如果出现网络分区,可能不知道自己已经不是leader了,这种情况下client和server都处在网络分区中,因此可以无限的等待下去,直到网络恢复
  3. A kvraft server不应该完成Get()操作如果得不到majority,因为这样子可能会得不到最新的数据

任务:

需要处理重复请求,保证满足at-most-once的语义

提示:

  1. 需要对每个client请求编号
  2. 要保证快速的释放内存,因此可以在下一个请求带上下一个请求

实际设计中出现的问题

频繁变化leader

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (ck *Clerk) Get(key string) string {

   args := GetArgs{Key:key}

   for {

      for _,c := range ck.servers {

         time.Sleep(time.Millisecond*1000)

         reply := GetReply{}

         ok := c.Call("RaftKV.Get", &args, &reply)

         if ok && !reply.WrongLeader {

            return reply.Value

         }

      }

   }

   // You will have to modify this function.

   return ""

}

此处如果没有sleep的话,相当于客户端一直不断的在START,导致的一个问题是:server不断在处理START命令,导致正常的心跳都完成不了了,就出现了频繁的变化leader了,问题很严重,那应该怎么做呢?

后来做了优化,对于读操作不走 chan,这就没问题了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
index := -1

term := -1

isLeader := true

if rf.state != StateLeader {

   isLeader = false

   return index, term, isLeader

}

这样就有个初判断了

通过labrpc传递的数据不对

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func StartKVServer(servers []*labrpc.ClientEnd, me int, persister *raft.Persister, maxraftstate int) *RaftKV {

   // call gob.Register on structures you want

   // Go's RPC library to marshall/unmarshall.

   gob.Register(Op{})

如果没有 gob.Register(Op{}) 这就错误,为什么要加上这句话呢?

出现阻塞

分析:此处阻塞了为什么呢?因为在get上的时候,有一个没有收到apply?好奇怪

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// TODO:优化超时的逻辑

select {

case op := <-ch:

   commited := op == entry

   kv.logger.Debug("index:%d commited:%v",index,commited)

   return commited

case <- time.After(AppendTimeOut):

   kv.logger.Info("index:%d %s timeout after %v",index, entry.Type,AppendTimeOut)

   return false

}

加上上面的超时逻辑后,就可以解决阻塞的问题,但是一旦超时

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2016/10/26 14:37:45 I index:323 Append timeout after 1s

2016/10/26 14:37:45 0: client new get 0

2016/10/26 14:37:45 get wrong value, key 0, wanted:

就会出现问题,会重复执行 Append操作,因为其实已经apply了这个请求了

那怎么解决呢?我现在去除这个超时限制,在获取Apply的时候逻辑变为下面的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 通知结果

ch, ok := kv.result[index]

if ok {

   select {

   case <-ch:

   default:

   }

}else {

   // 没人读就有了数据?

   ch = make(chan Op,1)

   kv.result[index] = ch

}

ch <- op

此时就不会有超时的问题了,为什么呢?

很反人类的问题:因为当调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (kv *RaftKV)AppendLog(entry Op) bool {

   index, _, isLeader := kv.rf.Start(entry)

此时可能没等到执行下面的去读chan的时候,已经apply成功了,因此我们就需要事先往chan里面存入数据

TestUnreliable

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
☁  kvraft [master] ⚡ go test -run TestUnreliable

Test: unreliable ...

2016/10/26 14:59:42 get wrong value, key 3, wanted:

x 3 0 yx 3 1 y

, got

x 3 0 yx 3 0 yx 3 1 y

很容易看出问题:一个请求重复执行了,我们需要在客户端去重

对于每个客户端都给编号,然后每个请求都顺序增长

TestManyPartitionsManyClients

测试出现阻塞

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select {

case op := <-ch:

   commited := op == entry

   kv.logger.Debug("index:%d commited:%v", index, commited)

   return commited

   // 此处超时其实也很好理解,因为刚开始是leader,但是在log得到commit之前,丢失了leadership,此时

   // 如果没有超时机制,则会一直阻塞下去

   // 或者由于此时的leader是一个分区里面的leader,则只可能一直阻塞下去了

   // 因此也需要超时

case <-time.After(AppendTimeOut):

   //kv.logger.Info("index:%d %s timeout after %v", index, entry.Type, AppendTimeOut)

   return false

}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016.10.26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
6.824 raft Lab 4 multi-raft-group KV-Server
上文6.824 raft Lab 3 kvRaft是实现了一个single raft group的键值数据库,本文实现一个multi-raft-group键值数据库,通过分片和副本来提高容量、性能和可用性。
冰寒火
2022/10/16
1.1K2
6.824 raft Lab 3 kvRaft
前面实现了raft协议,本文实现一个单机键-值数据库,并通过raft建立主从架构,使得能够容错,但是没有分片。
冰寒火
2022/10/05
8570
MIT 6.824 Lab2 - Raft 实现
本文将介绍6.824 Lab2(测试用例2021/2020版 2A + 2B + 2C部分)的具体实现,视频版的讲解将发在B站:s09g谷歌摸鱼 。代码通过5000次测试,大致上应该没有问题。2021版的测试还有一个2D的部分,并没有包含在本文中。2D部分是关于Raft Snapshot,过早的实现2D可能会掩盖一些隐藏的bug。比如2C的一些test其实会产生超长的歧义链,这个时候就需要实现fast rollback优化,但是如果过早实现了snapshot就可以通过发送snapshot的方式直接修正歧义链。
s09g
2022/07/06
1.1K0
6.824 raft Lab 2D 日志压缩
书接上文6.824 raft Lab 2C 持久化与恢复,本文继续往下讲解日志压缩。
冰寒火
2022/10/03
1.3K0
6.824 2020 视频笔记六:Fault Tolerate Raft 1
MIT 今年终于主动在 Youtube 上放出了随堂视频资料,之前跟过一半这门课,今年打算刷一下视频,写写随堂笔记。该课程以分布式基础理论:容错、备份、一致性为脉络,以精选的工业级系统论文为主线,再填充上翔实的阅读材料和精到的课程实验,贯通学术理论和工业实践,实在是一门不可多得的分布式系统佳课。课程视频: Youtube,B 站。课程资料:6.824 主页。本篇是第六节课笔记,是 Raft 论文讲解的第一部分,主要总结了容错的几种类型以及 Raft 中的 Leader 选举相关内容。
木鸟杂记
2021/09/26
3520
MIT 6.824 - Raft学生指南
For the past few months, I have been a Teaching Assistant for MIT’s 6.824 Distributed Systems class. The class has traditionally had a number of labs building on the Paxos consensus algorithm, but this year, we decided to make the move to Raft. Raft was “designed to be easy to understand”, and our hope was that the change might make the students’ lives easier.
s09g
2022/07/06
8420
MIT 6.824 2020 Raft 实现细节备忘
18 年的时候做过一些 6.824,旧文在此,无奈做到 Part 2C,无论如何也跑不过测试,便一直搁置起来。但在后来的日子,却时时念起此门神课,心下伤感。拖到今日,终于可以来还愿了。
木鸟杂记
2021/09/26
8670
超详细教程!手把手带你使用Raft分布式共识性算法
导语 |从头理一遍Raft会给你带来新的体验与收获,让你从根本上理解Raft,理解它被提出的背景,在此背景下又是如何解决实际问题的,这才是从头实现一个Raft所带来的真正收益。本文聚焦在Raft算法的实现上,不对Raft本身做过多介绍,从领导者选举、日志同步、持久化和快照等几个层面进行展开讲解。 一、介绍 Raft目前是最著名的分布式共识性算法,被广泛的应用在etcd、k8s中。 根据Raft论文,可将Raft拆分为如下4个功能模块: 领导者选举 日志同步、心跳 持久化 日志压缩,快照 这4个模块彼此
腾讯云开发者
2021/10/29
2.2K0
RAFT && 6.824_lab2
Raft是著名的状态机类型的协议,他通过在多个服务器之间确定leader,保证了服务器之间对于一对key-value的consensus,可以通过这个可视化动画来理解raft
Heeler-Deer
2023/07/24
3040
分布式一致性协议之Raft的实现详解
到目前为止,不管是哪门语言,应该都已经有一些raft协议的实现了。但是大家的实现也都是根据raft协议论文来的,根据自己的服务形态在细节上有一些差异而已,大体上是一样的。
tunsuy
2022/10/27
7880
raft 系列解读(2) 之 测试用例raft 系列解读(2) 之 测试用例
基于mit的6.824课程,github代码地址:https://github.com/zhuanxuhit/distributed-system
zhuanxu
2018/08/23
1.4K0
raft 系列解读(2) 之 测试用例raft 系列解读(2) 之 测试用例
MIT 6.824 (2020 Spring) Lab1 - MapReduce 实现
使用的2020 Spring版的教程和Lab,最新的2021版是由Frans Kaashoek主讲,而不是Robert Morris。再加上Frans的口音都比Morris重很多,手写也比较难认,所以没有使用2021的教程。
s09g
2022/07/06
6360
MIT 6.824 (2020 Spring) Lab1 - MapReduce 实现
使用hashicorp Raft开发分布式服务
开发raft时用到的比较主流的两个库是Etcd Raft 和hashicorp Raft,网上也有一些关于这两个库的讨论。之前分析过etcd Raft,发现该库相对hashicorp Raft比较难以理解,其最大的问题是没有实现网络层,实现难度比较大,因此本文在实现时使用了hashicorp Raft。
charlieroro
2023/07/09
5590
Raft协议实现etcd
Etcd中跟存储部分相关的模块主要有3块,Raft状态机中存储的日志条目、持久化到文件的日志条目以及后端的KV存储。
ruochen
2021/11/22
1.3K0
深入浅出etcd之raft实现
etcd是coreOS使用golang开发的分布式,一致性的kv存储系统,因其易用性和高可靠性被广泛运用于服务发现、消息发布和订阅、分布式锁和共享配置等方面,也被认为是zookeeper的强有力的竞争者。作为分布式kv,其底层使用raft算法实现多副本数据的强一致性。etcd作为raft开源实现的标杆,在设计上,将 raft 算法逻辑和持久化、网络、线程等完全抽离出来单独实现,充分解耦,在工程上,实现了诸多性能优化,是 raft 开源实践中较早的工业级的实现,很多后来的 raft 实践者都直接或者间接的参考了 ectd-raft 的设计和实现,例如kubernetes,tiDb等。其广泛的影响力和优雅的golang代码实践也使得ectd成为golang的明星项目。在我们实际的分布式存储系统的项目开发中,raft也被应用于元信息管理和数据存储等多个模块,因此熟悉和理解etcd-raft的实现具有重大意义,本文从raft的基本原理出发,深入浅出地分析了raft在ectd中的具体实现。
evin
2019/02/23
9.7K0
深入浅出etcd之raft实现
golang源码分析:etcd(19)
etcd的增删改都会增加全局版本号,删除也是软删除,虽然便于回溯修改历史,但是随之带来问题,数据量的膨胀。因此需要进行压缩,也就是compact。假如我们制定压缩版本是v6,那么v6之前的所有已经删除的key会被删除,没有被删除的key保留最新的版本,丢弃之前的修改历史。
golangLeetcode
2023/09/20
3090
golang源码分析:etcd(19)
raft算法详解_python raft
  raft是工程上使用较为广泛的强一致性、去中心化、高可用的分布式协议。在这里强调了是在工程上,因为在学术理论界,最耀眼的还是大名鼎鼎的Paxos。但Paxos是:少数真正理解的人觉得简单,尚未理解的人觉得很难,大多数人都是一知半解。本人也花了很多时间、看了很多材料也没有真正理解。直到看到raft的论文,两位研究者也提到,他们也花了很长的时间来理解Paxos,他们也觉得很难理解,于是研究出了raft算法。
全栈程序员站长
2022/09/20
7920
raft算法详解_python raft
Raft: 寻找可理解的共识算法(完)
Figure 10: Switching directly from one configuration to another is unsafe because different servers will switch at different times. In this example, the cluster grows from three servers to five. Unfortunately, there is a point in time where two different leaders can be elected for the same term, one with a majority of the old configuration (Cold) and another with a majority of the new configuration (Cnew).
s09g
2022/07/06
5020
Raft: 寻找可理解的共识算法(完)
TiKV 源码阅读三部曲(三)写流程
TiKV 是一个支持事务的分布式 Key-Value 数据库,目前已经是 CNCF 基金会 的顶级项目。
PingCAP
2022/11/16
7460
Raft: 寻找可理解的共识算法(2)
Figure 2: A condensed summary of the Raft consensus algorithm (excluding membership changes and log compaction). The server behavior in the upper-left box is described as a set of rules that trigger independently and repeatedly. Section numbers such as §5.2 indicate where particular features are discussed. A formal specification [31] describes the algorithm more precisely.
s09g
2022/07/06
5420
Raft: 寻找可理解的共识算法(2)
相关推荐
6.824 raft Lab 4 multi-raft-group KV-Server
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验