content/discuss/2019-07-17-global-variable-init.md
来源:『Go夜读』微信群
时间:2019-07-17
go程序运行初始化顺序如图:
其实以前在看书时就看到过,不过当时并没有什么体会,通过下面这个问题代码,可以让读者对这个初始化顺序更有体会。
工程结构如图:
config/config.go:
package config
import (
"encoding/json"
"io/ioutil"
"log"
)
var (
// 单例
E_config *Config
)
type Config struct {
MongodbUri string `json:"mongodbUri"`
MongodbConnectTimeout int `json:"mongodbConnectTimeout"`
LogBatchSize int `json:"logBatchSize"`
LogCommitTimeout int `json:"logCommitTimeout"`
NodeAddress string `json:"nodeAddress"`
GenesisBlockMsg string `json:"genesisBlockMsg"`
BlockChainDatabaseEngine string `json:"blockChainDatabaseEngine"`
BlockChainDatabasePathTemplate string `json:"blockChainDatabasePathTemplate"`
}
// 读取配置
func InitConfig(configFile string) (err error) {
var (
content []byte
config Config
)
// 读取配置文件,得到[]byte内容
if content, err = ioutil.ReadFile(configFile); err != nil {
log.Println("读取配置失败")
return
}
// 反序列化
if err = json.Unmarshal(content, &config); err != nil {
return
}
// 赋值单例
E_config = &config
//log.Print(E_config)
return
}
ledger/blockchain/chain.go:
package blockchain
import (
"log"
)
func InitChain() {
log.Println(dbEngine, dbPathTemp, nodeAddress)
}
ledger/blockchain/config.go:
package blockchain
import "../../config"
var (
// json配置
dbPathTemp = config.E_config.BlockChainDatabasePathTemplate
dbEngine = config.E_config.BlockChainDatabaseEngine
nodeAddress = config.E_config.NodeAddress
)
ledger/cli/cli.go:
package main
import (
"../../config"
"../blockchain"
"log"
)
func initConfig() {
const configFile = "config/enode.json"
if err := config.InitConfig(configFile); err != nil {
log.Panic(err)
}
}
func main() {
initConfig()
blockchain.InitChain()
}
我的最初想法是在blockchain下新建config.go,将config.E_config的元素赋给config.go/dbEngine等,将这些从外部包引用的配置参数集中放置,达到提升代码可读性的目的,但是这似乎不行:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x58 pc=0x4cd34d]
goroutine 1 [running]:
_/E_/GO/XProjects/EChain-error/ledger/blockchain.init.ializers()
E:/GO/XProjects/EChain-error/ledger/blockchain/config.go:13 +0x2d
Process finished with exit code 2
程序直接panic掉了,报错指向ledger/blockchain/config.go下dbEngine等变量的初始化,并指出产生了空指针调用。
但是看上去好像没有问题啊?main程序先执行配置初始化,得到E_config,再初始化Chain,Chain中使用E_config得到的元素值。
问题出在了变量初始化的顺序。根据go程序初始化顺序,一个程序在执行时,首先会去寻找import,然后会进行全局常量、全局变量然后是init()函数的初始化,最后才会执行到main()函数(也就是常说的程序入口)。
在我的代码中,dbEngine等变量就是blockchain中的包内全局变量,因此程序应该是先进行dbEngine等的初始化赋值,然而,此时尚未执行到main(),也就不会执行initConfig(),E_config也就仍然不存在。因此在dbEngine等元素赋值时我赋了一个根本不存在的值给到它们,这显然是无效的内存地址。
知道问题出在哪以后,就可以进行改正了,最简单的改正措施就是,将ledger/blockchain/config.go删掉,将dbEngine等变量初始化赋值语句放到InitChain中,使之由全局变量转为局部变量,这样,其初始化赋值操作就在InitConfig()之后了,也就不会产生空指针调用的错误。更正后代码及运行结果如下:
ledger/blockchain/chain.go:
package blockchain
import (
"../../config"
"log"
)
func InitChain() {
var (
// json配置
dbPathTemp = config.E_config.BlockChainDatabasePathTemplate
dbEngine = config.E_config.BlockChainDatabaseEngine
nodeAddress = config.E_config.NodeAddress
)
log.Println(dbEngine, dbPathTemp, nodeAddress)
}
运行ledger/cli/cli.go/main()的结果:
2019/07/18 11:20:13 badger ./tmp/blocks/%s/blocks_%s 127.0.0.1:9797
Process finished with exit code 0
这确实是从enode.json中读取的配置信息。说明现在没有问题了。
作为一个Go语言初学者及区块链初学者,在掌握了一些前置基础之后,我开始了EChain项目(一个基于区块链进行设备管理的物联网平台?)的开发(其实也是导师布置的任务)。在开发的过程中遇到了本文中提到的问题,非常感谢夜读群中Ryan等人热心的指导与帮助,在此谢过!另外希望EChain能够顺利完成!(来自菜鸟的祈祷~)