Fabric 1.4 orderer 服务启动流程

1.提要

orderer提供broadcast和deliver两个服务接口。orderer节点与各个peer节点通过grpc连接,orderer将所有peer节点通过broadcast发来的交易(Envelope格式,比如peer部署后的数据)按照配置的大小依次封装到一个个block中并写入orderer自己的账本中,然后供各个peer节点的gossip服务通过deliver来消费这个账本中的数据进行自身结点账本的同步。

2.初始化过程

先看看main函数。

// Main is the entry point of orderer process
func Main() {
    fullCmd := kingpin.MustParse(app.Parse(os.Args[1:]))

    // "version" command
    if fullCmd == version.FullCommand() {
        fmt.Println(metadata.GetVersionInfo())
        return
    }

    conf, err := localconfig.Load()
    if err != nil {
        logger.Error("failed to parse config: ", err)
        os.Exit(1)
    }
    initializeLogging()
    initializeLocalMsp(conf)

    prettyPrintStruct(conf)
    Start(fullCmd, conf)
}

从中可知 orderer服务命令行是通过kingpin来实现的,基本上只是简单使用了下,也只实现了3个命令:

  start*
    Start the orderer node
  version
    Show version information
  benchmark
    Run orderer in benchmark mode

并且从上述main函数可知,仅version有对应操作,而orderer 默认为orderer start。

启动流程为:

  1. 加载配置(orderer.yaml/Defaults/环境变量)
  2. 初始化log(log级别和log格式)
  3. 初始化本地msp
  4. 启动服务Start()

