Paxos算法的java實現demo(只是爲了簡單的測試)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章