MIT6824 Lab2要求

原文

https://pdos.csail.mit.edu/6.824/labs/lab-raft.html

介绍

这是在构建一系列容错的key-value数据库系统中的第一个,在这个lab中你将会涉及到Raft(一个状态机的复制协议)。在下一个lab你将会在Raft的基础上构建一个key-value的数据库服务,然后您将在多个复制状态机上分享您的服务,以获得更高的性能。

复制服务通过在多个复制服务器上存储其状态(即数据)的完整副本来实现容错。复制允许服务继续运行,即使它的一些服务器发生故障(崩溃或损坏或脆弱的网络)。挑战在于,失败可能导致副本保存不同的数据副本。

Raft将客户机请求组织成一个称为日志的序列,并确保所有副本服务器都看到相同的日志。每个副本按日志顺序执行客户机请求,并将它们应用到服务状态的本地副本。因为所有活动副本都看到相同的日志内容,所以它们都以相同的顺序执行相同的请求,因此继续具有相同的服务状态。如果服务器出现故障但随后恢复,Raft会负责将其日志更新到最新。只要至少大部分服务器都是正常运行的,并且可以相互通信,Raft就会继续运行。如果没有这样的多数派,Raft将不会取得任何进展,但一旦多数派能够再次进行沟通,Raft就会重新开始。

在这个实验室中,您将实现Raft作为一个Go对象类型和相关的方法,这意味着在更大的服务中作为一个模块使用。一组Raft实例通过RPC相互通信,以维护复制的日志。Raft接口将支持不确定的编号命令序列,也称为日志条目。条目用索引号编号。具有给定索引的日志项最终将被提交。此时,Raft应该将日志条目发送到更大的服务以便其执行。

您应该遵循扩展Raft论文中的设计,特别要注意图2。您将实现本文中的大部分内容,包括保存持久状态和在节点故障并重新启动后读取它。您将不会实现集群成员关系更改(第6节)。

您可能会发现本指南以及关于并发锁和结构的建议都很有用。如果你想要获得更广阔的视角,可以看看Paxos, Chubby, Paxos Made Live, Spanner, Zookeeper, Harp, Viewstamped Replication和Bolosky等。(注:学生指南是几年前写的,特别是2D部分已经改变了。)在盲目地遵循一种特定的实现策略之前,一定要了解它的意义!)

请记住,这个实验中最具挑战性的部分可能不是实现您的解决方案,而是调试它。为了帮助解决这一挑战,您可能希望花时间考虑如何使您的实现更容易调试。你可以参考指南页面和这篇关于有效打印声明的博客文章。

我们还提供了Raft交互的示意图,可以帮助阐明Raft代码如何与上面的层交互。

本实验分为四个部分。你必须在相应的截止日期提交每一部分。

开始

如果您已经完成了实验1,那么您已经拥有了实验源代码的副本。如果没有,您可以在Lab 1指令中找到如何通过git获取源代码的说明。

我们为您提供骨架代码src/raft/raft.go。我们还提供了一组测试,您应该使用这些测试来驱动您的实现工作,我们将使用这些测试来对您提交的实验室进行评分。测试在src/raft/test_test.go中。

当我们给你的提交打分时,我们将运行没有-race标志的测试。但是,您应该确保代码没有竞态条件,因为竞态条件可能导致测试失败。因此,强烈建议在开发解决方案时还运行带有-race标志的测试。

要启动并运行,请执行以下命令。不要忘记git拉来获得最新的软件。

$ cd ~/6.824
$ git pull
...
$ cd src/raft
$ go test
Test (2A): initial election ...
--- FAIL: TestInitialElection2A (5.04s)
        config.go:326: expected one leader, got none
Test (2A): election after network failure ...
--- FAIL: TestReElection2A (5.03s)
        config.go:326: expected one leader, got none
...
$

code

通过在Raft / Raft .go中添加代码来实现Raft。在该文件中,您将找到框架代码,以及如何发送和接收rpc的示例。

您的实现必须支持以下接口,测试人员和(最终)您的键/值服务器将使用该接口。你可以在raft.go的评论中找到更多细节。

// create a new Raft server instance:
rf := Make(peers, me, persister, applyCh)

// start agreement on a new log entry:
rf.Start(command interface{}) (index, term, isleader)

// ask a Raft for its current term, and whether it thinks it is leader
rf.GetState() (term, isLeader)

// each time a new entry is committed to the log, each Raft peer
// should send an ApplyMsg to the service (or tester).
type ApplyMsg

一个服务调用Make(peers,me,…)来创建一个Raft peer。peers参数是Raft对等体(包括这个)的网络标识符数组,用于RPC。me参数是这个对等体在对等体数组中的索引。Start(命令)要求Raft启动处理,将命令追加到复制的日志中。Start()应该立即返回,而不需要等待日志追加完成。服务期望您的实现为每个新提交的日志条目发送一个ApplyMsg到Make()的applyCh通道参数。

