paxos 练手 推进中
学习https://github.com/huoyu820125/SecondPaxos 自己编写网络版本
在学习过程将此代码的线程 锁等改成c++11 就不用包含那么多文件
主要更改如下

1 // MyPaxos.cpp: 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <iostream> 6 #include <chrono> 7 #include <mutex> 8 #include <thread> 9 #include "Acceptor.h" 10 #include "Proposer.h" 11 12 13 paxos::Proposer p[11]; 14 paxos::Acceptor a[11]; 15 int finishedCount = 0; 16 std::mutex l[11]; 17 18 std::mutex printlock; 19 20 void Proposer(int id) { 21 paxos::Proposer &proposer = p[(int)id]; 22 paxos::PROPOSAL value = proposer.GetProposal(); 23 paxos::PROPOSAL lastValue; 24 25 26 int acceptorId[11]; 27 int count = 0; 28 29 while (true) { 30 value = proposer.GetProposal();//拿到提议 31 printlock.lock(); 32 std::cout << "Proposer" << (int)id << "号开始(Propose阶段):提议=[编号:" << value.serialNum 33 << ",提议:" << value.value << "]\n"; 34 printlock.unlock(); 35 count = 0; 36 int i = 0; 37 38 for (i = 0; i < 11; i++) 39 { 40 /* 41 * 发送消息到第i个acceptor 42 * 经过一定时间达到acceptor,sleep(随机时间)模拟 43 * acceptor处理消息,mAcceptors[i].Propose() 44 * 回应proposer 45 * 经过一定时间proposer收到回应,sleep(随机时间)模拟 46 * proposer处理回应mProposer.proposed(ok, lastValue) 47 */ 48 //经过随机时间,消息到达了mAcceptors[i] 49 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100) ); 50 l[i].lock(); 51 bool ok = a[i].Propose(value.serialNum, lastValue); 52 l[i].unlock(); 53 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100)); 54 //处理Propose回应 55 if (!proposer.Proposed(ok, lastValue)) //重新开始Propose阶段 56 { 57 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100)); 58 //为了降低活锁,多等一会让别的proposer有机会完成自己的2阶段批准 59 break; 60 } 61 paxos::PROPOSAL curValue = proposer.GetProposal();//拿到提议 62 if (curValue.value != value.value)//acceptor本次回应可能推荐了一个提议 63 { 64 printlock.lock(); 65 std::cout << "Proposer" << (int)id << "号修改了提议:提议=[编号:" << 66 curValue.serialNum << ",提议:" << curValue.value << "]\n"; 67 printlock.unlock(); 68 } 69 acceptorId[count++] = i;//记录愿意投票的acceptor 70 if (proposer.StartAccept()) 71 { 72 if (0 == rand() % 2) break; 73 } 74 } 75 //检查有没有达到Accept开始条件,如果没有表示要重新开始Propose阶段 76 if (!proposer.StartAccept()) continue; 77 //开始Accept阶段 78 //发送Accept消息到所有愿意投票的acceptor 79 value = proposer.GetProposal(); 80 printlock.lock(); 81 std::cout << "Proposer" << (int)id << "号开始(Accept阶段):提议=[编号:" << 82 value.serialNum << ",提议:" << value.value << "]\n"; 83 printlock.unlock(); 84 for (i = 0; i < count; i++) 85 { 86 //发送accept消息到acceptor 87 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100));//经过随机时间,accept消息到达acceptor 88 //处理accept消息 89 l[acceptorId[i]].lock(); 90 bool ok = a[acceptorId[i]].Accept(value); 91 l[acceptorId[i]].unlock(); 92 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100));//经过随机时间,accept回应到达proposer 93 //处理accept回应 94 if (!proposer.Accepted(ok)) //重新开始Propose阶段 95 { 96 std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100));///为了降低活锁,多等一会让别的proposer有机会完成自己的2阶段批准 97 break; 98 } 99 if (proposer.IsAgree())//成功批准了提议 100 { 101 printlock.lock(); 102 std::cout << "Proposer" << (int)id << "号批准了提议:最终提议 = [编号:" << 103 value.serialNum << ",提议:" << value.value << "]\n"; 104 printlock.unlock(); 105 return ; 106 } 107 } 108 } 109 return ; 110 } 111 112 int main() 113 { 114 int i = 0; 115 std::cout << "11个Proposer, 11个Acceptor准备进行Paxos\n"<< 116 "每个Proposer独立线程,Acceptor不需要线程\n"<< 117 "Proposer线程中等待随机时间:表示与Acceptor的通信时间\n"<< 118 "Proposer线程中调用Acceptor.Proposed()表示拿到了Propose请求结果\n"<< 119 "Proposer线程中调用Acceptor.Accepted()表示拿到了Accept请求结果\n"<< 120 "Proposer被批准后结束线程,其它线程继续投票最终,全部批准相同的值,达成一致。\n"; 121 122 paxos::PROPOSAL value; 123 for (i = 0; i < 11; i++) 124 { 125 p[i].SetPlayerCount(11, 11); 126 value.serialNum = value.value = i + 1; 127 p[i].StartPropose(value); 128 } 129 130 std::thread t[11]; 131 for (i = 0; i < 11; i++) { 132 t[i] = std::thread(Proposer, i); 133 } 134 for (i = 0; i < 11; i++) { 135 t[i].join(); 136 } 137 while (true) { 138 std::this_thread::sleep_for(std::chrono::seconds(1)); 139 } 140 141 142 143 return 0; 144 }
20180513添加
根据视频 paxos和分布式系统_1024x768_2.00M_h.264
添加自写代码 vs2017 boost1.65编译
方案1 单点接收多点提交 二段提交 抢占提交权

