[blockchain-027]一个极简的验证版paxos的go实现

1.参考资料

《区块链核心算法解析》第15页。

2.三个文件: main.go, service.go, client.go

把三个文件放在同一个目录编译既可,没有使用第三方库。编译完既可执行。客户端和服务端在同一个进程。

3.main.go

package main

import (
	"time"
)

func main() {
	done := make(chan int)

	go startServer()
	time.Sleep(1000 * time.Millisecond)
	go runClient()

	<-done
}

4.service.go

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"strconv"
	"strings"
)

//服务端输入参数
type Args struct {
	StrParams string
}

//服务端输出参数
type Reply struct {
	StrResult string
}

//服务的结构体
type PaxosService struct {
	//进程参数
	Host string `本进程所在主机名或者ip地址`
	Port string `本进程使用的端口号`

	//paxos参数
	T_max   int    `当前已经发布的最大票号`
	C       string `当前存储的命令`
	T_store int    `存储命令C的票`
}

//服务的测试函数
func (ps *PaxosService) Test1(args *Args, reply *Reply) error {
	reply.StrResult = args.StrParams + "123"
	return nil
}

func (ps *PaxosService) Process(args *Args, reply *Reply) error {
	reply.StrResult = args.StrParams + "123"
	//切分参数
	params := strings.Split(args.StrParams, ":")

	//输出一下
	//	for _, p := range params {
	//		fmt.Println(p)
	//	}
	//根据参数情况进行回复
	switch params[2] {
	case "require_ticket":
		//阶段1 客户端请求票
		fmt.Println("阶段1 客户端请求票")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("字符串转整型失败", err, params[3])
			return err
		}
		fmt.Println("阶段1 ,t=", require_t)
		if require_t > ps.T_max {
			ps.T_max = require_t
			reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_ticket" + ":" + "ok" + ":" + ps.C
		}
	case "require_propose":
		fmt.Println("阶段2 客户端请求propose")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("字符串转整型失败", err, params[3])
			return err
		}
		fmt.Println("require_t=", require_t, ", ps.T_max=", ps.T_max)
		if require_t == ps.T_max {
			ps.C = params[4]
			ps.T_store = require_t
			reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_propose" + ":" + "success"
		}
	case "require_commit":
		fmt.Println("阶段3 客户端请求commit")
		require_t, err := strconv.Atoi(params[3])
		if err != nil {
			fmt.Println("字符串转整型失败", err, params[3])
			return err
		}
		if require_t == ps.T_store {
			if params[4] == ps.C {
				reply.StrResult = ps.Host + ":" + ps.Port + ":" + "response_propose" + ":" + "run" + ":" + params[4]
			}
		}
	default:
		fmt.Println("命令不明,无法确认阶段,返回fail")
		reply.StrResult = ps.Host + ":" + ps.Port + ":" + "fail"
	}

	return nil
}

//启动服务
func startServer() {
	fmt.Println("start server...")
	ps := PaxosService{
		Host:    "127.0.0.1",
		Port:    "1234",
		T_max:   0,
		C:       "none",
		T_store: 0,
	}
	server := rpc.NewServer()
	server.Register(&ps)
	server.HandleHTTP(rpc.DefaultRPCPath, rpc.DefaultDebugPath)
	listener, e := net.Listen("tcp", ":1234")
	if e != nil {
		log.Fatal("listen error:", e)
	}
	fmt.Println("server running...")
	for {
		if conn, err := listener.Accept(); err != nil {
			log.Fatal("accept error: " + err.Error())
		} else {
			log.Printf("new connection established\n")
			go server.ServeCodec(jsonrpc.NewServerCodec(conn))
		}
	}
}

5.client.go

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc/jsonrpc"
)

//运行一次客户端,提交参数,获取结果
func runClient() {
	client, err := net.Dial("tcp", "127.0.0.1:1234")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	//第一阶段 请求票
	args := &Args{"127.0.0.1:1234:require_ticket:34"}
	var reply Reply
	c := jsonrpc.NewClient(client)
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段1 response:", reply.StrResult)

	//第二阶段 propose
	args = &Args{"127.0.0.1:1234:require_propose:34:akp"}
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段2 response:", reply.StrResult)

	//第3阶段 commit
	args = &Args{"127.0.0.1:1234:require_commit:34:akp"}
	err = c.Call("PaxosService.Process", args, &reply)
	if err != nil {
		log.Fatal("PaxosService error:", err)
	}
	fmt.Println("阶段3 response:", reply.StrResult)

}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值