GO-Blockchain搭建(二)

使用GO实验自己的区块链

本文是继上一篇论文之后,实现自己的区块链应用。在实现区块链应用之前,你还需要掌握知识点:并发编程。也许此时的你会感觉博主啰嗦,但是没办法,如果你没有相关的知识背景,实现的过程对于你,将是天花乱坠。当然,如果,你有相关的go开发经验,可以天国本章节。

go语言的天生高并发性

go语言的神奇之处,之一是其并发性。十秒解决的事情,go一秒钟搞定。go相较与其他的高级语言,比如java,python等,占用更少的内存。是高级语言的十分之一,这也是好多大企业,投来橄榄枝的重要原因。当然,他的特性远非如此。比如,针对web开发,有自己的框架:beego.可以很的进行相关系统的开发,而且,非常简洁,高效。

为了是blockchain代码可以实行本地的并发性。以及保证添加区块时候的顺序执行,引入并发编程以及锁的概念。

我们先附上主代码,并详细的解释代码中的作用:

func main() {
    
    
	err := godotenv.Load()
	if err != nil {
    
    
		log.Fatal(err)
	}

	go func() {
    
    
		t := time.Now()
		genesisBlock := Block{
    
    }
		genesisBlock = Block{
    
    0, t.String(), 0, calculateHash(genesisBlock), "",""}
		spew.Dump(genesisBlock)

		mutex.Lock()
		Blockchain = append(Blockchain, genesisBlock)
		mutex.Unlock()
	}()
	log.Fatal(run())

}

主函数中:

1.godotenv package的使用

这个包,主要是获得当前主函数所在目录下的".env"文件。然后配合使用的是os.getenv()函数。通过这个函数可以读取“.env”文件下面的相关配置。直接上代码,不废话:

package main

import (
  "fmt"
  "log"
  "os"
  "github.com/joho/godotenv"
)
func main() {
    
    
  err := godotenv.Load()
  if err != nil {
    
    
    log.Fatal(err)
  }

  fmt.Println("name: ", os.Getenv("name"))
  fmt.Println("age: ", os.Getenv("age"))
}

输出结果如下:
然后在可执行程序相同目录下,添加一个.env文件:

name = dj
age = 18

运行程序,输出:

name:  dj
age:  18

在本项目中主要是配置端口号。

2.go关键字的使用

go关键字的使用主要是为了并发的执行程序,这中执行,相当于开启了另一个线程。

go func() {
    
    
		t := time.Now()
		genesisBlock := Block{
    
    }
		genesisBlock = Block{
    
    0, t.String(), 0, calculateHash(genesisBlock), "",""}
		spew.Dump(genesisBlock)

		mutex.Lock()
		Blockchain = append(Blockchain, genesisBlock)
		mutex.Unlock()
	}()
	log.Fatal(run())

用法不是太难,go后面加个函数就可以了 当然上面的这种func也是声明一个函数,这中命名的方法有点想JavaScript语言。
在上述函数中主要是实现创世区块的创建。当然,创建好创世区块之后要加入Blockchain数组中,在这个过程中,因为我们开启的是并发编程,必须使用锁,不然,这个时候,创世区块将不会是第一个区块。锁的使用很简单:

 mutex.Lock()
//操作......
mutex.Unlock

当然,在之前一定要进行锁的声明:

var mutex = &sync.Mutex{
    
    }
3.现在还有一个函数:run

run()函数,我想大家一定清楚是干啥用的了。是为了创建一个web server。
下面我们来介绍run()函数,如果不了解web server的创建,建议转回第一节

func run() error {
    
    
	mux := makeMuxRouter()
	httpAddr := os.Getenv("PORT")
	log.Println("Listening on", httpAddr)
	s := &http.Server{
    
    
		Addr:           ":" + httpAddr,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}
	if err := s.ListenAndServe(); err != nil {
    
    
		return err
	}
	return nil
}

创建服务器函数的使用,首先当然是声明一个服务器。只不过,在这个函数体中,这种声明方式,更加的好。htt.Server{}。包含我们的端口,以及处理函数。好有超时函数等。

在声明之后,一定是声明一个监听服务:LinstenAndServer()。
这里的mux,可以很容易的看出来是一个处理函数。下面我们自然是对处理函数的讲解:

4. makeMuxRouter():路由处理函数
func makeMuxRouter() http.Handler {
    
    
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
	muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
	return muxRouter
}

其中函数,包括声明一个新的路由,以及对请求路由的处理:
其中的含handleGetBlockchian 是获得当前的区块链中的信息;handleWriteBlock函数是产生区块,也就是添加区块到Blockchain数组中,前面我们有提到过,这种数组是一个区块结构体数据。

5. handleGetBlockchain函数
func handleGetBlockchain(writer http.ResponseWriter, request *http.Request) {
    
    
	bytes, err := json.MarshalIndent(Blockchain, "", " ")
	if err != nil {
    
    
		http.Error(writer, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(writer, string(bytes))
}

这个函数的主要是在前端显示区块的信息,只不过,使用json进行了相应的处理。第一行,是获得当前区块数组中的区块信息,并进行返回到前端。

6.handleWriteBlock 函数
func handleWriteBlock(writer http.ResponseWriter, request *http.Request) {
    
    
	fmt.Print("wrr", writer)
	var m Mesage
	decoder := json.NewDecoder(request.Body)
	if err := decoder.Decode(&m); err != nil {
    
    
		respondWithJSON(writer, request, http.StatusBadRequest, request.Body)
	}
	defer request.Body.Close()
	newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM,m.Data )
	if err != nil {
    
    
		respondWithJSON(writer, request, http.StatusInternalServerError, m)
		return
	}
	if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
    
    
		newBlockchain := append(Blockchain, newBlock)
		replaceChain(newBlockchain)
		spew.Dump(Blockchain)
	}

	respondWithJSON(writer, request, http.StatusCreated, newBlock)

}

写入区块的函数产生区块:generateBlock,判断是不是合法区块isBlockValid。然后还有一个疑惑的地方是spew是干啥用的,其实,这是一中格式化输出方式,相当于fmt.Printf(),功能更加强大一些。
我们可以看如下实例:
package main

import (
	"github.com/davecgh/go-spew/spew"
)
func main() {
    
    
	i:=0
	s:="哈哈"
	spew.Dump(i,s)
}

输出如下:

(int) 0
(string) (len=6) "哈哈"

到这里已经介绍完关于go中创建区块链的实现。具体的代码,可以点击这里

运行,如何运行?

点击运行之后,会在run窗口下显示如下代码:
在这里插入图片描述
需要注意的是,我们是通过get请求获得去快速数据,而使用pos请求进行区块新信息的添加。
所以我们可以先打开浏览器输入:Localhost:8080
在这里插入图片描述
然后通过postman模拟进行post请求:
请注意请求的参数如何写:{“BPM”:1, “Data”:“zhangshan”}

在这里插入图片描述
在这里插入图片描述
到这里,我们已经实现了区块链的go语言的实现,并且我们引入了里一个参数Data,可以用俩存储各种想要的信息,比如,通过Data数据传输数据,这类似于记事本的操纵,并且这是不可逆的,无法篡改。

下面会进行更高级的学习P2P!感兴趣的敬请期待,欢迎关注。另外,博主很多文章是VIP,又需要的留言,就可以帮你打开权限。

猜你喜欢

转载自blog.csdn.net/YSS_33521/article/details/109284610