// Accepter.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <ctime> #include <iostream> #include <string> #include <memory> #include <mutex> #include <thread> #include <boost/asio.hpp> using boost::asio::ip::tcp; const int default_port = 9687; #pragma pack (1) //提议数据结构 typedef struct PROPOSAL { int index; //当前请求的阶段 int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }PROPOSAL; typedef struct ACCEPTSAL { int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }ACCEPTSAL; #pragma pack() enum INDEX { INIT_INDEX = 1, PREPARE_INDEX, ACCEPT_INDEX, FINISH_INDEX, ERROR_INDEX = -1, }; int current_index = PREPARE_INDEX; ACCEPTSAL g_ServerRecord = {-1,-1 }; boost::asio::io_service io_service; std::mutex g_mtx; int acceptepoch = -1; //========================================================== void HandlePropose(std::shared_ptr<tcp::socket> socket) { PROPOSAL buf = { -1,-1,-1 }; ACCEPTSAL tmpAcpsal = { -1,-1 }; try { while (1) { boost::asio::read(*socket, boost::asio::buffer(&buf, sizeof(buf))); { std::lock_guard<std::mutex> lock(g_mtx); //prepare阶段 if (buf.index == PREPARE_INDEX) { if (g_ServerRecord.epoch <= buf.epoch && g_ServerRecord.value == -1) { //更新最新的prepare epoch g_ServerRecord.epoch = buf.epoch; current_index = ACCEPT_INDEX; } } else if (buf.index == ACCEPT_INDEX && (-1 == g_ServerRecord.value)) { if ((buf.epoch >= g_ServerRecord.epoch)) { g_ServerRecord.value = buf.value; current_index = FINISH_INDEX; } } //拷贝accepter记录 tmpAcpsal = g_ServerRecord; } //回复 boost::asio::write(*socket, boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal))); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; return; } } int main() { try { tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), default_port)); for (;;) { std::shared_ptr<tcp::socket> psocket = std::make_shared<tcp::socket>(io_service); acceptor.accept(*psocket); std::thread t(HandlePropose,psocket); t.detach(); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }

