package main
import (
“os”
“fmt”
“net/http”
“io”
)
//声明nodeInfo节点,代表各个小国家
type nodeInfo struct {
//节点名称
id string
//节点路径
path string
//http响应,每个终端代表一个小国家
writer http.ResponseWriter
}
//创建map,存处各个国家的IP地址
//make()形成一个对象,可以生成地址便于操作
var nodeTable = make(map[string]string)
func main() {
//接收终端参数
//可以读多个参数
//for a := 1; a < 3; a++ {
userId := os.Args[1]
fmt.Println(userId)
//存储四个国家的IP地址
nodeTable = map[string]string {
“N0”:“localhost:1111”,
“N1”:“localhost:1112”,
“N2”:“localhost:1113”,
“N3”:“localhost:1114”,
}
//创建国家对象
//为结构体赋值指定个数的参数
node:=nodeInfo{id:userId,path:nodeTable[userId]}
//http协议的回调函数
//回调函数如对如下地址进行监听
//http://localhost:1111/req?warTime=1111
http.HandleFunc("/req",node.request)
//这3句一会儿再写
http.HandleFunc("/prePrepare",node.prePrepare)
http.HandleFunc("/prepare",node.prepare)
http.HandleFunc("/commit",node.commit)
//启动服务器,实现了一个简单的http服务器
//此时输入参数启动,会是一个监听的状态
//访问浏览器http://localhost:1111/req?warTime=ok,控制台可以打印信息
if err:=http.ListenAndServe(node.path,nil);err!=nil {
fmt.Println(err)
}
}
//当http服务器接收到网络请求,并且带/req,则回调request
//为nodeInfo类定义私有方法
//这里对照画的图说
func (node *nodeInfo)request(writer http.ResponseWriter,request *http.Request){
//该命令允许request请求参数,必须写
request.ParseForm()
//判断数组中是否有值
//这里warTime作战时间改其他也可以
if (len(request.Form[“warTime”])>0) {
node.writer = writer
fmt.Println(“主节点接收到的参数信息为”,request.Form[“warTime”][0])
//激活主节点后,向其他的节点发送广播
node.broadcast(request.Form[“warTime”][0],"/prePrepare")
}
}
//节点发送广播的方法
func(node *nodeInfo) broadcast(msg string ,path string ) {
fmt.Println(“广播”,path)
//遍历所有的节点
for nodeId, url :=range nodeTable {
//若遍历出的是节点自己,则跳出当次循环
if nodeId == node.id {
continue
}
//让当前节点外的节点做响应
//可以看图分析一下,继续写回调函数
//给遍历到的url发送get请求
http.Get(“http://”+url+path+"?warTime="+msg+"&nodeId="+node.id)
}
}
//处理接收到的数据和广播
func (node *nodeInfo)prePrepare(writer http.ResponseWriter,request *http.Request){
request.ParseForm()
fmt.Println(“接收到的广播”,request.Form[“warTime”][0])
//有值的情况下做广播
if len(request.Form[“warTime”])>0 {
node.broadcast(request.Form[“warTime”][0],"/prepare")
}
}
//接收子节点的广播
func (node *nodeInfo)prepare(writer http.ResponseWriter,request *http.Request){
request.ParseForm()
//打印
fmt.Println(“接收到子节点的广播”,request.Form[“warTime”][0])
//校验,可以画图说
//能接收到两条,则认为成功
if len(request.Form[“warTime”])>0 {
node.authentication(request)
}
}
//先定义一个会用到的Map
var authenticationNodeMap = make(map[string]string)
//定义标签
//若有两个返回,再置为OK
var authenticationSuceess = false
//校验拜占庭
func (node *nodeInfo)authentication(request *http.Request){
//这步第一次肯定能进去
if !authenticationSuceess {
if len(request.Form[“nodeId”])>0 {
//nodeId是拼接到broadcast方法请求中的
//若有值,则子节点是OK的
authenticationNodeMap[request.Form[“nodeId”][0]] = “OK”
//如果有两个国家节点正确的返回了结果
//这里是4节点,1主3从,有1个以上节点返回,则成功
//(n-1)/3的容错性
if len(authenticationNodeMap)>len(nodeTable)/3 {
authenticationSuceess = true
node.broadcast(request.Form[“warTime”][0],"/commit")
}
}
}
}
//返回响应成功
func (node *nodeInfo)commit(writer http.ResponseWriter,request *http.Request){
if writer != nil {
fmt.Println(“拜占庭校验成功”)
io.WriteString(node.writer,“ok”)
}
}
学院Go语言视频主页
https://edu.csdn.net/lecturer/1928
[清华团队带你实战区块链开发]
(https://ke.qq.com/course/344443?tuin=3d17195d)
扫码获取海量视频及源码 QQ群:721929980