6.824 Lab2 PartA实验部分

2024-02-26 03:38
文章标签 实验 部分 lab2 6.824 parta

本文主要是介绍6.824 Lab2 PartA实验部分,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

测试文件测试内容分析

func TestReElection2A(t *testing.T) {servers := 3cfg := make_config(t, servers, false)defer cfg.cleanup()cfg.begin("Test (2A): election after network failure")leader1 := cfg.checkOneLeader()// if the leader disconnects, a new one should be elected.cfg.disconnect(leader1)cfg.checkOneLeader()// if the old leader rejoins, that shouldn't// disturb the new leader.cfg.connect(leader1)leader2 := cfg.checkOneLeader()// if there's no quorum, no leader should// be elected.cfg.disconnect(leader2)cfg.disconnect((leader2 + 1) % servers)time.Sleep(2 * RaftElectionTimeout)cfg.checkNoLeader()// if a quorum arises, it should elect a leader.DPrintf("Check two server can choose a leader")cfg.connect((leader2 + 1) % servers)cfg.checkOneLeader()// re-join of last node shouldn't prevent leader from existing.cfg.connect(leader2)cfg.checkOneLeader()cfg.end()
}

整体测试流程如下:

1、开启3个服务器,检查能否选举出一个leader

2、一个leader掉线,看能否选出一个新的leader

3、旧的leader回归,不应该影响新的leader的状态(当然旧leader迅速被重新选举为leader也没问题)

4、下线两个服务器,此时不应该有leader

5、恢复一个机器,此时有两个机器。应该选举出一个leader

6、恢复下线机器,三台服务器同时可用,此时原有leader的状态不应该被影响

需要推敲的问题

1、断线的旧leader的回来尚未接收到发送的心跳就直接开始新的选举是否有问题

答:没问题,是运行的正常逻辑

2、lastActiveTime在哪些时候进行更新比较好?

答:应该严格遵守figure2的实现流程

If election timeout elapses without receiving AppendEntries RPC from current leader or granting vote to candidate:

只有在接收到当前leader的AppendEntries请求或者批准投票时才会对lastActiveTime进行更新

3、第五步通常会出现新来和回归的机器同时开始选举,怎么办?

答:此时第一次选举应该失败,随后因为二者的election Timeout不同,会自动开启新一轮选举得到最终结果。

4、选举的leader应该将广播时间设置比较早,以便迅速出发发送心跳的服务

核心代码示例

func (rf *Raft) startElection() {rf.mu.Lock()defer rf.mu.Unlock()now := time.Now()elapses := now.Sub(rf.lastActiveTime)// 超时的话会从Follower转为Candidateif rf.role == Follower {if elapses >= rf.electionTimeOut {rf.role = CandidateDPrintf("RaftNode [%d] changed from follower to candidate at term %d", rf.me, rf.currentTerm)}}if rf.role == Candidate && elapses >= rf.electionTimeOut {rf.lastActiveTime = nowrf.currentTerm += 1rf.votedFor = rf.merf.persist()// 准备开始发起投票,构造投票流程args := RequestVoteArgs{Term:         rf.currentTerm,CandidateId:  rf.me,LastLogIndex: len(rf.logs),// go不支持三元表达式,所以term赋值写在最后}lastLogTerm := 0if len(rf.logs) != 0 {lastLogTerm = rf.logs[len(rf.logs)-1].Term}args.LastLogTerm = lastLogTerm// 参数赋予流程初步结束,为了避免RPC请求停顿影响效率,先放掉锁rf.mu.Unlock()// 开始for循环不断发起响应通知进行处理cond := sync.NewCond(&rf.mu)// 初始化时全部peer里自己就已经给自己投了一票,因此初始化为1finished := 1count := 1for index, _ := range rf.peers {if index == rf.me {continue}// 发送请求并处理返回结果go func(server int, args *RequestVoteArgs) {reply := RequestVoteReply{}ok := rf.sendRequestVote(server, args, &reply)rf.mu.Lock()finished += 1DPrintf("RaftNode [%d] send Rpc to RaftNode [%d] and get response,%v,now finish %d", rf.me, server, ok, finished)if ok {// 进行逻辑运行,除了降级逻辑外,还是尽量将逻辑推迟到下面if reply.VoteGranted == true {count += 1} else {if reply.Term > rf.currentTerm {// 状态进行改变清空rf.currentTerm = reply.Termrf.role = Followerrf.votedFor = -1rf.leaderId = -1rf.persist()}}DPrintf("RaftNode [%d] get result %d,vote %d", rf.me, finished, count)}cond.Broadcast()rf.mu.Unlock()}(index, &args)}rf.mu.Lock()// 想要直接往后走:1、投票完毕 2、得到超过半数票数无需继续等待 3、已经不再是candidate状态// 注意小于等于与大于等于的使用,一开始在这里卡死便是因为小于等于直接满足for count <= len(rf.peers)/2 && finished != len(rf.peers) && rf.role == Candidate {DPrintf("finish %d,count %d", finished, count)cond.Wait()}// 不是candidate的话直接结束if rf.role != Candidate {return}DPrintf("boolean %v,count %d,total %d", count > len(rf.peers)/2, count, len(rf.peers))// 否则对状态进行判断if count > len(rf.peers)/2 {rf.role = Leaderrf.leaderId = rf.merf.lastBroadcastTime = time.Unix(0, 0) // 设置为原始时间,保证立刻发送心跳rf.persist()DPrintf("RaftNode  [%d] become a leader", rf.me)}}return
}