// Proposer.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <ctime> #include <iostream> #include <string> #include <memory> #include <mutex> #include <thread> #include <boost/asio.hpp> using boost::asio::ip::tcp; const std::string default_port = "9687"; #pragma pack (1) //提议数据结构 typedef struct PROPOSAL { int index; //当前请求的阶段 int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }PROPOSAL; typedef struct ACCEPTSAL { int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }ACCEPTSAL; #pragma pack() enum INDEX { INIT_INDEX = 1, PREPARE_INDEX, ACCEPT_INDEX, FINISH_INDEX, ERROR_INDEX = -1, }; //======================================================== int main() { try { boost::asio::io_service io_service; tcp::resolver resolver(io_service); tcp::resolver::query query("127.0.0.1", default_port.c_str()); tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); tcp::socket socket(io_service); boost::asio::connect(socket, endpoint_iterator); for (;;) { PROPOSAL pro = { PREPARE_INDEX ,1,99 }; ACCEPTSAL tmpAcpsal = { -1,-1 }; boost::system::error_code error; boost::asio::write(socket, boost::asio::buffer(&pro,sizeof(pro)), error); size_t len = socket.read_some(boost::asio::buffer(&tmpAcpsal,sizeof(tmpAcpsal)), error); if (error == boost::asio::error::eof) break; // Connection closed cleanly by peer. else if (error) throw boost::system::system_error(error); // Some other error. if (tmpAcpsal.epoch == pro.epoch) { pro.index = ACCEPT_INDEX; boost::asio::write(socket, boost::asio::buffer(&pro, sizeof(pro)), error); size_t len = socket.read_some(boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal)), error); } if (tmpAcpsal.value != -1) { std::cout << " value is " << tmpAcpsal.value << std::endl; system("pause"); return 1; } else { std::cerr << " value is " << tmpAcpsal.value << " . Error !!!" << std::endl; system("pause"); return 1; } } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } system("pause"); return 0; }
缺点 若抢占的propose 出现故障 则无法释放锁
方案2 单点接受多点提交 二段提交 根据epoch抢占提交权 若获取提交权的proposer出现故障 将会被拥有更高epoch的propose替代

// Accepter.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <ctime> #include <iostream> #include <string> #include <memory> #include <mutex> #include <thread> #include <boost/asio.hpp> using boost::asio::ip::tcp; const int default_port = 9687; #pragma pack (1) //提议数据结构 typedef struct PROPOSAL { int index; //当前请求的阶段 int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }PROPOSAL; typedef struct ACCEPTSAL { int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }ACCEPTSAL; #pragma pack() enum INDEX { INIT_INDEX = 1, PREPARE_INDEX, ACCEPT_INDEX, FINISH_INDEX, ERROR_INDEX = -1, }; int current_index = PREPARE_INDEX; ACCEPTSAL g_ServerRecord = {-1,-1 }; boost::asio::io_service io_service; std::mutex g_mtx; int acceptepoch = -1; //========================================================== void HandlePropose(std::shared_ptr<tcp::socket> socket) { PROPOSAL buf = { -1,-1,-1 }; ACCEPTSAL tmpAcpsal = { -1,-1 }; try { while (1) { boost::asio::read(*socket, boost::asio::buffer(&buf, sizeof(buf))); { std::lock_guard<std::mutex> lock(g_mtx); std::cout << "recv " << "index = " << buf.index << ". epoch = " << buf.epoch << ". value = " << buf.value << std::endl; } { std::lock_guard<std::mutex> lock(g_mtx); //prepare阶段 if (buf.index == PREPARE_INDEX) { if (g_ServerRecord.epoch <= buf.epoch && g_ServerRecord.value == -1) { std::cout << "Prepare index" << std::endl; std::cout << "Prepare recv " << "index = " << buf.index << ". epoch = " << buf.epoch << ". value = " << buf.value << std::endl; //更新最新的prepare epoch g_ServerRecord.epoch = buf.epoch; current_index = ACCEPT_INDEX; } } else if (buf.index == ACCEPT_INDEX && (-1 == g_ServerRecord.value)) { if ((buf.epoch >= g_ServerRecord.epoch)) { std::cout << "Accept index, epoch =" << buf.epoch << ".value = "<< buf.value << std::endl; g_ServerRecord.epoch = buf.epoch; g_ServerRecord.value = buf.value; current_index = FINISH_INDEX; } } //拷贝accepter记录 tmpAcpsal = g_ServerRecord; } //回复 { std::lock_guard<std::mutex> lock(g_mtx); std::cout << "reply epoch = " << tmpAcpsal.epoch << ". value = " << tmpAcpsal.value << std::endl; } boost::asio::write(*socket, boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal))); } } catch (std::exception& e) { //std::cerr << e.what() << std::endl; return; } } int main() { try { tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), default_port)); for (;;) { std::shared_ptr<tcp::socket> psocket = std::make_shared<tcp::socket>(io_service); acceptor.accept(*psocket); std::thread t(HandlePropose,psocket); t.detach(); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }

