Michael.W谈hyperledger Fabric第22期-详细带读Fabric的源码7-orderer节点相关源码梳理

Michael.W谈hyperledger Fabric第22期-详细带读Fabric的源码7-orderer节点相关源码梳理

    • 1 模块功能顺序梳理
    • 2 具体脉络总结
      • 2.1 main函数
      • 2.2 多通道管理的Manage
      • 2.3 通道对象的代理ChainSupport
      • 2.4 solo排序的实现
      • 2.5 区块切割的标准
      • 2.6 为什么Fabric的共识只有排序?

1 模块功能顺序梳理

  1. main方法:orderer节点初始化的流程

  2. Manager:多通道的控制中枢。所有对通道的操作都需要从这里得到ChainSupport的实例。

  3. ChainSupport:通道对象的帮助接口,可以认为是通道的代理。一个通道对应一个ChainSupport实例化对象。

  4. 区块切割:重点看了Ordered方法的具体实现。这个方法的作用就是:判断当前堆积在orderer节点的交易(待处理交易)是否应当被切割到一个区块中。如果需要被切割,就调用Cut方法。切割后,待处理交易被清空

  5. solo共识:代码上看,solo模式将区块链这个分布式去中心化系统简化成一个中心系统。代码逻辑读几遍就能明白其中的逻辑,不是很复杂。

  6. 交易收集和区块扩散:起源于main.go中:

    	// 实例化服务实现
    	server := NewServer(manager, signer) 
    

    实现的服务类server包含两个接口实例,作用分别为交易收集区块扩散,即gRPC两个接口的实现。

    	type server struct {
    		// 交易收集
    		bh broadcast.Handler 
    		// 区块扩散
    		dh deliver.Handler 
    	}
    

2 具体脉络总结

2.1 main函数

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节点推送数据。

2.2 多通道管理的Manage

下面来说说多通道管理的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"

2.3 通道对象的代理ChainSupport

接下来是通道对象的代理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{}
	}

2.4 solo排序的实现

其实逻辑很简单。就是通过go语言的channel通道区块切割模块结合使用,实现了solo机制。
solo模式下直接影响区块打包的有两个因素,一个是区块打包的时间,一个是区块内对交易大小或数量的限制
时间影响因素比较简单,使用一个定时器实现,整体结构如下:
Michael.W谈hyperledger Fabric第22期-详细带读Fabric的源码7-orderer节点相关源码梳理_第1张图片
而且如果超时后,发现没有接收到新的交易,是不会进行区块打包的。
所以在Fabric的体系中是不会存在空区块的,这也是Fabric与其他区块链不同的地方(EOS中就存在空区块)

2.5 区块切割的标准

关于区块切割对区块的切割标准存在如下逻辑:

  1. 第一步:如果当前交易为配置交易或者信息大小大于配置文件中设置的区块推荐最大字节大小:
    如果此时存在待处理的数据,先切割待处理的数据,然后再切割当前的交易。然后退出。
    注:也就是说如果当前交易大于推荐大小,就不会成为待处理交易,而是直接打包。
  2. 第二步:如果待处理交易数据+本次交易数据>配置文件中设置的区块推荐最大字节大小:
    如果此时存在待处理的数据,先切割待处理的数据,然后将当前交易信息放到待处理交易集合中。
    ​ 注:待处理交易是存在内存中,并未写到区块里。
    ​ 如果将当前交易加入到待处理交易后,待处理交易数据数量>区块允许的最大数量:
    ​ 切割待交易数据。

这里面我提到两个参照值:区块推荐最大字节大小区块允许的最大数量
这两个值是在搭建网络中的configtx.yaml文件中设置的PreferredMaxBytes和MaxMessageCount:

我之前帖子《Michael.W谈hyperledger Fabric第5期-手动搭建Fabric网络之创世块/通道的生成一》有对这个yaml文件详细的解读,感兴趣的朋友可以去看看。

2.6 为什么Fabric的共识只有排序?

排序的目的是为了让分布式网络中各个节点的数据保持一致,那么必须保证每个peer节点收到的交易顺序都得是一致的。至于交易数据的正确性,背书策略可以保证。

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

你可能感兴趣的:(Fabric,Fabric)