Paxos 的概念我就不在這裏囉嗦了,網上有很多優秀的博客,下面是我推薦的一個寫的比較好的
https://www.cnblogs.com/linbingdong/p/6253479.html
我們直接上代碼吧,代碼裏面都有註釋
先看一下項目結構圖
Acceptor類
package com.paxos;
import java.util.Map;
/**
* @author jiezhou
* @CalssName: Acceptor
* @Package com.paxos
* @Description: 接受者
* @date 2020/6/24/16:08
*/
public class Acceptor {
/**
* 接受者的id(唯一標識)
*/
int id;
/**
* 提案Map類型,key 爲提案編號,value提案值
*/
private Map<Integer,String> proporsal;
/**
* 接收過的最大提案編號N
*/
int resN;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Map<Integer, String> getProporsal() {
return proporsal;
}
public void setProporsal(Map<Integer, String> proporsal) {
this.proporsal = proporsal;
}
public int getResN() {
return resN;
}
public void setResN(int resN) {
this.resN = resN;
}
/**
* @param proposerN 提案編號
*
*/
public Map<Integer, String> prepareReq(int proposerN ){
if(proposerN<this.resN){ //不響應
System.out.println("proposerN:"+proposerN+",this.resN"+resN);
return null;
}else{
this.resN=proposerN;
//響應pok
return this.proporsal;
}
}
/**
* 第二階段的 accept請求
*/
public String acceptReq(Map<Integer,String> map){
for (Map.Entry<Integer, String> entry : map.entrySet()) {
if (entry.getKey()>=this.resN){
this.setProporsal(map);
return "aok";
}
}
return "no";
}
}
Common類
package com.paxos;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author jiezhou
* @CalssName: common
* @Package com.paxos
* @Description: 公有的數據類
* @date 2020/6/24/16:18
*/
public class Common {
/**
* 提交者數量
*/
public static final int PROPOSER_COUNT = 2;
/**
* 接受者數量
*/
public static final int ACCEPTOR_COUNT = 3;
/**
* 全局提案編號(初始值爲1)
*/
public static AtomicInteger proposerN=new AtomicInteger(0);
}
Proposer類
package com.paxos;
import java.util.Map;
/**
* @author jiezhou
* @CalssName: Proposer
* @Package com.paxos
* @Description: 提議者
* @date 2020/6/24/16:08
*/
public class Proposer {
/**
* 提議者id(唯一標識)
*/
private int id;
/**
* 提案Map類型,key 爲提案編號,value提案值
*/
private Map<Integer,String> proporsal;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Map<Integer, String> getProporsal() {
return proporsal;
}
public void setProporsal(Map<Integer, String> proporsal) {
this.proporsal = proporsal;
}
}
RandomUtils類
package com.paxos;
import java.util.Random;
/**
* @author jiezhou
* @CalssName: RandomUtils
* @Package com.paxos
* @Description: 隨機數(模擬由於網絡通信掛掉的提交者或者接受者的id)
* @date 2020/6/24/16:22
*/
public class RandomUtils {
public static int randomAcceptorId(){
Random random = new Random();
int id = random.nextInt(Common.ACCEPTOR_COUNT)+1;
System.out.println(id);
return id;
}
public static int randomProposerId(){
Random random = new Random();
int id = random.nextInt(Common.PROPOSER_COUNT)+1;
// System.out.println(id);
return id;
}
/* public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
randomProposerId();
}
}*/
}
Test類
package com.paxos;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author jiezhou
* @CalssName: Test
* @Package com.paxos
* @Description: 測試
* @date 2020/6/24/16:15
*/
public class Test {
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
//接受者集合
private static List<Acceptor> acceptorList=new ArrayList<>();
//提交者集合
private static List<Proposer> proposerList=new ArrayList<>();
/**
* 初始化方法
*/
public static void init(){
//初始化接受者 acceptor
for (int i = 1; i <= Common.ACCEPTOR_COUNT; i++) {
Acceptor acceptor = new Acceptor();
acceptor.setId(i);
acceptor.setResN(0);
acceptor.setProporsal(new HashMap<>());
acceptorList.add(acceptor);
}
//初始化提交者proposer
for (int i = 1; i <= Common.PROPOSER_COUNT; i++) {
Proposer proposer = new Proposer();
proposer.setId(i);
proposer.setProporsal(new HashMap<>());
proposerList.add(proposer);
}
}
public static void main(String[] args) throws InterruptedException {
init();
int id = RandomUtils.randomProposerId();//宕機id
for (int i = 0; i < proposerList.size(); i++) {
Proposer proposer = proposerList.get(i);
if(id!=proposer.getId()){//使用Random來模擬網絡通信阻塞(宕機)
executorService.execute(new Runnable() {
@Override
public void run() {
try {
guocheng(proposer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
Thread.sleep(4000);
//宕機的機器重啓了
for (Proposer proposer1 : proposerList) {
if(proposer1.getId()==id){//找到宕機的機器
executorService.execute(new Runnable() {//模擬重啓
@Override
public void run() {
try {
System.out.println("提交者id:"+id+"重啓了");
guocheng(proposer1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
public static void guocheng( Proposer proposer) throws InterruptedException {
//1、生成提案
// 先判斷(學習)之前的接受者裏面有沒有接受之前提議者的提案,沒有就自己生成一個提案
//如果有接受者已經接受了之前提議者的提案,無論自己的提案編號大還是小,都得把自己的提案的value指定爲之前的那個提案的value
if(!chackAccept()){//沒有接受過提案
HashMap<Integer, String> map = new HashMap<>();
map.put(Common.proposerN.incrementAndGet(),"提案"+proposer.getId());
proposer.setProporsal(map);
}else{
//之前有接受者接受過提案,只能乖乖用之前的提案值(也就是Map的value使用之前的提案的)
for (int i = 0; i < acceptorList.size(); i++) {
Acceptor acceptor = acceptorList.get(i);
Map<Integer, String> proporsal = acceptor.getProporsal();
if(proporsal.size()!=0){
for (Map.Entry<Integer, String> entry : proporsal.entrySet()) {
Map<Integer, String> map = new HashMap<>();
map.put(Common.proposerN.incrementAndGet(),entry.getValue());
proposer.setProporsal(map);
break;
}
break;
}
}
}
Integer var1=0;//提案編號
Map<Integer, String> proporsal = proposer.getProporsal();
Set<Map.Entry<Integer, String>> entries = proporsal.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
var1 = entry.getKey();
}
//2、prepare請求(這裏是對所有的接受者進行請求)
//統計 prepare請求 pok響應的個數
AtomicInteger var2 =new AtomicInteger(0);
Map<Integer, String> var3;
String maxAcceptV;
int maxAcceptN=0;
Boolean flg=true;
for (int i = 0; i < acceptorList.size(); i++) {
Acceptor acceptor = acceptorList.get(i);
var3 = acceptor.prepareReq(var1);
if(var3!=null){
var2.incrementAndGet();
for (Map.Entry<Integer, String> var4 : var3.entrySet()) {
Integer key = var4.getKey();
if(flg){
maxAcceptN=key;
maxAcceptV=var4.getValue();
flg=false;
}
if(maxAcceptN<key){
maxAcceptN=key;
maxAcceptV=var4.getValue();
}
}
}
}
//判斷是否收到超過一半響應(包括一半)
//階段2,accept請求
AtomicInteger aokCount=new AtomicInteger(0);
Boolean half = chackHalf(Common.ACCEPTOR_COUNT, var2.intValue());
if(half){
for (Acceptor acceptor : acceptorList) {
String req = acceptor.acceptReq(proporsal);
if("aok".equals(req)){
aokCount.incrementAndGet();
}
}
}else{
guocheng(proposer);
}
//如果過半,V被確定,不過半,重新發起Prepare請求
Boolean var4 = chackHalf(Common.ACCEPTOR_COUNT, aokCount.intValue());
if(var4){
//輸出一下每個acceptor的AcceptV
for (Acceptor acceptor : acceptorList) {
for (Map.Entry<Integer, String> entry : acceptor.getProporsal().entrySet()) {
System.out.println("接受者的id:"+acceptor.getId()+"最終acceptN :"+entry.getKey()+",最終acceptV:"+entry.getValue());
}
}
return;//結束
}else{
guocheng(proposer);
}
}
/**
* 判斷是否超過一半響應
* @param total
* @param var1
* @return true 過半 false 不過半
*/
public static Boolean chackHalf(int total,int var1){
double var = total / 2;
if(var>var1){
return false;
}else{return true;}
}
/**
*
* @return false 沒有接收提案
*/
public static Boolean chackAccept(){
Boolean res=true;
for (int i = 0; i < acceptorList.size(); i++) {
Acceptor acceptor = acceptorList.get(i);
Map<Integer, String> proporsal = acceptor.getProporsal();
if(proporsal.size()==0){//之前沒有接受過提案
res=false;
break;
}
}
return res;
}
}
增強版的,只需要吧Test類換掉就行,如下
package com.paxos;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author jiezhou
* @CalssName: Test
* @Package com.paxos
* @Description: 測試
* @date 2020/6/24/16:15
*/
public class Test {
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
//接受者集合(全部)
private static List<Acceptor> acceptorList=new ArrayList<>();
//接受者集合(過半)
private static List<Acceptor> acceptorListQuorum=new ArrayList<>();
//提交者集合
private static List<Proposer> proposerList=new ArrayList<>();
private static Thread thread;
private static Thread runnable;
/**
* 初始化方法
*/
public static void init(){
//初始化接受者 acceptor (全部)
// for (int i = 1; i <= Common.ACCEPTOR_COUNT; i++) {
// Acceptor acceptor = new Acceptor();
// acceptor.setId(i);
// acceptor.setResN(0);
// acceptor.setProporsal(new HashMap<>());
// acceptorList.add(acceptor);
// }
//初始化提交者proposer
for (int i = 1; i <= Common.PROPOSER_COUNT; i++) {
Proposer proposer = new Proposer();
proposer.setId(i);
proposer.setProporsal(new HashMap<>());
proposerList.add(proposer);
}
}
private static void randomAcceptorQuorum() {
ArrayList<Acceptor> temp = new ArrayList<>();
ArrayList<Integer> ids = new ArrayList<>();
Random random = new Random();
for (;;) {
int id = random.nextInt(Common.ACCEPTOR_COUNT)+1;
if(ids.contains(id)){
continue;
}
double ceil = (Common.ACCEPTOR_COUNT / 2)+1;
if(ids.size()==ceil){
break;
}
if(acceptorListQuorum.size()==0){
Acceptor acceptor = new Acceptor();
acceptor.setId(id);
acceptor.setResN(0);
acceptor.setProporsal(new HashMap<>());
temp.add(acceptor);
ids.add(id);
}else{
Boolean f=false;
for (Acceptor acceptor : acceptorListQuorum) {
if(id==acceptor.getId()){
temp.add(acceptor);
ids.add(id);
f=true;
}
}
if(!f){
Acceptor acceptor = new Acceptor();
acceptor.setId(id);
acceptor.setResN(0);
acceptor.setProporsal(new HashMap<>());
temp.add(acceptor);
ids.add(id);
}
}
}
acceptorListQuorum=temp;
}
public static void main(String[] args) throws InterruptedException {
init();
int id = RandomUtils.randomProposerId();//宕機id
for (int i = 0; i < proposerList.size(); i++) {
Proposer proposer = proposerList.get(i);
if(id!=proposer.getId()){//使用Random來模擬網絡通信阻塞(宕機)
runnable = new Thread() {
@Override
public void run() {
try {
guocheng(proposer);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
runnable.start();
}
}
Thread.sleep(4000);
//宕機的機器重啓了
for (Proposer proposer1 : proposerList) {
if(proposer1.getId()==id){//找到宕機的機器
thread = new Thread() {//模擬重啓
@Override
public void run() {
try {
runnable.join();
System.out.println("提交者id:" + id + "重啓了");
guocheng(proposer1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
}
public static void guocheng( Proposer proposer) throws InterruptedException {
//1、生成提案
// 先判斷(學習)之前的接受者裏面有沒有接受之前提議者的提案,沒有就自己生成一個提案
//如果有接受者已經接受了之前提議者的提案,無論自己的提案編號大還是小,都得把自己的提案的value指定爲之前的那個提案的value
randomAcceptorQuorum();
if(!chackAccept()){//沒有接受過提案
HashMap<Integer, String> map = new HashMap<>();
map.put(Common.proposerN.incrementAndGet(),"提案"+proposer.getId());
proposer.setProporsal(map);
}else{
//之前有接受者接受過提案,只能乖乖用之前的提案值(也就是Map的value使用之前的提案的)
for (int i = 0; i < acceptorListQuorum.size(); i++) {
Acceptor acceptor = acceptorListQuorum.get(i);
Map<Integer, String> proporsal = acceptor.getProporsal();
if(proporsal.size()!=0){
for (Map.Entry<Integer, String> entry : proporsal.entrySet()) {
Map<Integer, String> map = new HashMap<>();
map.put(Common.proposerN.incrementAndGet(),entry.getValue());
proposer.setProporsal(map);
break;
}
break;
}
}
}
Integer var1=0;//提案編號
Map<Integer, String> proporsal = proposer.getProporsal();
Set<Map.Entry<Integer, String>> entries = proporsal.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
var1 = entry.getKey();
}
//2、prepare請求(這裏是對半數以上的接受者進行請求)
//統計 prepare請求 pok響應的個數
randomAcceptorQuorum();
AtomicInteger var2 =new AtomicInteger(0);
Map<Integer, String> var3;
String maxAcceptV;
int maxAcceptN=0;
Boolean flg=true;
for (int i = 0; i < acceptorListQuorum.size(); i++) {
Acceptor acceptor = acceptorListQuorum.get(i);
var3 = acceptor.prepareReq(var1);
if(var3!=null){
var2.incrementAndGet();
for (Map.Entry<Integer, String> var4 : var3.entrySet()) {
Integer key = var4.getKey();
if(flg){
maxAcceptN=key;
maxAcceptV=var4.getValue();
flg=false;
}
if(maxAcceptN<key){
maxAcceptN=key;
maxAcceptV=var4.getValue();
}
}
}
}
//判斷是否收到超過一半響應(包括一半)
//階段2,accept請求
randomAcceptorQuorum();
AtomicInteger aokCount=new AtomicInteger(0);
Boolean half = chackHalf(Common.ACCEPTOR_COUNT, var2.intValue());
if(half){
for (Acceptor acceptor : acceptorListQuorum) {
String req = acceptor.acceptReq(proporsal);
if("aok".equals(req)){
aokCount.incrementAndGet();
}
}
}else{
guocheng(proposer);
}
//如果過半,V被確定,不過半,重新發起Prepare請求
Boolean var4 = chackHalf(Common.ACCEPTOR_COUNT, aokCount.intValue());
if(var4){
//輸出一下每個acceptor的AcceptV
for (Acceptor acceptor : acceptorListQuorum) {
for (Map.Entry<Integer, String> entry : acceptor.getProporsal().entrySet()) {
System.out.println("接受者的id:"+acceptor.getId()+"最終acceptN :"+entry.getKey()+",最終acceptV:"+entry.getValue());
}
}
return;//結束
}else{
guocheng(proposer);
}
}
/**
* 判斷是否超過一半響應
* @param total
* @param var1
* @return true 過半 false 不過半
*/
public static Boolean chackHalf(int total,int var1){
double var = total / 2.0;
if(var>var1){
return false;
}else{return true;}
}
/**
*
* @return false 沒有接收提案 true 有接受過提案
*/
public static Boolean chackAccept(){
Boolean res=false;
for (int i = 0; i < acceptorListQuorum.size(); i++) {
Acceptor acceptor = acceptorListQuorum.get(i);
Map<Integer, String> proporsal = acceptor.getProporsal();
if(proporsal.size()!=0){//之前有接受過提案
return true;
}
}
return res;
}
}
運行結果(增強版的運行結果):
如有bug或者錯誤,歡迎大家評論,大家一起學習。完整代碼:https://github.com/jcode95/Paxos-java-demo