在写的时候本人遇到的错误点如下:

1、在写逻辑表达式、if判断条件时一定要注意大于等于与大于之类的区别。

开始写成count<len(peers)/2,导致一开始直接满足条件跳过cond.wait所处的for循环,永远输出错误结果。实际上应该为count<=len(peers)/2

2、第一遍完成后遇到了split-brain问题,两个都获得了一票,总票数不等于len(peers)且没有人得票超过一半陷入死锁状态

解决方法:将rf.mu.lock从if ok块里提出来,放到rpc结果后。如果机器下线,RPC结果ok为false,也可以进行统计

编程技巧

1、尽可能使用cond.wait等方法来避免for循环空转浪费CPU资源

2、RPC调用前不要加锁,等返回结果后再加锁处理结果,以避免因为网络因素长时间无法得到结果程序无法释放出锁,影响整体执行效率

这篇关于6.824 Lab2 PartA实验部分的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/747606

相关文章

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

项目实战系列三: 家居购项目 第四部分

购物车 🌳购物车🍆显示购物车🍆更改商品数量🍆清空购物车&&删除商品 🌳生成订单 🌳购物车 需求分析 1.会员登陆后, 可以添加家居到购物车 2.完成购物车的设计和实现 3.每添加一个家居,购物车的数量+1, 并显示 程序框架图 1.新建src/com/zzw/furns/entity/CartItem.java, CartItem-家居项模型 /***

码蹄集部分题目(2024OJ赛9.4-9.8;线段树+树状数组)

1🐋🐋配对最小值(王者;树状数组) 时间限制:1秒 占用内存:64M 🐟题目思路 MT3065 配对最小值_哔哩哔哩_bilibili 🐟代码 #include<bits/stdc++.h> using namespace std;const int N=1e5+7;int a[N],b[N],c[N],n,q;struct QUERY{int l,r,id;}que

关于断言的部分用法

1、带变量的断言  systemVerilog assertion 中variable delay的使用,##[variable],带变量的延时(可变延时)_assertion中的延时-CSDN博客 2、until 的使用 systemVerilog assertion 中until的使用_verilog until-CSDN博客 3、throughout的使用   常用于断言和假设中的

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口

61.以太网数据回环实验(4)以太网数据收发器发送模块

(1)状态转移图: (2)IP数据包格式: (3)UDP数据包格式: (4)以太网发送模块代码: module udp_tx(input wire gmii_txc ,input wire reset_n ,input wire tx_start_en , //以太网开始发送信