筏。go包含发送RPC (sendRequestVote())和处理传入RPC (RequestVote())的示例代码。你的Raft对等体应该使用labrpc Go包交换rpc(来源在src/labrpc)。测试人员可以告诉labrpc延迟rpc,重新排列它们的顺序,并丢弃它们,以模拟各种网络故障。虽然您可以临时修改labrpc,但要确保您的Raft与原始的labrpc一起工作,因为我们将使用原始的labrpc来测试和评分您的实验室。你的Raft实例必须只与RPC交互;例如,它们不允许使用共享的围棋变量或文件进行通信。

后续的实验建立在这个实验的基础上,因此给自己足够的时间来编写可靠的代码是很重要的。

Part 2A: leader election (moderate)

实现Raft leader的选举和心跳(没有日志条目的AppendEntries rpc)。2A部分的目标是选出一个leader,如果没有失败,leader将继续担任leader,如果旧的leader失败或来自旧leader的数据包丢失,则由新的leader接管。运行go test -运行2A来测试你的2A代码。

你不能轻易地直接运行你的Raft实现;相反,你应该通过测试器来运行它,即执行test -run 2A。

遵循本文的图2。在这一点上,你关心的是发送和接收RequestVote rpc,与选举有关的服务器规则,以及与领导人选举有关的国家,
将图2中用于领导人选举的状态添加到Raft .go中的Raft结构中。您还需要定义一个结构来保存关于每个日志条目的信息。
填写RequestVoteArgs和RequestVoteReply结构。修改Make()来创建一个后台gor例程,当它有一段时间没有收到另一个对等体的消息时,它会周期性地通过发送RequestVote rpc启动leader选举。这样,如果已经有了领导,同伴就会知道谁是领导,或者自己成为领导。实现RequestVote() RPC处理程序,这样服务器就可以相互投票。

要实现心跳,定义一个AppendEntries RPC结构(尽管您可能还不需要所有的参数),并让leader定期发送它们。编写一个AppendEntries RPC处理程序方法,重置选举超时,以便当一个服务器已经当选时,其他服务器不会继续作为leader前进。

确保不同同伴的选举超时不总是同时触发,否则所有同伴只会投票给自己,没有人会成为领导者。

测试人员要求leader每秒发送心跳rpc的次数不超过10次。

测试人员要求您的Raft在旧领导者失败的5秒内选出新的领导者(如果大多数同行仍然可以通信)。但是,请记住,在选票分裂的情况下,领袖选举可能需要多轮投票(如果数据包丢失或候选人不幸选择了相同的随机退选时间,就会发生这种情况)。您必须选择足够短的选举超时(以及心跳间隔),以便选举很可能在不到5秒的时间内完成,即使它需要多轮投票。

论文的第5.2节提到了150到300毫秒的选举超时。这样的范围只有在领导者每150毫秒发送心跳的频率大大超过一次时才有意义。因为测试者将你的心跳限制在每秒10次,你必须使用比试卷上的150到300毫秒更大的选举超时,但不能太大,因为这样你可能无法在5秒内选出一个领导人。

你可能会发现go的rand很有用。

您需要编写能够定期或在时间延迟后执行操作的代码。最简单的方法是创建一个带有调用time.Sleep()的循环的goroutine;(请参阅Make()为此目的创建的ticker() goroutine)。不要占用围棋的时间。定时器或时间。这些都很难正确使用。

指南页面提供了一些关于如何开发和调试代码的提示。

如果您的代码在通过测试时遇到困难,请再次阅读本文的图2;领导选举的完整逻辑分布在图的多个部分。

不要忘记实现GetState()。

当Raft永久关闭一个实例时,测试人员调用Raft的rf.Kill()。你可以使用rf.killed()检查Kill()是否被调用。您可能希望在所有循环中都这样做,以避免死筏实例打印令人困惑的消息。

Go RPC只发送名称以大写字母开头的结构字段。子结构还必须有大写的字段名(例如数组中的日志记录字段)。labgob包会警告你这一点;不要忽视这些警告。

在提交第2A部分之前,请确保您通过了2A测试,这样您就可以看到如下内容:

$ go test -run 2A
Test (2A): initial election ...
  ... Passed --   3.5  3   58   16840    0
Test (2A): election after network failure ...
  ... Passed --   5.4  3  118   25269    0
Test (2A): multiple elections ...
  ... Passed --   7.3  7  624  138014    0
PASS
ok  	6.824/raft	16.265s
$

每个“Passed”行包含五个数字;它们是测试花费的时间(以秒为单位)、Raft对等体的数量、测试期间发送的RPC的数量、RPC消息中的字节总数以及Raft报告提交的日志条目的数量。您的数字将与这里显示的不同。如果愿意,您可以忽略这些数字,但是它们可以帮助您检查实现发送的rpc的数量。对于所有实验2、3和4,如果所有测试(开始测试)花费的时间超过600秒,或者任何单个测试花费的时间超过120秒,则评分脚本将使解决方案失败。

当我们对你的提交进行评分时,我们将运行不带-race标志的测试,但你也应该确保你的代码始终通过带-race标志的测试。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值