Fabric的区块存储持久化,即区块的文件存储。
Fabric的文件存储是基于文件系统的,而不像前面所讲的世界状态和历史状态索引是基于levelDB数据库。
注:Fabric的区块索引还是存储在levelDB中,用于快速定位区块位于文件中的位置。
源码文件位置:common/ledger/blkstorage/fsblkstorage/fs_blockstore.go
// 该类是对接口BlockStore的实现,用于区块文件存储
type fsBlockStore struct {
id string
conf *Conf
// 区块文件管理对象[1]
fileMgr *blockfileMgr
}
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神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人