在前面帖子《Michael.W谈hyperledger Fabric第18期-详细带读Fabric的源码3-通道对交易数据的操作封装》中提到的newChainSupport方法中:
cutter := blockcutter.NewReceiverImpl(ledgerResources.SharedConfig(), filters)
实例化了一个blockcutter包下的Receiver接口。来看看这个blockcutter包是干什么的?
type Receiver interface {
Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, committers [][]filter.Committer, validTx bool, pending bool)
Cut() ([]*cb.Envelope, []filter.Committer)
}
Receiver接口中声明了两个方法,Ordered方法指定打包区块的遵循的规则,Cut方法用来进行待处理数据的切割。
// receiver类
type receiver struct {
sharedConfigManager config.Orderer
filters *filter.RuleSet
// 待处理批次(里面是尚未提交的交易信息)
pendingBatch []*cb.Envelope
pendingBatchSizeBytes uint32
// 过滤器切片
pendingCommitters []filter.Committer
}
这个类实现了上面的Receiver接口。先看看关于两个方法的具体实现。
func (r *receiver) Ordered(msg *cb.Envelope) (messageBatches [][]*cb.Envelope, committerBatches [][]filter.Committer, validTx bool, pending bool) {
// 交易信息在被接收后需要再一次经过过滤器,防止配置发生改变。第一次过滤是发生在orderer节点刚刚接收到交易信息的时候。得到一个过滤结果对象committer
committer, err := r.filters.Apply(msg)
if err != nil {
logger.Debugf("Rejecting message: %s", err)
return
}
// 将交易信息标记为有效。该值为返回值。
validTx = true
// 计算交易体的大小:Payload长度+Signature长度。
messageSizeBytes := messageSizeBytes(msg)
// committer.Isolated():判断这笔交易是否自己独自享有一个区块(该笔交易为配置交易,独自成为一个区块)或者与其他交易信息混合在一起(本交易为普通交易)
// 也就是说当我们想对某一条通道的配置进行更改的时候,这个配置更改的交易是需要单独成区块的
// messageSizeBytes > r.sharedConfigManager.BatchSize().PreferredMaxBytes:当前交易信息大小大于在配置文件中设置的区块推荐最大字节大小
if committer.Isolated() || messageSizeBytes > r.sharedConfigManager.BatchSize().PreferredMaxBytes {
// 如果该交易为配置交易或体积大于配置设定,会进入if
if committer.Isolated() {
logger.Debugf("Found message which requested to be isolated, cutting into its own batch")
} else {
logger.Debugf("The current message, with %v bytes, is larger than the preferred batch size of %v bytes and will be isolated.", messageSizeBytes, r.sharedConfigManager.BatchSize().PreferredMaxBytes)
}
// 如果存在待处理的交易信息,先做处理待处理的
if len(r.pendingBatch) > 0 {
// 返还待处理的交易信息和过滤器,并将待处理批次的相关设置清空(就像把批次里的东西拿出来,然后这个批次就变成了个新的批次)
messageBatch, committerBatch := r.Cut()
// 将返回的交易信息加入到切片messageBatches(返回值)
messageBatches = append(messageBatches, messageBatch)
// 将返回的过滤器加入到切片committerBatches(返回值)
committerBatches = append(committerBatches, committerBatch)
}
// 将本次的交易信息加入到切片messageBatches(返回值)
messageBatches = append(messageBatches, []*cb.Envelope{msg})
// 将本次的过滤器加入到切片committerBatches(返回值)
committerBatches = append(committerBatches, []filter.Committer{committer})
// 结束方法
return
}
// 当该消息消息不需要隔离且当前交易信息大小不大于在配置文件中设置的区块推荐最大字节大小时
// 判断待处理的交易大小+本次交易信息大小是否大于在配置文件中设置的区块推荐最大字节大小
messageWillOverflowBatchSizeBytes := r.pendingBatchSizeBytes+messageSizeBytes > r.sharedConfigManager.BatchSize().PreferredMaxBytes
if messageWillOverflowBatchSizeBytes {
// 大于在配置文件中设置的区块推荐最大字节大小
logger.Debugf("The current message, with %v bytes, will overflow the pending batch of %v bytes.", messageSizeBytes, r.pendingBatchSizeBytes)
logger.Debugf("Pending batch would overflow if current message is added, cutting batch now.")
// 先处理待处理的交易信息,待处理队列清零
messageBatch, committerBatch := r.Cut()
messageBatches = append(messageBatches, messageBatch)
committerBatches = append(committerBatches, committerBatch)
}
// 将本次传入的交易msg放入待处理的队列
logger.Debugf("Enqueuing message into batch")
r.pendingBatch = append(r.pendingBatch, msg)
r.pendingBatchSizeBytes += messageSizeBytes
r.pendingCommitters = append(r.pendingCommitters, committer)
// 是否还有处理的数据的标志位(返回值)
pending = true
// 当待处理的交易条数 >= 区块中所能装载的最大交易数量
if uint32(len(r.pendingBatch)) >= r.sharedConfigManager.BatchSize().MaxMessageCount {
logger.Debugf("Batch size met, cutting batch")
// 切割待处理的交易信息,待处理队列清零
messageBatch, committerBatch := r.Cut()
messageBatches = append(messageBatches, messageBatch)
committerBatches = append(committerBatches, committerBatch)
pending = false
}
return
}
尽管这个Ordered的方法实现的很复杂,但是是遵循一个设计理念。就是本次的消息是加到待处理的消息队列中,而加入到返回值的消息都是从待处理的消息队列中取值。这样就完成了一个先进先出的队列结构。
func (r *receiver) Cut() ([]*cb.Envelope, []filter.Committer) {
// 将待处理的消息取出
batch := r.pendingBatch
// 待处理的消息清空
r.pendingBatch = nil
// 将待处理消息的过滤器结果取出
committers := r.pendingCommitters
// 将待处理消息的过滤器清空
r.pendingCommitters = nil
// 待处理消息大小置为0
r.pendingBatchSizeBytes = 0
// 返回被取出的待处理的消息和待处理消息的过滤器
return batch, committers
}
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人