区块链教程Fabric1.0源代码分析blockfile区块文件存储1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# Fabric 1.0源代码笔记 之 blockfile(区块文件存储)
 
## 1、blockfile概述
 
blockfile,即Fabric区块链区块文件存储,默认目录/var/hyperledger/production/ledgersData/chains,含index和chains两个子目录。
其中index为索引目录,采用leveldb实现。而chains为各ledger的区块链文件,子目录以ledgerid为名,使用文件系统实现。
区块文件以blockfile_为前缀,最大大小默认64M。
 
blockfile,相关代码集中在common/ledger/blkstorage/fsblkstorage目录,目录结构如下:
 
* blockfile_mgr.go,blockfileMgr和checkpointInfo结构体及方法。
* block_stream.go,blockfileStream、blockStream、blockPlacementInfo结构体及方法。
* blockfile_rw.go,blockfileWriter和blockfileReader结构体及方法(blockfileReader未使用)。
* blockindex.go,index接口定义,index接口实现即blockIndex结构体及方法定义,以及blockIdxInfo、locPointer、fileLocPointer结构体及方法。
* blockfile_helper.go,定义了4个工具函数,constructCheckpointInfoFromBlockFiles、retrieveLastFileSuffix、isBlockFileName、getFileInfoOrPanic。
作用分别为:扫描最新的blockfile并重新构造检查点信息、获取最新的文件后缀、根据文件前缀判断是否为区块文件、获取文件状态信息。
* block_serialization.go,block序列化相关工具函数。
* blocks_itr.go,blocksItr结构体及方法。
 
## 2、Block结构体定、以及Block序列化
 
### 2.1、Block相关结构体
 
Block结构体:
 
```
```go
type Block struct {
    Header *BlockHeader //BlockHeader
    Data *BlockData //BlockData
    Metadata *BlockMetadata
}
 
func (m *Block) GetHeader() *BlockHeader //获取BlockHeader,即m.Header
func (m *Block) GetData() *BlockData //获取BlockData,即m.Data
func (m *Block) GetMetadata() *BlockMetadata //m.Metadata
//代码在protos/common/common.pb.go
```
 
BlockHeader结构体:
 
```go
type BlockHeader struct {
    Number uint64 //区块编号
    PreviousHash []byte //前一个区块哈希
    DataHash []byte //当前区块哈希
}
 
func (m *BlockHeader) GetNumber() uint64 //获取区块编号,即m.Number
func (m *BlockHeader) GetPreviousHash() []byte //获取前一个区块哈希,即m.PreviousHash
func (m *BlockHeader) GetDataHash() []byte //获取当前区块哈希,即m.DataHash
//代码在protos/common/common.pb.go
```
 
BlockData结构体:
 
```go
type BlockData struct {
    Data [][]byte //Data,存储交易信息
}
 
func (m *BlockData) GetData() [][]byte //获取Data,即m.Data
//代码在protos/common/common.pb.go
```
 
BlockMetadata结构体:
 
```go
type BlockMetadata struct {
    Metadata [][]byte //K/V均为[]byte格式
}
 
func (m *BlockMetadata) GetMetadata() [][]byte //m.Metadata
//代码在protos/common/common.pb.go
```
 
补充BlockMetadataIndex:
 
```go
type BlockMetadataIndex int32
 
const (
    BlockMetadataIndex_SIGNATURES BlockMetadataIndex = 0
    BlockMetadataIndex_LAST_CONFIG BlockMetadataIndex = 1
    BlockMetadataIndex_TRANSACTIONS_FILTER BlockMetadataIndex = 2
    BlockMetadataIndex_ORDERER BlockMetadataIndex = 3
)
```
 
### 2.2、Block序列化
 
serializedBlockInfo结构体定义及工具函数:
 
```go
type serializedBlockInfo struct {
    blockHeader *common.BlockHeader //BlockHeader
    txOffsets []*txindexInfo //交易索引信息
    metadata *common.BlockMetadata
}
 
type txindexInfo struct {
    txID string //交易ID
    loc *locPointer //文件指针
}
 
