在上篇文章: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")
}

现在就来分析下吧。首先,初始化变量initializedtrue,记录已经初始化了。然后为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()

这句话是干什么的? 这个就是担心在创建过程中,由于突发原因,导致创建中断了。就把数据内容恢复出来。

小结

本文主要分析了数据库的启动过程,还有很多没有看到,等后续的分析把。