Michael.W谈hyperledger Fabric第28期-详细带读Fabric的源码13-区块的存储持久化

Michael.W谈hyperledger Fabric第28期-详细带读Fabric的源码13-区块的存储持久化

1 区块的存储持久化

Fabric的区块存储持久化,即区块的文件存储

Fabric的文件存储是基于文件系统的,而不像前面所讲的世界状态历史状态索引是基于levelDB数据库。

注:Fabric的区块索引还是存储在levelDB中,用于快速定位区块位于文件中的位置。

2 源码实现

源码文件位置:common/ledger/blkstorage/fsblkstorage/fs_blockstore.go

	// 该类是对接口BlockStore的实现,用于区块文件存储
	type fsBlockStore struct { 
		id      string
		conf    *Conf
	  	// 区块文件管理对象[1]
		fileMgr *blockfileMgr
	}
  • [1] 区块文件管理类,位于文件:common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go
    	type blockfileMgr struct {
    		rootDir           string
    		conf              *Conf
    		db                *leveldbhelper.DBHandle
    		index             index
    		cpInfo            *checkpointInfo
    		cpInfoCond        *sync.Cond
    		currentFileWriter *blockfileWriter
    		bcInfo            atomic.Value
    	}
    

接下来查看一下common/ledger/blkstorage/fsblkstorage/block_stream.go中的区块文件流的实现:

	// 用于处理区块文件的类
	type blockfileStream struct { 
		// 文件数量
	  	fileNum       int
	  	// 文件句柄
		file          *os.File
	  	// 读取数据的对象
		reader        *bufio.Reader
	  	// 当前偏移值
		currentOffset int64
	}
	
	// 用于处理区块的类
	type blockStream struct {
	  	// 文件根目录
		rootDir           string
	  	// 当前的文件编号
		currentFileNum    int
	  	// 查询结束的文件编号
		endFileNum        int
	  	// 当前的区块文件流
		currentFileStream *blockfileStream
	}
	
	// 创建一个区块文件流
	func newBlockfileStream(rootDir string, fileNum int, startOffset int64) (*blockfileStream, error) {
	  	// 生成区块文件路径:文件根目录+"/blockfile_"+fileNum
		filePath := deriveBlockfilePath(rootDir, fileNum)
		logger.Debugf("newBlockfileStream(): filePath=[%s], startOffset=[%d]", filePath, startOffset)
		var file *os.File
		var err error
	  	// 只读方式打开区块文件
		if file, err = os.OpenFile(filePath, os.O_RDONLY, 0600); err != nil {
			return nil, err
		}
		var newPosition int64
	  	// 根据传入参数startOffset,将文件指针移动到对应的偏移位置
		if newPosition, err = file.Seek(startOffset, 0); err != nil {
			return nil, err
		}
		if newPosition != startOffset {
	    // 如果传入的偏移量不满足区块文件要求
			panic(fmt.Sprintf("Could not seek file [%s] to given startOffset [%d]. New position = [%d]",
				filePath, startOffset, newPosition))
		}
	  	// 生成区块文件流对象
		s := &blockfileStream{fileNum, file, bufio.NewReader(file), startOffset}
		return s, nil
	}
	
	// 获取下一个区块的字节流数据
	func (s *blockfileStream) nextBlockBytes() ([]byte, error) {
		blockBytes, _, err := s.nextBlockBytesAndPlacementInfo()
		return blockBytes, err
	}
	
	// 关闭该区块文件流中的文件句柄
	func (s *blockfileStream) close() error {
		return s.file.Close()
	}

接着看一下区块流的实现:

	// 创建区块流对象
	func newBlockStream(rootDir string, startFileNum int, startOffset int64, endFileNum int) (*blockStream, error) {
	  	// 生成查询起始的区块文件流
		startFileStream, err := newBlockfileStream(rootDir, startFileNum, startOffset)
		if err != nil {
			return nil, err
		}
	  	// 返回对应的区块文件流
		return &blockStream{rootDir, startFileNum, endFileNum, startFileStream}, nil
	}
	
	// 转移到下个文件区块流
	func (s *blockStream) moveToNextBlockfileStream() error {
		var err error
	  	// 关闭当前的区块文件流
		if err = s.currentFileStream.close(); err != nil {
			return err
		}
	  	// 当前区块编号自加1
		s.currentFileNum++
	  	// 根据新的当前区块编号创建新的区块文件流
		if s.currentFileStream, err = newBlockfileStream(s.rootDir, s.currentFileNum, 0); err != nil {
			return err
		}
		return nil
	}

最后看一下common/ledger/blkstorage/fsblkstorage/ blocks_itr.go中的区块迭代器:

	// 用于迭代遍历区块
	type blocksItr struct { 
		mgr                  *blockfileMgr
	  	// 有效的最大的区块编号
		maxBlockNumAvailable uint64
	  	// 当前的区块编号
		blockNumToRetrieve   uint64
	  	// 区块流
		stream               *blockStream 
	  	// 关闭标记
		closeMarker          bool
	  	// 互斥锁
		closeMarkerLock      *sync.Mutex
	}
	
	// 创建区块迭代器对象
	func newBlockItr(mgr *blockfileMgr, startBlockNum uint64) *blocksItr {
		return &blocksItr{mgr, mgr.cpInfo.lastBlockNumber, startBlockNum, nil, false, &sync.Mutex{}}
	}
	
	// 迭代区块
	func (itr *blocksItr) Next() (ledger.QueryResult, error) {
	  	// 如果当前的区块编号大于有效的最大的区块编号
		if itr.maxBlockNumAvailable < itr.blockNumToRetrieve {
			itr.maxBlockNumAvailable = itr.waitForBlock(itr.blockNumToRetrieve)
		}
	  	// 加锁
		itr.closeMarkerLock.Lock()
	  	// 方法结束后解锁
		defer itr.closeMarkerLock.Unlock()
	  	// 如果关闭标记为true,返回nil
		if itr.closeMarker {
			return nil, nil
		}
	  	// 如果区块流为空
		if itr.stream == nil {
			logger.Debugf("Initializing block stream for iterator. itr.maxBlockNumAvailable=%d", itr.maxBlockNumAvailable)
	    	// 初始化区块迭代器中的区块流对象
			if err := itr.initStream(); err != nil {
				return nil, err
			}
		}
	  	// 获取下一个区块的字节流数据
		nextBlockBytes, err := itr.stream.nextBlockBytes()
		if err != nil {
			return nil, err
		}
	  	// 当前的区块编号加1
		itr.blockNumToRetrieve++
	 	// 返回反序列化后的区块数据
		return deserializeBlock(nextBlockBytes)
	}
	
	// 关闭区块迭代器对象
	func (itr *blocksItr) Close() {
		itr.closeMarkerLock.Lock()
		defer itr.closeMarkerLock.Unlock()
		itr.closeMarker = true
		itr.mgr.cpInfoCond.L.Lock()
		defer itr.mgr.cpInfoCond.L.Unlock()
		itr.mgr.cpInfoCond.Broadcast()
		if itr.stream != nil {
			itr.stream.close()
		}
	}

ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值