//序列化区块,返回序列化后字节,以及serializedBlockInfo(含BlockHeader和交易索引信息)
func serializeBlock(block *common.Block) ([]byte, *serializedBlockInfo, error)
//反序列化区块,构建Block结构体
func deserializeBlock(serializedBlockBytes []byte) (*common.Block, error)
//反序列化区块,并构造serializedBlockInfo
func extractSerializedBlockInfo(serializedBlockBytes []byte) (*serializedBlockInfo, error)
//序列化中添加BlockHeader,即Number、DataHash和PreviousHash
func addHeaderBytes(blockHeader *common.BlockHeader, buf *proto.Buffer) error
//序列化中添加BlockData,并从BlockData中解析txid,返回交易索引信息数组
func addDataBytes(blockData *common.BlockData, buf *proto.Buffer) ([]*txindexInfo, error)
//序列化中添加Metadata
func addMetadataBytes(blockMetadata *common.BlockMetadata, buf *proto.Buffer) error
//反序列化出BlockHeader
func extractHeader(buf *ledgerutil.Buffer) (*common.BlockHeader, error)
//反序列化出BlockData,并返回交易索引信息数组
func extractData(buf *ledgerutil.Buffer) (*common.BlockData, []*txindexInfo, error)
//反序列化出Metadata
func extractMetadata(buf *ledgerutil.Buffer) (*common.BlockMetadata, error)
//从BlockData中解析出交易ID
func extractTxID(txEnvelopBytes []byte) (string, error)
//代码在common/ledger/blkstorage/fsblkstorage/block_serialization.go
```
 
## 3、checkpointInfo结构体定义及方法
 
checkpointInfo,即检查点信息,结构体定义如下:
 
```go
type checkpointInfo struct {
    latestFileChunkSuffixNum int //最新的区块文件后缀,如blockfile_000000
    latestFileChunksize int //最新的区块文件大小
    isChainEmpty bool //是否空链
    lastBlockNumber uint64 //最新的区块编号
}
//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go
```
 
涉及方法如下:
 
```go
func (i *checkpointInfo) marshal() ([]byte, error) //checkpointInfo序列化
func (i *checkpointInfo) unmarshal(b []byte) error //checkpointInfo反序列化
func (i *checkpointInfo) String() string //转换为string
//代码在common/ledger/blkstorage/fsblkstorage/blockfile_mgr.go
```
 
## 4、blockfileStream相关结构体及方法
 
### 4.1、blockfileStream
 
blockfileStream定义如下:
 
```go
type blockfileStream struct {
    fileNum int //blockfile文件后缀
    file *os.File //os.File
    reader *bufio.Reader //bufio.Reader
    currentOffset int64 //当前偏移量
}
//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go
```
 
涉及方法如下:
 
```go
//构造blockfileStream
func newBlockfileStream(rootDir string, fileNum int, startOffset int64) (*blockfileStream, error) 
func (s *blockfileStream) nextBlockBytes() ([]byte, error) //下一个块,调取s.nextBlockBytesAndPlacementInfo()
//下一个块和位置信息
func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) 
func (s *blockfileStream) close() error //关闭blockfileStream
//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go
```
 
func (s *blockfileStream) nextBlockBytesAndPlacementInfo() ([]byte, *blockPlacementInfo, error) 代码如下:
 
```go
var lenBytes []byte
var err error
var fileInfo os.FileInfo
moreContentAvailable := true
 
fileInfo, err = s.file.Stat() //获取文件状态
remainingBytes := fileInfo.Size() - s.currentOffset //文件读取剩余字节
peekBytes := 8
if remainingBytes < int64(peekBytes) { //剩余字节小于8,按实际剩余字节,否则按8
    peekBytes = int(remainingBytes)
    moreContentAvailable = false
}
//存储形式:前n位存储block长度length,之后length位为实际block
lenBytes, err = s.reader.Peek(peekBytes) //Peek 返回缓存的一个切片,该切片引用缓存中前 peekBytes 个字节的数据
length, n := proto.DecodeVarint(lenBytes) //从切片中读取 varint 编码的整数,它返回整数和被消耗的字节数。
    err = s.reader.Discard(n) //丢弃存储block长度length的前n位
    blockBytes := make([]byte, length)
    _, err = io.ReadAtLeast(s.reader, blockBytes, int(length))
    blockPlacementInfo := &blockPlacementInfo{
        fileNum: s.fileNum,
        blockStartOffset: s.currentOffset,
        blockBytesOffset: s.currentOffset + int64(n)}
    s.currentOffset += int64(n) + int64(length)
    return blockBytes, blockPlacementInfo, nil
//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go
```
 
补充blockPlacementInfo:块位置信息
 
```go
type blockPlacementInfo struct {
    fileNum int //块文件后缀
    blockStartOffset int64 //n+length,n之前
    blockBytesOffset int64 //n+length,length之前
}
//代码在common/ledger/blkstorage/fsblkstorage/block_stream.go
```
 
## 5、blockfileWriter结构体定义及方法
 
```go
type blockfileWriter struct {
    filePath string //路径
    file *os.File //os.File
}
 
