main方法:orderer节点初始化的流程
Manager:多通道的控制中枢。所有对通道的操作都需要从这里得到ChainSupport的实例。
ChainSupport:通道对象的帮助接口,可以认为是通道的代理。一个通道对应一个ChainSupport实例化对象。
区块切割:重点看了Ordered方法的具体实现。这个方法的作用就是:判断当前堆积在orderer节点的交易(待处理交易)是否应当被切割到一个区块中。如果需要被切割,就调用Cut方法。切割后,待处理交易被清空。
solo共识:代码上看,solo模式将区块链这个分布式去中心化系统简化成一个中心系统。代码逻辑读几遍就能明白其中的逻辑,不是很复杂。
交易收集和区块扩散:起源于main.go中:
// 实例化服务实现
server := NewServer(manager, signer)
实现的服务类server包含两个接口实例,作用分别为交易收集和区块扩散,即gRPC两个接口的实现。
type server struct {
// 交易收集
bh broadcast.Handler
// 区块扩散
dh deliver.Handler
}
orderer节点的main方法:
func main() {
// 打印版本信息
kingpin.Version("0.0.1")
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
case start.FullCommand():
logger.Infof("Starting %s", metadata.GetVersionInfo())
// 载入配置信息[1]。采用go语言常用的配置文件库viper,想了解可以去github上看一下viper的使用方法
conf := config.Load()
// 初始化日志级别。根据配置文件动态修改的。
initializeLoggingLevel(conf)
// 初始化profile。profile是go语言内置的一个可以观察程序运行的工具,可以通过http服务暴露出来。
initializeProfilingService(conf)
// 初始化grpc服务端
grpcServer := initializeGrpcServer(conf)
// 载入msp证书
initializeLocalMsp(conf)
// msp证书用于签名者实例化
signer := localmsp.NewSigner()
// 初始化多链manager
manager := initializeMultiChainManager(conf, signer)
// 实例化服务实现
server := NewServer(manager, signer)
// 绑定服务器 + 服务实现
ab.RegisterAtomicBroadcastServer(grpcServer.Server(), server)
logger.Info("Beginning to serve requests")
// 启动服务
grpcServer.Start()
case version.FullCommand():
fmt.Println(metadata.GetVersionInfo())
}
}
[1]Fabric中的日志实现的是一个同步的日志,如果将日志模式设定为DEBUG,可能会影响到Fabric网络的性能。在实际使用的时候,一定不要设定为DEBUG。
后面为了实例化gRPC的服务端,相继实例化了签名者signer、多通道的manager以及server,然后将server的实现对象注册到gRPC的服务端对象中。这样orderer节点就可以开始接收交易和进行区块扩散了。
注:我所说的orderer节点接收交易就是客户端向orderer节点提交交易的过程.
orderer节点对外的gRPC服务只有两个方法:
service AtomicBroadcast {
rpc Broadcast(stream common.Envelope) returns (stream BroadcastResponse) {}
rpc Deliver(stream common.Envelope) returns (stream DeliverResponse) {}
}
Broadcast方法用于接收客户端发过来的交易数据,而Deliver方法用于扩散区块。往往根据名字字面理解会正好理解反了。这个要注意,不要望文生义。
rpc里面分服务端和客户端,这里orderer节点正扮演的是服务端的角色。即便区块广播从字面理解是从orderer节点将数据区块发出去,但是在实际实现上还是要peer组织的主节点(客户端)来主动向服务端(orderer节点)发起调用申请,而不是orderer节点主动去向peer节点推送数据。
下面来说说多通道管理的Manager:
type Manager interface {
// 获取链对象。需要输入链ID,注意这地方返回的不是链对象,而是ChainSupport接口对象
GetChain(chainID string) (ChainSupport, bool)
// 获取系统通道名
SystemChannelID() string
// 生成新通道的配置。可能是新建,也可能是更新。
NewChannelConfig(envConfigUpdate *cb.Envelope) (configtxapi.Manager, error)
}
Manager接口中只包含三个方法。比如:可以通过Manager实现类对象来新创建一个通道、更新多通道中的某一个通道的配置或获取系统通道的ID。
在处理每一条通道的时候需要获得用来管理某一条通道的ChainSupport对象。
系统通道的作用是:1.创建其他通道;2.权限验证。网络中的第一条通道必须是系统通道。如果没有对系统通道做特殊设置的话,系统通道的名字就是"testChannelID"
接下来是通道对象的代理ChainSupport接口:
从整个逻辑上看,ChainSupport接口是位于Manager接口和Chain接口之间的一个代理人。大多数对ChainSupport接口的操作都会落实到:读取通道对象,即ledger.Reader或者是Chain对象。
type ChainSupport interface {
// 策略配置
PolicyManager() policies.Manager
// 账本读取
Reader() ledger.Reader
// 是否有错误发生?
Errored() <-chan struct{}
// 处理交易输入
broadcast.Support
// 共识机制帮助方法
ConsenterSupport
// 当前通道配置状态的序列号(更次有更新就自加1)
Sequence() uint64
// 交易转换(配置交易)
ProposeConfigUpdate(env *cb.Envelope) (*cb.ConfigEnvelope, error)
}
下面来看看chain对象,即常说的共识机制:位于orderer/solo/consensus.go文件中
type chain struct {
support multichain.ConsenterSupport
sendChan chan *cb.Envelope
exitChan chan struct{}
}
其实逻辑很简单。就是通过go语言的channel通道和区块切割模块结合使用,实现了solo机制。
solo模式下直接影响区块打包的有两个因素,一个是区块打包的时间,一个是区块内对交易大小或数量的限制。
时间影响因素比较简单,使用一个定时器实现,整体结构如下:
而且如果超时后,发现没有接收到新的交易,是不会进行区块打包的。
所以在Fabric的体系中是不会存在空区块的,这也是Fabric与其他区块链不同的地方(EOS中就存在空区块)
关于区块切割对区块的切割标准存在如下逻辑:
这里面我提到两个参照值:区块推荐最大字节大小和区块允许的最大数量。
这两个值是在搭建网络中的configtx.yaml文件中设置的PreferredMaxBytes和MaxMessageCount:
我之前帖子《Michael.W谈hyperledger Fabric第5期-手动搭建Fabric网络之创世块/通道的生成一》有对这个yaml文件详细的解读,感兴趣的朋友可以去看看。
排序的目的是为了让分布式网络中各个节点的数据保持一致,那么必须保证每个peer节点收到的交易顺序都得是一致的。至于交易数据的正确性,背书策略可以保证。
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人