死磕hyperledger fabric源码|Order节点启动

死磕hyperledger fabric源码|Order节点启动

文章及代码:https://github.com/blockchainGuide/

分支:v1.1.0

207f698ab0d8c69266fb6fe4c8f85def

Orderer节点启动流程

节点启动开始在/orderer/common/server/main.go:

func Main() {
    fullCmd := kingpin.MustParse(app.Parse(os.Args[1:]))  // 解析用户命令行
    ...
    conf, err := config.Load() //  加载orderer.yaml配置文件
    ...
    initializeLoggingLevel(conf) //初始化日志级别
    initializeLocalMsp(conf) //初始化本地MSP组件

    prettyPrintStruct(conf) // 打印配置信息
    Start(fullCmd, conf) // 启动Orderer排序服务器
}

主要做了以下几件事:

  • 解析用户命令行
  • 加载orderer.yaml配置文件
  • 初始化日志级别
  • 初始化本地MSP组件
  • 打印配置信息
  • 启动Orderer排序服务器

接下来将会展开的去讲上面比较重要的一些内容。

加载orderer.yaml配置文件

是由config.Load()开启的,进入到/orderer/common/localconfig/config.go/Load()

①:初始化viper

调用InitViper()函数设置配置文件路径,并默认在$FABRIC_CFG_PATH(如/etc/hyperledger/fabric)路径下查找配置文件,找不到文件时再依次查找当前目录、默认开发配置目录 ($GOPATH/src/github.com/hyperledger/fabric/sampleconfig)和系统默认配置路径 (/etc/hyperledger/fabric)。接着开启匹配系统环境变量的模式,即为Viper组件配置项(以.分割的格式)添加指定前缀“ORDERER_”,转换为大写字母形式,再将“.”替换为_。这样,Viper组件就能在查找配置项时,与以“ORDERER_”前缀开头的环境变量进行匹配,获取其在环境变量中的配置值。

config := viper.New()
    cf.InitViper(config, configName)
    config.SetEnvPrefix(Prefix)
    config.AutomaticEnv()
    replacer := strings.NewReplacer(".", "_")
    config.SetEnvKeyReplacer(replacer)

②:加载Orderer.yaml配置文件

err := config.ReadInConfig()

③:解析配置文件成Orderer配置对象

var uconf TopLevel
err = viperutil.EnhancedExactUnmarshal(config, &uconf)

此配置对象为TopLevel型,结构体如下:

type TopLevel struct {
    General    General    //通用配置对象
    FileLedger FileLedger //文本账本配置对象
    RAMLedger  RAMLedger  //RAM账本配置对象
    Kafka      Kafka      // Kafka共识组件配置对象
    Debug      Debug      // 调试信息配置对象
}

④:检查Orderer配置对象conf配置项并设置默认值

uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed()))

初始化日志与本地MSP组件

①:初始化日志

initializeLoggingLevel设置Orderer节点上的日志后端输出流、输出格 式与默认日志级别(INFO级别)。

func initializeLoggingLevel(conf *config.TopLevel) {
    flogging.InitBackend(flogging.SetFormat(conf.General.LogFormat), os.Stderr)
    flogging.InitFromSpec(conf.General.LogLevel)
}

②:初始化本地MSP组件

首先加载本地的MSP组件,根据MSP配置文件路径、BCCSP密码服务组件配置、MSP名称初始化本地MSP组件 。

func initializeLocalMsp(conf *config.TopLevel) {
    err := mspmgmt.LoadLocalMsp(conf.General.LocalMSPDir, conf.General.BCCSP, conf.General.LocalMSPID)
    ...
}

本地MSP组件默认使用bccspmsp类型对象。该类型的MSP组件是基于BCCSP组件提供密码套件服务的,封装了MSP组件(通常对应于一个组织)信任的相关证书列表(包含根CA证书、中间CA证书等)、MSP名称、签名者身份实体与管理员身份实体列表等。MSP组件的关键内容后面会有专门去讲解。

启动Orderer排序节点

启动函数在/orderer/common/server/main.go/Start()函数中,接下来一步步的解析:

①:创建本地MSP签名者实体 signer

signer := localmsp.NewSigner()       

②:初始化TLS认证的安全服务器配置项和gRPC服务器

本地gRPC服务器grpcServer默认端口为7050

serverConfig := initializeServerConfig(conf)       
grpcServer := initializeGrpcServer(conf, serverConfig) 

③:设置TLS连接认证的回调函数

tlsCallback := func(bundle *channelconfig.Bundle) {
        if grpcServer.MutualTLSRequired() { // 检测是否需要认证TLS客户端证书
            logger.Debug("Executing callback to update root CAs")
            updateTrustedRoots(grpcServer, caSupport, bundle) //执行回调函数更新根CA证书
        }
    }

④:创建多通道注册管理器

多通道注册管理器Registrar对象,用于注册Orderer节点上的所有通道(包括系统通道和应用通道),负责维护通道配置、账本等重要资源。

多通道注册管理器Registrar对象相当于Orderer节点上的“资源管理器”,为每个通道创建关联的共识组件链对象,负责交易排序、打包出块、提交账本以及通道管理等工作