接下来主要关注第4步。前面基本上是配置初始化第过程。
查看一下start函数:

  1. 从配置文件启动块路径获取配置块及验证是否可作为配置块(系统通道第一个块)
  2. 集群相关初始化配置
  3. 判断是否是raft共识及使用的是最新配置块,如果是,则进行下列流程:
    1. 获取所有应用链及其创世区块块(discoverChannels)
    2. 根据orderer是否在应用链配置块的raft节点中分类(channelsToPull topull/nottopull)
    3. 创建所有的应用通道账本
    4. 获取topull应用通道的账本(从orderer处获取)
    5. 获取系统通道账本
      ```
      if clusterType && conf.General.GenesisMethod == "file" {
      r.replicateIfNeeded(bootstrapBlock)
      }

func (r *Replicator) ReplicateChains() []string {
var replicatedChains []string
channels := r.discoverChannels()
pullHints := r.channelsToPull(channels)
totalChannelCount := len(pullHints.channelsToPull) + len(pullHints.channelsNotToPull)

for _, channels := range [][]ChannelGenesisBlock{pullHints.channelsToPull, pullHints.channelsNotToPull} {
    for _, channel := range channels {
        ...
        r.appendBlock(gb, ledger, channel.ChannelName)
    }
}

for _, channel := range pullHints.channelsToPull {
    err := r.PullChannel(channel.ChannelName)
    ...
}

// Last, pull the system chain.
if err := r.PullChannel(r.SystemChannel); err != nil && err != ErrSkipped {
    r.Logger.Panicf("Failed pulling system channel: %v", err)
}
return replicatedChains

}

4. 启动及初始化必要模块
    1. 创建系统链
    ```
    // Are we bootstrapping?
    if len(lf.ChainIDs()) == 0 {
        initializeBootstrapChannel(genesisBlock, lf)
    } else {
        logger.Info("Not bootstrapping because of existing chains")
    }
    ```
    2. 多通道初始化(initializeMultichannelRegistrar)
        + 初始化registrar实例
        ```
        registrar := multichannel.NewRegistrar(lf, signer, metricsProvider, callbacks...)
        ```
        ```
        // Registrar serves as a point of access and control for the individual channel resources.
        type Registrar struct {
            lock   sync.RWMutex
            //当前所有通道的chain对象
            chains map[string]*ChainSupport
            //不同共识类型的consenter
            consenters         map[string]consensus.Consenter
            //Factory通过chainID检索或创建新的分类帐
            ledgerFactory      blockledger.Factory
            //签名相关
            signer             crypto.LocalSigner
            blockcutterMetrics *blockcutter.Metrics
            //系统链id
            systemChannelID    string
            //系统链chainSupport
            systemChannel      *ChainSupport
            //通道配置模版
            templator          msgprocessor.ChannelConfigTemplator
            callbacks          []channelconfig.BundleActor
        }
        ```
        + 初始化共识机制
        ```
        consenters["solo"] = solo.New()
        var kafkaMetrics *kafka.Metrics
        consenters["kafka"], kafkaMetrics = kafka.New(conf, metricsProvider, healthChecker, registrar)
        go kafkaMetrics.PollGoMetricsUntilStop(time.Minute, nil)
        if isClusterType(bootstrapBlock) {
            initializeEtcdraftConsenter(consenters, conf, lf, clusterDialer, bootstrapBlock, ri, srvConf, srv, registrar, metricsProvider)
        }
        ```
        + 启动orderer现存的链(系统链/应用链,通过读取链的目录查看现存链),为每条链实例化了ChainSupport对象,然后启动
         ```
        chain := newChainSupport(
                r,
                ledgerResources,
                r.consenters,
                r.signer,
                r.blockcutterMetrics,
            )
        ```
        ```
        for _, chainID := range existingChains {
                ...
                chain.start()
                ...
        }
        ```
    3. 启动GRPC服务
    server.go中的服务端对象实例server在main.go的main()中由server := NewServer(manager, signer)生成,使用ab.RegisterAtomicBroadcastServer(grpcServer.Server(), server)进行了注册,随后grpcServer.Start()启动起来。
    其主要的两个接口为:
    ```
    type AtomicBroadcastServer interface {
        Broadcast(AtomicBroadcast_BroadcastServer) error
        Deliver(AtomicBroadcast_DeliverServer) error
    }
    ```
    其接口的实现在:orderer/common/server/server.go

## 3.相关模块介绍
### 3.1 ChainSupport
提供支持chain相关操作的资源,既包含账本本身,也包含了账本用到的各种工具对象,如分割工具cutter,签名工具signer,最新配置在chain中的位置信息(lastConfig的值代表当前链中最新配置所在的block的编号,lastConfigSeq的值则代表当前链中最新配置消息自身的编号)等

type ChainSupport struct {
// 账本相关资源 包括账本的读写及配置的获取
ledgerResources
// 提供从客户端获取消息分类处理接口
msgprocessor.Processor
// 将区块写入磁盘
BlockWriter
// 链 提供对messages对处理方法
//This design allows for two primary flows
// 1. Messages are ordered into a stream, the stream is cut into blocks, the blocks are committed (solo, kafka)
// 2. Messages are cut into blocks, the blocks are ordered, then the blocks are committed (sbft)
consensus.Chain
// 广播消息接收器 提供切块方法
cutter blockcutter.Receiver
//签名
crypto.LocalSigner
// chains need to know if they are system or standard channel.
systemChannel bool
}

### 3.2 blockcutter模块
+ 块分割工具,用于分割block,具体为orderer/common/blockcutter/blockcutter.go中定义的receiver。一条一条消息数据在流水线上被传送到cutter处,cutter按照configtx.yaml中的配置,把一条条消息打包成一批消息,同时返回整理好的这批消息对应的committer集合,至此cutter的任务完成。

MaxMessageCount指定了block中最多存储的消息数量
AbsoluteMaxBytes指定了block最大的字节数
PreferredMaxBytes指定了一条消息的最优的最大字节数(blockcutter处理消息的过程中会努力使每一批消息尽量保持在这个值上)。
````

  1. 若一个Envelope的数据大小(Payload+签名)大于PreferredMaxBytes时,无论当前缓存如何,立即Cut;
  2. 若一个Envelope被要求单纯存储在一个block(即该消息对应的committer的Isolated()返回为true),要立即Cut
  3. 若一个Envelope的大小加上blockcutter已有的消息的大小之和大于PreferredMaxBytes时,要立即Cut;
  4. 若blockcutter当前缓存的消息数量大于MaxMessageCount了,要立即Cut。
  5. 由configtx.yaml中BatchTimeout配置项(默认2s)控制,当时间超过此值,chain启动的处理消息的流程中主动触发的Cut。

3.3 chain start()模块

主要是对消息进行处理,将交易消息传输给block cutter切成块及写入账本。不同的共识机制操作不同。(后续结合consenter模块一起详细介绍)

chain := newChainSupport(
                r,
                ledgerResources,
                r.consenters,
                r.signer,
                r.blockcutterMetrics,
            )
r.chains[chainID] = chain
chain.start()

3.4 consenter模块:

solo/kafka/etcdraft三种共识类型,用于序列化生产(即各个peer点传送来的Envelope)出来的消息。

参考:
https://blog.csdn.net/idsuf698987/article/details/78639203

菜鸟系列Fabric源码学习—orderer服务启动的更多相关文章

  1. 菜鸟系列Fabric源码学习 — peer节点启动

    Fabric 1.4 源码分析peer节点启动 peer模块采用cobra库来实现cli命令. Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具.Cobra同时也是一个程序, ...

  2. 菜鸟系列Fabric源码学习 — 区块同步

    Fabric 1.4 源码分析 区块同步 本文主要从源码层面介绍fabric peer同步区块过程,peer同步区块主要有2个过程: 1)peer组织的leader与orderer同步区块 2)pee ...

  3. 菜鸟系列Fabric源码学习 — committer记账节点

    Fabric 1.4 源码分析 committer记账节点 本文档主要介绍committer记账节点如何初始化的以及committer记账节点的功能及其实现. 1. 简介 记账节点负责验证交易和提交账 ...

  4. 菜鸟系列Fabric源码学习—创建通道

    通道创建源码解析 1. 与通道创建相关配置及操作命令 主要是configtx.yaml.通过应用通道的profile生成创建通道的配置文件. TwoOrgsChannel: Consortium: S ...

  5. 菜鸟系列Fabric源码学习 — MVCC验证

    Fabric 1.4 源码分析 MVCC验证 读本节文档之前建议先查看[Fabric 1.4 源码分析 committer记账节点]章节. 1. MVCC简介 Multi-Version Concur ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  7. [转]MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    本文转自:http://www.cnblogs.com/landeanfen/p/5989092.html 阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHan ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

随机推荐

  1. Leetcode Tags(13)Bit Manipulation

    一.477.汉明距离总和 输入: , , 输出: 解释: 在二进制表示中,4表示为0100,14表示为1110,2表示为0010.(这样表示是为了体现后四位之间关系) HammingDistance( ...

  2. Java基础(八)对象包装器与自动装箱

    1.对象包装器 有时候,需要将int这样的基本类型转换为对象.所有的基本类型都有一个与之对应的类.通常,这些类被称为包装器(wrapper). 这些对象包装类分别是:Integer.Long.Floa ...

  3. fenby C语言 P11

    else {} if {} #include int main() { int a=15; if(a%2==0) { printf("我是偶数!"); }else { printf ...

  4. MongoDB系列---用户及权限管理02

    MongoDB-——Privilege 学习大纲: 1.用户权限管理 2.用户操作 知识回顾:  本系列上一篇博文我们讲述了如何搭建环境以及配置我们的MongoDB,通过搭建环境后我们又学习了如何通过 ...

  5. 论RSA算法的重要性 -RSA 简介

    地球上最重要的算法 (这个说法似乎有点夸张了,但是当你了解了RSA算法后,就觉得不夸张了.) 如果没有 RSA 算法,现在的网络世界毫无安全可言,也不可能有现在的网上交易.上一篇文章 ssh 协议为什 ...

  6. Unity3d粒子特效:制作火焰效果

    效果 分析 真实的火焰效果,通常包括:火.火光.火星等组成部分,而火焰对周围环境的烘焙,可以通过灯光实现,如点光源. 针对火焰组成部分,我们可以创建对应的粒子系统组件,实现相应的效果,如下图所示: 1 ...

  7. 暑期集训20190729 字典序(dictionary)

    [题目描述] 你需要构造一个1~n的排列,使得它满足m个条件,每个条件形如(ai,bi),表示ai必须在bi前面. 在此基础上,你需要让1尽可能靠前,然后你需要让2尽可能靠前,然后是3,4,5,…,n ...

  8. CSPS模拟测试59

    这场考得我心态爆炸......... 开场T1只会$n^{2}$,然后发现bfs时每个点只需要被更新一次,其他的更新都是没用的. 也就是说,我们可以只更新还没被更新的点? 于是我先YY了一个链表,发现 ...

  9. UWP 带左右滚动按钮的横向ListView———仿NetFlix首页河的设计

    也是之前写的控件了,模仿NetFlix的河的设计. 大体要求如下: 1. 横向的ListView 2. 左右按钮,可以左右移动河卡片,左右的滚动条不可见 3. 左右按钮仅在鼠标Hover事件中可见 大 ...

  10. 【Java实践】Kettle从一次实验说起

    一,安装Kettle 1,关于简易安装Kettle 第一次接触kettle(以前只是听过罢了),摸索了几天,在mac源码安装失败,转而快速安装.在mac上安装最新版kettle并成功启动代码如下: ☁ ...