// Proposer.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <ctime> #include <iostream> #include <string> #include <memory> #include <mutex> #include <random> #include <thread> #include <boost/asio.hpp> using boost::asio::ip::tcp; const std::string default_port = "9687"; #pragma pack (1) //提议数据结构 typedef struct PROPOSAL { int index; //当前请求的阶段 int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }PROPOSAL; typedef struct ACCEPTSAL { int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }ACCEPTSAL; #pragma pack() enum INDEX { INIT_INDEX = 1, PREPARE_INDEX, ACCEPT_INDEX, FINISH_INDEX, ERROR_INDEX = -1, }; //======================================================== boost::asio::io_service io_service; unsigned GetRand() { static std::default_random_engine e; static std::uniform_int_distribution<unsigned> u(0, 1000); return u(e); } void ProposeThreadFunc(int id) { try { tcp::resolver resolver(io_service); tcp::resolver::query query("127.0.0.1", default_port.c_str()); tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); tcp::socket socket(io_service); boost::asio::connect(socket, endpoint_iterator); int epoch = id+1; for (;;) { PROPOSAL pro = { PREPARE_INDEX ,epoch +1,id +1 }; ACCEPTSAL tmpAcpsal = { -1,-1 }; boost::system::error_code error; boost::asio::write(socket, boost::asio::buffer(&pro, sizeof(pro)), error); size_t len = socket.read_some(boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal)), error); if (error == boost::asio::error::eof) break; // Connection closed cleanly by peer. else if (error) throw boost::system::system_error(error); // Some other error. if (tmpAcpsal.epoch == pro.epoch) { pro.index = ACCEPT_INDEX; std::chrono::milliseconds dura(GetRand()); std::this_thread::sleep_for(dura); boost::asio::write(socket, boost::asio::buffer(&pro, sizeof(pro)), error); size_t len = socket.read_some(boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal)), error); } if (tmpAcpsal.epoch > epoch) { int i = tmpAcpsal.epoch%11; int loopcount = tmpAcpsal.epoch / 11; epoch = loopcount * 11 + 11 + id + 1; if(id == 2) std::cout << "epoch = " << epoch << std::endl; } if (tmpAcpsal.value != -1) { std::cout << " value is " << tmpAcpsal.value << std::endl; break ; } std::chrono::milliseconds dura(GetRand()); std::this_thread::sleep_for(dura); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } } int main() { std::thread t[11]; for (int i = 0; i < 11; i++) { t[i] = std::thread(ProposeThreadFunc,i); } for (int i = 0; i < 11; i++) { t[i].join(); } system("pause"); return 0; }
运行代码 添加随机参数 最后提交接受的数值 实现随机化
运行第一次 最后接受数值为6
运行第二次 最后接受数值为2
accept扩展多点 匹配客户端待完成

