【注意】最后更新于 November 21, 2017,文中内容可能已过时,请谨慎使用。
在上篇文章:fabric启动II中,我们大体上说到了peer的大体启动流程,但是并没有深入进去,现在我们追随node start
的脚步继续探索。
前言
这篇不是侧重于fabric的启动,而是数据库的创建与应用,因为内容太多了。启动到时候再分一篇文章来说。
在peer/node/start.go
中是这么创建对应的start指令
1
2
3
4
5
6
7
8
|
var nodeStartCmd = &cobra.Command{
Use: "start",
Short: "Starts the node.",
Long: `Starts a node that interacts with the network.`,
RunE: func(cmd *cobra.Command, args []string) error {
return serve(args)
},
}
|
可以看到,会调用serve函数,这个函数第二句话就是
1
|
ledgermgmt.Initialize()
|
这个就得进去看了。
ledger
通过import很容易找到对应的文件是core/ledger/ledgermgmt/ledger_mgmt.go
文件,只执行了一次,使用了锁进行初始化。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
func initialize() {
logger.Info("Initializing ledger mgmt")
lock.Lock()
defer lock.Unlock()
initialized = true
openedLedgers = make(map[string]ledger.PeerLedger)
provider, err := kvledger.NewProvider()
if err != nil {
panic(fmt.Errorf("Error in instantiating ledger provider: %s", err))
}
ledgerProvider = provider
logger.Info("ledger mgmt initialized")
}
|
现在就来分析下吧。首先,初始化变量initialized
为true
,记录已经初始化了。然后为openedLedgers
创建了一个map映射,应该是账本美容与什么的映射关系。最后创建了一个provider,提供对数据库的操作。
重点来了!!!
除了provider都是直接在这初始化了,看看provider是怎么创建的呗!
provider
这段代码比较长,分开来看
函数体是
1
|
func NewProvider() (ledger.PeerLedgerProvider, error)
|
不需要参数,返回一个PeerLedgerProvider是一个接口
1
2
3
4
5
6
7
8
9
10
11
12
|
type PeerLedgerProvider interface {
Create(genesisBlock *common.Block) (PeerLedger, error)
Open(ledgerID string) (PeerLedger, error)
Exists(ledgerID string) (bool, error)
List() ([]string, error)
Close()
}
|
实现这几个方法就可以。很好,根据最后面的return,返回的就是本文件中的Provider
1
2
3
4
5
6
|
type Provider struct {
idStore *idStore
blockStoreProvider blkstorage.BlockStoreProvider
vdbProvider statedb.VersionedDBProvider
historydbProvider historydb.HistoryDBProvider
}
|
这里面的都是对应的变量,对不同的数据库进行操作。
1. idSore
是记录的账本的id,对不同的数据库操作都需要输入ID才能找到
2. blockStoreProvider
就是咱们的核心了,区块的数据库操作
3. vdbProvider
是fabric相关的状态数据库
4. historydbProvider
是历史状态数据库
idStore
函数内容第一句是
1
|
idStore := openIDStore(ledgerconfig.GetLedgerProviderPath())
|
就是创建这个Id数据库。
先看下提供的这个GetLedgerProviderPath
。
最终这计划就是执行的
1
2
3
4
|
func GetRootPath() string {
sysPath := config.GetPath("peer.fileSystemPath")
return filepath.Join(sysPath, "ledgersData")
}
|
我们在上一篇中说过了viper的相关内容。会最后到core.yaml文件中查找peer.fileSystemPath
。但是直接查找是找不到的,为何?
因为viper在初始化的时候设置了SetEnvPrefix(cmdRoot)
会自动追加前缀,找到了文件路径是/var/hyperledger/production/ledgerData/ledgerProvider
这个路径下面。
blockStoreProvider
这个首先创建了一堆属性
1
2
3
4
5
6
7
8
|
attrsToIndex := []blkstorage.IndexableAttr{
blkstorage.IndexableAttrBlockHash,
blkstorage.IndexableAttrBlockNum,
blkstorage.IndexableAttrTxID,
blkstorage.IndexableAttrBlockNumTranNum,
blkstorage.IndexableAttrBlockTxID,
blkstorage.IndexableAttrTxValidationCode,
}
|
这几个变量都是写死的变量内容。就是这几个内容
1
2
3
4
5
6
7
8
9
10
11
12
|
// IndexableAttr represents an indexable attribute
type IndexableAttr string
// constants for indexable attributes
const (
IndexableAttrBlockNum = IndexableAttr("BlockNum")
IndexableAttrBlockHash = IndexableAttr("BlockHash")
IndexableAttrTxID = IndexableAttr("TxID")
IndexableAttrBlockNumTranNum = IndexableAttr("BlockNumTranNum")
IndexableAttrBlockTxID = IndexableAttr("BlockTxID")
IndexableAttrTxValidationCode = IndexableAttr("TxValidationCode")
)
|
就是写到数据库的字段吧。没有太多好说的。
然后就是创建具体的provider
了。
1
2
3
4
|
indexConfig := &blkstorage.IndexConfig{AttrsToIndex: attrsToIndex}
blockStoreProvider := fsblkstorage.NewProvider(
fsblkstorage.NewConf(ledgerconfig.GetBlockStorePath(), ledgerconfig.GetMaxBlockfileSize()),
indexConfig)
|
乍一看,很乱。实际一看 ,还是很乱。
indexConfig
就是一个结构体变量,存储这些字段。
下面这一句是创建具体的provider了。NewConf
传入了两个参数,一个是存储的路径,讨论idStore
的时候就说过方法了,区块的大小可以查看到是64M。所以啊!你想往区块写太多数据还是做不到的。
具体的provider
是这个内容
1
2
3
4
5
|
type FsBlockstoreProvider struct {
conf *Conf
indexConfig *blkstorage.IndexConfig
leveldbProvider *leveldbhelper.Provider
}
|
后面我们也会发现,剩下的provider也会有这个leveldbProvider
.实质上就是一个对应自己的内容+操作数据库。
vdbProvider
这个是状态数据库
1
2
3
4
5
6
7
8
9
10
11
12
|
var vdbProvider statedb.VersionedDBProvider
if !ledgerconfig.IsCouchDBEnabled() {
logger.Debug("Constructing leveldb VersionedDBProvider")
vdbProvider = stateleveldb.NewVersionedDBProvider()
} else {
logger.Debug("Constructing CouchDB VersionedDBProvider")
var err error
vdbProvider, err = statecouchdb.NewVersionedDBProvider()
if err != nil {
return nil, err
}
}
|
提供了两种存储结构,一个是couchDB,一个是levelDB,我们就继续用levelDB进行分析。
1
2
3
4
5
6
|
func NewVersionedDBProvider() *VersionedDBProvider {
dbPath := ledgerconfig.GetStateLevelDBPath()
logger.Debugf("constructing VersionedDBProvider dbPath=%s", dbPath)
dbProvider := leveldbhelper.NewProvider(&leveldbhelper.Conf{DBPath: dbPath})
return &VersionedDBProvider{dbProvider}
}
|
这个就一个内容,对levelDB的操作提供者。
historydbProvider
这个内容跟vdbProvider
内容很像,都是从配置文件中读取数据库的存储位置,然后生成操作对象,返还回去。没有太多可以分析的。
1
2
3
4
5
6
|
func NewHistoryDBProvider() *HistoryDBProvider {
dbPath := ledgerconfig.GetHistoryLevelDBPath()
logger.Debugf("constructing HistoryDBProvider dbPath=%s", dbPath)
dbProvider := leveldbhelper.NewProvider(&leveldbhelper.Conf{DBPath: dbPath})
return &HistoryDBProvider{dbProvider}
}
|
生成对象
以上四个类型的数据库都有了,就是合并到provider
中,然后返回就好了。
1
|
provider := &Provider{idStore, blockStoreProvider, vdbProvider, historydbProvider}
|
等等!还有一句话
1
|
provider.recoverUnderConstructionLedger()
|
这句话是干什么的?
这个就是担心在创建过程中,由于突发原因,导致创建中断了。就把数据内容恢复出来。
小结
本文主要分析了数据库的启动过程,还有很多没有看到,等后续的分析把。