func newBlockfileWriter(filePath string) (*blockfileWriter, error) //构造blockfileWriter,并调用writer.open()
func (w *blockfileWriter) truncateFile(targetSize int) error //截取文件
func (w *blockfileWriter) append(b []byte, sync bool) error //追加文件
func (w *blockfileWriter) open() error //打开文件
func (w *blockfileWriter) close() error //关闭文件
//代码在common/ledger/blkstorage/fsblkstorage/blockfile_rw.go
```
 
## 6、blockIndex相关结构体及方法
 
### 6.1、index接口定义
 
```go
type index interface {
    getLastBlockIndexed() (uint64, error) //获取最后一个块索引(或编号)
    indexBlock(blockIdxInfo *blockIdxInfo) error //索引区块
    getBlockLocByHash(blockHash []byte) (*fileLocPointer, error) //根据区块哈希,获取文件区块指针
    getBlockLocByBlockNum(blockNum uint64) (*fileLocPointer, error) //根据区块编号,获取文件区块指针
    getTxLoc(txID string) (*fileLocPointer, error) //根据交易ID,获取文件交易指针
    getTXLocByBlockNumTranNum(blockNum uint64, tranNum uint64) (*fileLocPointer, error) //根据区块编号和交易编号,获取文件交易指针
    getBlockLocByTxID(txID string) (*fileLocPointer, error)//根据交易ID,获取文件区块指针
    getTxValidationCodeByTxID(txID string) (peer.TxValidationCode, error)//根据交易ID,获取交易验证代码
}
//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go
```
 
### 6.2、blockIndex结构体
 
blockIndex结构体定义如下:
 
```go
type blockIndex struct {
    indexItemsMap map[blkstorage.IndexableAttr]bool //index属性映射
    db *leveldbhelper.DBHandle //index leveldb操作
}
//代码在common/ledger/blkstorage/fsblkstorage/blockindex.go
```
 
补充IndexableAttr:
 
```go
const (
    IndexableAttrBlockNum = IndexableAttr("BlockNum")
    IndexableAttrBlockHash = IndexableAttr("BlockHash")
    IndexableAttrTxID = IndexableAttr("TxID")
    IndexableAttrBlockNumTranNum = IndexableAttr("BlockNumTranNum")
    IndexableAttrBlockTxID = IndexableAttr("BlockTxID")
    IndexableAttrTxValidationCode = IndexableAttr("TxValidationCode")
)
//代码在common/ledger/blkstorage/blockstorage.go


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31557831/viewspace-2215947/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/31557831/viewspace-2215947/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于这么大的文件,我们需要使用外部排序(External Sorting)算法,也叫多路归并排序(Multiway Merge Sort)。这种算法可以将文件分成多个小块,对每个小块进行排序,然后再将它们合并成一个有序的文件。 以下是一个基于Java的实现示例: ```java import java.io.*; import java.nio.charset.StandardCharsets; import java.util.*; public class ExternalSort { // 默认每个小块的大小为1G private static final long BLOCK_SIZE = 1024 * 1024 * 1024; /** * 对文件进行排序 * @param inputFile 输入文件路径 * @param outputFile 输出文件路径 * @throws IOException */ public static void sort(String inputFile, String outputFile) throws IOException { // 读取文件,将其分成多个小块 List<File> blocks = splitFile(inputFile); // 对每个小块进行排序 List<BufferedReader> readers = new ArrayList<>(); for (File block : blocks) { BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(block), StandardCharsets.UTF_8)); readers.add(reader); } PriorityQueue<String> heap = new PriorityQueue<>(Comparator.naturalOrder()); for (BufferedReader reader : readers) { String line = reader.readLine(); if (line != null) { heap.offer(line); } } // 将排序结果写入输出文件 BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)); while (!heap.isEmpty()) { String line = heap.poll(); writer.write(line); writer.newLine(); BufferedReader reader = null; for (int i = 0; i < readers.size(); i++) { reader = readers.get(i); if (line.equals(reader.readLine())) { break; } } if (reader != null) { String nextLine = reader.readLine(); if (nextLine != null) { heap.offer(nextLine); } } } writer.close(); // 关闭读取器 for (BufferedReader reader : readers) { reader.close(); } // 删除临时文件 for (File block : blocks) { block.delete(); } } /** * 将输入文件分成多个小块 * @param inputFile 输入文件路径 * @return 小块文件列表 * @throws IOException */ private static List<File> splitFile(String inputFile) throws IOException { List<File> blocks = new ArrayList<>(); BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile), StandardCharsets.UTF_8)); long blockSize = 0; int blockNum = 0; BufferedWriter writer = null; String line; while ((line = reader.readLine()) != null) { if (blockSize == 0 || blockSize + line.length() + 1 > BLOCK_SIZE) { if (writer != null) { writer.close(); } String blockFileName = inputFile + ".block" + blockNum++; File blockFile = new File(blockFileName); blocks.add(blockFile); writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(blockFile), StandardCharsets.UTF_8)); blockSize = 0; } writer.write(line); writer.newLine(); blockSize += line.length() + 1; } if (writer != null) { writer.close(); } reader.close(); return blocks; } } ``` 这个实现使用了一个 `splitFile` 方法,将输入文件分成多个小块。每个小块的大小为1G,可以根据实际情况进行调整。然后将每个小块读入内存,使用优先队列(`PriorityQueue`)进行排序,最后将排序结果写入输出文件。 注意,在排序过程需要处理小块的边界,以及如果有多个小块有相同的元素怎么处理。另外,在排序完成后需要关闭读取器和删除临时文件。 使用示例: ```java ExternalSort.sort("input.txt", "output.txt"); ``` 这个方法会将 `input.txt` 文件排序后输出到 `output.txt` 文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值