本文是继上一篇论文之后,实现自己的区块链应用。在实现区块链应用之前,你还需要掌握知识点:并发编程。也许此时的你会感觉博主啰嗦,但是没办法,如果你没有相关的知识背景,实现的过程对于你,将是天花乱坠。当然,如果,你有相关的go开发经验,可以天国本章节。
go语言的神奇之处,之一是其并发性。十秒解决的事情,go一秒钟搞定。go相较与其他的高级语言,比如java,python等,占用更少的内存。是高级语言的十分之一,这也是好多大企业,投来橄榄枝的重要原因。当然,他的特性远非如此。比如,针对web开发,有自己的框架:beego.可以很的进行相关系统的开发,而且,非常简洁,高效。
我们先附上主代码,并详细的解释代码中的作用:
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())
}
主函数中:
这个包,主要是获得当前主函数所在目录下的".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
在本项目中主要是配置端口号。
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{}
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,可以很容易的看出来是一个处理函数。下面我们自然是对处理函数的讲解:
func makeMuxRouter() http.Handler {
muxRouter := mux.NewRouter()
muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
return muxRouter
}
其中函数,包括声明一个新的路由,以及对请求路由的处理: 其中的含handleGetBlockchian 是获得当前的区块链中的信息;handleWriteBlock函数是产生区块,也就是添加区块到Blockchain数组中,前面我们有提到过,这种数组是一个区块结构体数据。
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进行了相应的处理。第一行,是获得当前区块数组中的区块信息,并进行返回到前端。
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数据传输数据,这类似于记事本的操纵,并且这是不可逆的,无法篡改。