manager := initializeMultichannelRegistrar(conf, signer, tlsCallback)
func initializeMultichannelRegistrar(conf *config.TopLevel, signer crypto.LocalSigner,
    callbacks ...func(bundle *channelconfig.Bundle)) *multichannel.Registrar {
    lf, _ := createLedgerFactory(conf) //创建通道的账本工厂对象
    if len(lf.ChainIDs()) == 0 {
        initializeBootstrapChannel(conf, lf) //初始化系统通道
    } else {
        logger.Info("Not bootstrapping because of existing chains")
    }

    //创建并设置共识组件字典
    consenters := make(map[string]consensus.Consenter)
    consenters["solo"] = solo.New()
    consenters["kafka"] = kafka.New(conf.Kafka) //// Kafka类型共识组件
    return multichannel.NewRegistrar(lf, consenters, signer, callbacks...)
}

4.1 创建通道的账本工厂对象

代码路径/orderer/common/server/util.go,大概做了以下几件事:

  • 获取Orderer节点上的区块账本存储目录ld,包括默认目录/var/hyperledger/production/orderer或临时目录中的子目录hyperledger-fabric-ordererledger+随机数后缀(默认目录不存在时使用)
  • 创建基于文件的区块账本工厂对象lf(fileLedgerFactory类型)
  • 在区块账本目录下建立以chains命名的子目录(/var/hyperledger/production/orderer/chains),由每个通道账本的区块数据存储对象负责在chains子目录下创建维护以通道ID(即链ID)命名的通道账本子目录,用于保存该通道账本的所有区块数据文件。其中,区块数据文件名都是以blockfile_num命名,num是6位区块文件编号,左侧不足位数用0补齐。

4.2 初始化系统通道

func initializeBootstrapChannel(conf *config.TopLevel, lf blockledger.Factory) {
    ...
    switch conf.General.GenesisMethod { // 分析创世区块的生成方式
    case "provisional": // 根据配置文件生成创世区块
        genesisBlock = encoder.New(genesisconfig.Load(conf.General.GenesisProfile)).GenesisBlockForChannel(conf.General.SystemChannel)
    case "file": // 根据创世区块文件生成创世区块
        genesisBlock = file.New(conf.General.GenesisFile).GenesisBlock()
    default:
    }

    chainID, err := utils.GetChainIDFromBlock(genesisBlock) // 从创世区块中解析获取通道ID
    ...
    gl, err := lf.GetOrCreate(chainID) // 创建系统通道的区块账本对象
    ...
    err = gl.Append(genesisBlock) // 添加区块到系统通道账本上
    ...
}

⑤:创建Orderer排序服务器

Orderer排序服务器,提供Orderer服务与管理所有通道资源及其账本、共识组件等。

server := NewServer(manager, signer, &conf.Debug, conf.General.Authentication.TimeWindow, mutualTLS)
func NewServer(r *multichannel.Registrar, _ crypto.LocalSigner, debug *localconfig.Debug, timeWindow time.Duration, mutualTLS bool) ab.AtomicBroadcastServer {
    s := &server{
        dh:        deliver.NewHandlerImpl(deliverSupport{Registrar: r}, timeWindow, mutualTLS),
        bh:        broadcast.NewHandlerImpl(broadcastSupport{Registrar: r}),
        debug:     debug,
        Registrar: r,
    }
    return s
}
  • bh:Broadcast服务处理句柄(deliverHandler类型)。该对象实现了Broadcast交易广播服务的Handle(srv ab.AtomicBroadcast_BroadcastServer)消息处理接口,负责接收客户端提交的普通交易消息与配置交易消息,并分别进行处理,过滤后转发给通道绑定的共识组件链对象进行处理;

  • dh:Deliver服务处理句柄(handlerImpl类型)。该对象实现了Deliver区块分发服务的Handle(srv*DeliverServer)消息处理接口,负责接收客户端提交的区块请求消息,从Orderer节点区块账本中读取指定的区块数据,并返回给请求节点。如果请求的指定区块还没有生成,则默认阻塞等待直到该区块创建和提交完毕;

  • Registrar:Orderer节点的多通道注册管理器(Registrar类型)。该对象封装了Orderer节点上所有通道的链支持对象字典chains、共识组件字典consenters、区块账本工厂对象ledgerFactory、系统通道链支持对象与ID、本地签名者实体signer等,用于管理通道配置、区块账本对象、共识组件等核心资源,相当于Orderer节点上的“资源管理器”。

⑥:解析执行子命令

  • start子命令:启动profile服务与Orderer排序服务器,支持go tool pprof命令查看与分析程序性能瓶颈
  • benchmark子命令:用于启动测试服务器
switch cmd { //分析命令类型
    case start.FullCommand(): // "start" command // start启动子命令
        logger.Infof("Starting %s", metadata.GetVersionInfo())
        initializeProfilingService(conf) // goroutine启动go profile服务
        ab.RegisterAtomicBroadcastServer(grpcServer.Server(), server)
        logger.Info("Beginning to serve requests")
        grpcServer.Start() // 启动gRPC服务器提供Orderer服务
    case benchmark.FullCommand(): // "benchmark" command // "benchmark" 测试用例子命令
        logger.Info("Starting orderer in benchmark mode")
        benchmarkServer := performance.GetBenchmarkServer()
        benchmarkServer.RegisterService(server)
        benchmarkServer.Start()
    }

参考

https://github.com/blockchainGuide/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容