// accepters.cpp: 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <ctime> #include <iostream> #include <string> #include <memory> #include <mutex> #include <thread> #include <boost/asio.hpp> using boost::asio::ip::tcp; const int default_port = 9687; #pragma pack (1) //提议数据结构 typedef struct PROPOSAL { int index; //当前请求的阶段 int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }PROPOSAL; typedef struct ACCEPTSAL { int epoch; //流水号,1开始递增,保证全局唯一 int value; //提议内容 }ACCEPTSAL; #pragma pack() enum INDEX { INIT_INDEX = 1, PREPARE_INDEX, ACCEPT_INDEX, FINISH_INDEX, ERROR_INDEX = -1, }; //======================================================= //多个accepter记录 boost::asio::io_service io_service; int current_index[11];// = PREPARE_INDEX; ACCEPTSAL g_ServerRecord[11];// = { -1,-1 }; std::mutex g_mtx[11]; std::mutex g_print_mtx; int acceptepoch[11];// = -1; //===================================================== void HandlePropose(std::shared_ptr<tcp::socket> socket,int id) { PROPOSAL buf = { -1,-1,-1 }; ACCEPTSAL tmpAcpsal = { -1,-1 }; try { while (1) { boost::asio::read(*socket, boost::asio::buffer(&buf, sizeof(buf))); { std::lock_guard<std::mutex> lock(g_mtx[id]); std::lock_guard<std::mutex> printLock(g_print_mtx); std::cout << "recv " << "index = " << buf.index << ". epoch = " << buf.epoch << ". value = " << buf.value << std::endl; } { std::lock_guard<std::mutex> lock(g_mtx[id]); //prepare阶段 if (buf.index == PREPARE_INDEX) { if (g_ServerRecord[id].epoch <= buf.epoch && g_ServerRecord[id].value == -1) { { std::lock_guard<std::mutex> printLock(g_print_mtx); std::cout << "Prepare index" << std::endl; std::cout << "Prepare recv " << "index = " << buf.index << ". epoch = " << buf.epoch << ". value = " << buf.value << std::endl; } //更新最新的prepare epoch g_ServerRecord[id].epoch = buf.epoch; current_index[id] = ACCEPT_INDEX; } } else if (buf.index == ACCEPT_INDEX && (-1 == g_ServerRecord[id].value)) { if ((buf.epoch >= g_ServerRecord[id].epoch)) { { std::lock_guard<std::mutex> printLock(g_print_mtx); std::cout << "Accept index, epoch =" << buf.epoch << ".value = " << buf.value << std::endl; } g_ServerRecord[id].epoch = buf.epoch; g_ServerRecord[id].value = buf.value; current_index[id] = FINISH_INDEX; } } //拷贝accepter记录 tmpAcpsal = g_ServerRecord[id]; } //回复 { std::lock_guard<std::mutex> lock(g_mtx[id]); { std::lock_guard<std::mutex> printLock(g_print_mtx); std::cout << "reply epoch = " << tmpAcpsal.epoch << ". value = " << tmpAcpsal.value << std::endl; } } boost::asio::write(*socket, boost::asio::buffer(&tmpAcpsal, sizeof(tmpAcpsal))); } } catch (std::exception& e) { //std::cerr << e.what() << std::endl; return; } } void AcceptThreadFunc(int id) { try { tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), default_port+id)); for (;;) { std::shared_ptr<tcp::socket> psocket = std::make_shared<tcp::socket>(io_service); acceptor.accept(*psocket); std::thread t(HandlePropose, psocket,id); t.detach(); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } } void init() { for (int i = 0; i < 11; i++) { current_index[i] = PREPARE_INDEX; g_ServerRecord[i].epoch = -1; g_ServerRecord[i].value = -1; } } int main() { init(); std::thread t[11]; for (int i = 0; i < 11; i++) { t[i] = std::thread(AcceptThreadFunc,i); } for (int i = 0; i < 11; i++) { t[i].join(); } return 0; }
作 者: itdef
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话