菜鸟系列Fabric源码学习 — peer节点启动
Fabric 1.4 源码分析peer节点启动
peer模块采用cobra库来实现cli命令。
Cobra提供简单的接口来创建强大的现代化CLI接口,比如git与go工具。Cobra同时也是一个程序, 用于创建CLI程序
peer支持的命令如下所示:
Usage:
peer [command]
Available Commands:
chaincode Operate a chaincode: install|instantiate|invoke|package|query|signpackage|upgrade|list.
channel Operate a channel: create|fetch|join|list|update|signconfigtx|getinfo.
help Help about any command
logging Log levels: getlevel|setlevel|revertlevels.
node Operate a peer node: start|status.
version Print fabric peer version.
Flags:
-h, --help help for peer
--logging-level string Default logging level and overrides, see core.yaml for full syntax
通过peer 的docker-compose文件可知,peer启动命令为peer node start。从下列代码可知,peer启动时调用serve()接口。
var nodeStartCmd = &cobra.Command{
Use: "start",
Short: "Starts the node.",
Long: `Starts a node that interacts with the network.`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return fmt.Errorf("trailing args detected")
}
// Parsing of the command line is done so silence cmd usage
cmd.SilenceUsage = true
return serve(args)
},
}
接下来深入分析serve()接口。
func serve(args []string) error {
// currently the peer only works with the standard MSP
// because in certain scenarios the MSP has to make sure
// that from a single credential you only have a single 'identity'.
// Idemix does not support this *YET* but it can be easily
// fixed to support it. For now, we just make sure that
// the peer only comes up with the standard MSP
// 当前peer启动时只支持标准MSP即Fabric。
mspType := mgmt.GetLocalMSP().GetType()
if mspType != msp.FABRIC {
panic("Unsupported msp type " + msp.ProviderTypeToString(mspType))
}
// Trace RPCs with the golang.org/x/net/trace package. This was moved out of
// the deliver service connection factory as it has process wide implications
// and was racy with respect to initialization of gRPC clients and servers.
grpc.EnableTracing = true
logger.Infof("Starting %s", version.GetInfo())
//startup aclmgmt with default ACL providers (resource based and default 1.0 policies based).
//Users can pass in their own ACLProvider to RegisterACLProvider (currently unit tests do this)
// 创建ACL提供者 ACL访问控制列表
aclProvider := aclmgmt.NewACLProvider(
aclmgmt.ResourceGetter(peer.GetStableChannelConfig),
)
// 平台注册
pr := platforms.NewRegistry(
&golang.Platform{},
&node.Platform{},
&java.Platform{},
&car.Platform{},
)
// 定义部署链码提供者
deployedCCInfoProvider := &lscc.DeployedCCInfoProvider{}
DeployedCCInfoProvider实现了DeployedChaincodeInfoProvider。
DeployedChaincodeInfoProvider是ledger用于构建集合配置历史记录的依赖项
LSCC模块应该为这个依赖项提供实现
type DeployedChaincodeInfoProvider interface {
Namespaces() []string //命名空间
UpdatedChaincodes(stateUpdates map[string][]*kvrwset.KVWrite) ([]*ChaincodeLifecycleInfo, error) // 保存更新的链码
ChaincodeInfo(chaincodeName string, qe SimpleQueryExecutor) (*DeployedChaincodeInfo, error) // 保存链码信息
CollectionInfo(chaincodeName, collectionName string, qe SimpleQueryExecutor) (*common.StaticCollectionConfig, error) // 链码集合信息
}
初始化账本资源ledgermgmt.Initialize
// 获取通道MSP管理员。如果不存在则创建
identityDeserializerFactory := func(chainID string) msp.IdentityDeserializer {
return mgmt.GetManagerForChain(chainID)
}
// peer 初始化
// 保存 peer 一些基本信息 ListenAddress TLS
opsSystem := newOperationsSystem()
// 监听 ListenAddress
err := opsSystem.Start()
if err != nil {
return errors.WithMessage(err, "failed to initialize operations subystems")
}
defer opsSystem.Stop()
metricsProvider := opsSystem.Provider
logObserver := floggingmetrics.NewObserver(metricsProvider)
flogging.Global.SetObserver(logObserver)
// 实例化私密数据成员membershipInfoProvider 用来判断peer是否在某个私密数据的集合中
membershipInfoProvider := privdata.NewMembershipInfoProvider(createSelfSignedData(), identityDeserializerFactory)
//initialize resource management exit
// 初始化账本资源 将前面实例化的对象都进行赋值
ledgermgmt.Initialize(
&ledgermgmt.Initializer{
CustomTxProcessors: peer.ConfigTxProcessors,
PlatformRegistry: pr,
DeployedChaincodeInfoProvider: deployedCCInfoProvider,
MembershipInfoProvider: membershipInfoProvider,
MetricsProvider: metricsProvider,
HealthCheckRegistry: opsSystem,
},
)
初始化peer GRPC服务配置
// Parameter overrides must be processed before any parameters are
// cached. Failures to cache cause the server to terminate immediately.
// 判断链码是否时开发者模式
if chaincodeDevMode {
logger.Info("Running in chaincode development mode")
logger.Info("Disable loading validity system chaincode")
viper.Set("chaincode.mode", chaincode.DevModeUserRunsChaincode)
}
// 缓存peer地址getLocalAddress address:port
if err := peer.CacheConfiguration(); err != nil {
return err
}
// 获取peer endpoint,没有则调用CacheConfiguration接口
peerEndpoint, err := peer.GetPeerEndpoint()
if err != nil {
err = fmt.Errorf("Failed to get Peer Endpoint: %s", err)
return err
}
// 获取peer Host
peerHost, _, err := net.SplitHostPort(peerEndpoint.Address)
if err != nil {
return fmt.Errorf("peer address is not in the format of host:port: %v", err)
}
listenAddr := viper.GetString("peer.listenAddress")
// 获取peer grpc相关配置 主要是TLS设置和心跳设置
serverConfig, err := peer.GetServerConfig()
if err != nil {
logger.Fatalf("Error loading secure config for peer (%s)", err)
}
// 设置GRPC最大并发2500
throttle := comm.NewThrottle(grpcMaxConcurrency)
// GRPC server的一些配置
serverConfig.Logger = flogging.MustGetLogger("core.comm").With("server", "PeerServer")
serverConfig.MetricsProvider = metricsProvider
serverConfig.UnaryInterceptors = append(
serverConfig.UnaryInterceptors,
grpcmetrics.UnaryServerInterceptor(grpcmetrics.NewUnaryMetrics(metricsProvider)),
grpclogging.UnaryServerInterceptor(flogging.MustGetLogger("comm.grpc.server").Zap()),
throttle.UnaryServerIntercptor,
)
serverConfig.StreamInterceptors = append(
serverConfig.StreamInterceptors,
grpcmetrics.StreamServerInterceptor(grpcmetrics.NewStreamMetrics(metricsProvider)),
grpclogging.StreamServerInterceptor(flogging.MustGetLogger("comm.grpc.server").Zap()),
throttle.StreamServerInterceptor,
)
将GRPC相关配置及Address传入创建GRPC服务器
peerServer, err := peer.NewPeerServer(listenAddr, serverConfig)
if err != nil {
logger.Fatalf("Failed to create peer server (%s)", err)
}
TLS及策略相关
// TLS相关配置
if serverConfig.SecOpts.UseTLS {
logger.Info("Starting peer with TLS enabled")
// set up credential support
cs := comm.GetCredentialSupport()
roots, err := peer.GetServerRootCAs()
if err != nil {
logger.Fatalf("Failed to set TLS server root CAs: %s", err)
}
cs.ServerRootCAs = roots
// set the cert to use if client auth is requested by remote endpoints
clientCert, err := peer.GetClientCertificate()
if err != nil {
logger.Fatalf("Failed to set TLS client certificate: %s", err)
}
comm.GetCredentialSupport().SetClientCertificate(clientCert)
}
mutualTLS := serverConfig.SecOpts.UseTLS && serverConfig.SecOpts.RequireClientCert
// 策略校验
policyCheckerProvider := func(resourceName string) deliver.PolicyCheckerFunc {
return func(env *cb.Envelope, channelID string) error {
return aclProvider.CheckACL(resourceName, channelID, env)
}
}
创建deliver server 传输区块及过滤区块
abServer := peer.NewDeliverEventsServer(mutualTLS, policyCheckerProvider, &peer.DeliverChainManager{}, metricsProvider)
pb.RegisterDeliverServer(peerServer.Server(), abServer)
初始化链码服务
startChaincodeServer将完成与链代码相关的初始化,包括:
1)设置本地链代码安装路径
2)创建特定链代码的CA
3)启动特定链代码的gRPC监听服务
// Initialize chaincode service
chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr, opsSystem)
logger.Debugf("Running peer")
注册背书服务,gossip组件初始化等操作
// Start the Admin server
startAdminServer(listenAddr, peerServer.Server(), metricsProvider)
privDataDist := func(channel string, txID string, privateData *transientstore.TxPvtReadWriteSetWithConfigInfo, blkHt uint64) error {
// 分发私有数据到其他节点
return service.GetGossipService().DistributePrivateData(channel, txID, privateData, blkHt)
}
// 获取本地签名
signingIdentity := mgmt.GetLocalSigningIdentityOrPanic()
serializedIdentity, err := signingIdentity.Serialize()
if err != nil {
logger.Panicf("Failed serializing self identity: %v", err)
}
libConf := library.Config{}
if err = viperutil.EnhancedExactUnmarshalKey("peer.handlers", &libConf); err != nil {
return errors.WithMessage(err, "could not load YAML config")
}
reg := library.InitRegistry(libConf)
// 背书 验证相关配置
authFilters := reg.Lookup(library.Auth).([]authHandler.Filter)
endorserSupport := &endorser.SupportImpl{
SignerSupport: signingIdentity,
Peer: peer.Default,
PeerSupport: peer.DefaultSupport,
ChaincodeSupport: chaincodeSupport,
SysCCProvider: sccp,
ACLProvider: aclProvider,
}
endorsementPluginsByName := reg.Lookup(library.Endorsement).(map[string]endorsement2.PluginFactory)
validationPluginsByName := reg.Lookup(library.Validation).(map[string]validation.PluginFactory)
signingIdentityFetcher := (endorsement3.SigningIdentityFetcher)(endorserSupport)
channelStateRetriever := endorser.ChannelStateRetriever(endorserSupport)
pluginMapper := endorser.MapBasedPluginMapper(endorsementPluginsByName)
pluginEndorser := endorser.NewPluginEndorser(&endorser.PluginSupport{
ChannelStateRetriever: channelStateRetriever,
TransientStoreRetriever: peer.TransientStoreFactory,
PluginMapper: pluginMapper,
SigningIdentityFetcher: signingIdentityFetcher,
})
endorserSupport.PluginEndorser = pluginEndorser
serverEndorser := endorser.NewEndorserServer(privDataDist, endorserSupport, pr, metricsProvider)
auth := authHandler.ChainFilters(serverEndorser, authFilters...)
// Register the Endorser server
pb.RegisterEndorserServer(peerServer.Server(), auth)
policyMgr := peer.NewChannelPolicyManagerGetter()
// Initialize gossip component
err = initGossipService(policyMgr, metricsProvider, peerServer, serializedIdentity, peerEndpoint.Address)
if err != nil {
return err
}
defer service.GetGossipService().Stop()
// register prover grpc service
// FAB-12971 disable prover service before v1.4 cut. Will uncomment after v1.4 cut
// err = registerProverService(peerServer, aclProvider, signingIdentity)
// if err != nil {
// return err
// }
初始化系统链码。
// initialize system chaincodes
// deploy system chaincodes
// 部署系统链码
sccp.DeploySysCCs("", ccp)
logger.Infof("Deployed system chaincodes")
// 查看已经安装等链码
installedCCs := func() ([]ccdef.InstalledChaincode, error) {
return packageProvider.ListInstalledChaincodes()
}
// 创建链码的生命周期
lifecycle, err := cc.NewLifeCycle(cc.Enumerate(installedCCs))
if err != nil {
logger.Panicf("Failed creating lifecycle: +%v", err)
}
// HandleMetadataUpdate在链代码生命周期更改发生变化时触发
onUpdate := cc.HandleMetadataUpdate(func(channel string, chaincodes ccdef.MetadataSet) {
// 更新链码
service.GetGossipService().UpdateChaincodes(chaincodes.AsChaincodes(), gossipcommon.ChainID(channel))
})
// 监听器 监听链码更新
lifecycle.AddListener(onUpdate)
通道相关配置
// this brings up all the channels
peer.Initialize(func(cid string) {
logger.Debugf("Deploying system CC, for channel <%s>", cid)
sccp.DeploySysCCs(cid, ccp)
sub, err := lifecycle.NewChannelSubscription(cid, cc.QueryCreatorFunc(func() (cc.Query, error) {
// 返回通道的查询器
return peer.GetLedger(cid).NewQueryExecutor()
}))
if err != nil {
logger.Panicf("Failed subscribing to chaincode lifecycle updates")
}
// 注册该通道ChaincodeLifecycleEventListener
cceventmgmt.GetMgr().Register(cid, sub)
}, ccp, sccp, txvalidator.MapBasedPluginMapper(validationPluginsByName),
pr, deployedCCInfoProvider, membershipInfoProvider, metricsProvider)
// 获取peer一些配置
if viper.GetBool("peer.discovery.enabled") {
registerDiscoveryService(peerServer, policyMgr, lifecycle)
}
networkID := viper.GetString("peer.networkId")
logger.Infof("Starting peer with ID=[%s], network ID=[%s], address=[%s]", peerEndpoint.Id, networkID, peerEndpoint.Address)
// Get configuration before starting go routines to avoid
// racing in tests
profileEnabled := viper.GetBool("peer.profile.enabled")
profileListenAddress := viper.GetString("peer.profile.listenAddress")
// Start the grpc server. Done in a goroutine so we can deploy the
// genesis block if needed.
serve := make(chan error)
// 开启peer grpc服务
go func() {
var grpcErr error
if grpcErr = peerServer.Start(); grpcErr != nil {
grpcErr = fmt.Errorf("grpc server exited with error: %s", grpcErr)
} else {
logger.Info("peer server exited")
}
serve <- grpcErr
}()
// Start profiling http endpoint if enabled
if profileEnabled {
go func() {
logger.Infof("Starting profiling server with listenAddress = %s", profileListenAddress)
if profileErr := http.ListenAndServe(profileListenAddress, nil); profileErr != nil {
logger.Errorf("Error starting profiler: %s", profileErr)
}
}()
}
go handleSignals(addPlatformSignals(map[os.Signal]func(){
syscall.SIGINT: func() { serve <- nil },
syscall.SIGTERM: func() { serve <- nil },
}))
// peer启动区块归档任务
if ledgerconfig.IsDataDumpEnabled() {
logger.Debugf("DataDump:{DumpDir:%s,LoadDir:%s,MaxFileLimit:%d,DumpCron:%v,DumpInterval:%d,LoadRetryTimes:%d}",
ledgerconfig.GetDataDumpPath(), ledgerconfig.GetDataLoadPath(), ledgerconfig.GetDataDumpFileLimit(),
ledgerconfig.GetDataDumpCron(), ledgerconfig.GetDataDumpInterval(), ledgerconfig.GetDataLoadRetryTimes())
go func() {
cronList := ledgerconfig.GetDataDumpCron()
if cronList != nil && len(cronList) > 0 {
cronTask := cron.New()
cronTask.Start()
for _, crontab := range cronList {
logger.Debugf("Crontab addFunc for %s", crontab)
err := cronTask.AddFunc(crontab, func() {
chainInfoArray := peer.GetChannelsInfo()
for _, chainInfo := range chainInfoArray {
chainId := chainInfo.ChannelId
l := peer.GetLedger(chainId)
if err := l.DataDump(datadump.DumpForCronTab); err != nil {
logger.Errorf("Failed to datadump for [%s]", err)
}
}
})
if err != nil {
logger.Errorf("Failed to add crontab task for %s", err)
}
}
}
}()
}
logger.Infof("Started peer with ID=[%s], network ID=[%s], address=[%s]", peerEndpoint.Id, networkID, peerEndpoint.Address)
if viper.GetBool("peer.enBlkrouter") {
go func() {
startBlockServer()
}()
}
// Block until grpc server exits
// 阻塞 直到grpc服务退出
return <-serve
}
到这里Peer
节点已经启动完成了,过程还是很复杂的,这里总结一下整体的过程:
首先就是读取配置信息,创建Cache结构,以及检测其他Peer节点的信息。
CacheConfiguration()
,主要保存其他Peer
节点的相关信息。创建
PeerServer
。peerServer, err := peer.NewPeerServer(listenAddr, serverConfig)
创建
DeliverEventsServer
abServer := peer.NewDeliverEventsServer(mutualTLS, policyCheckerProvider, &peer.DeliverChainManager{}, metricsProvider)
pb.RegisterDeliverServer(peerServer.Server(), abServer)
fabric/core/peer/deliverevents.go
,该服务主要用于区块的交付与过滤,主要方法:Deliver(),DeliverFiltered()
启动
ChaincodeServer
chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr, opsSystem)
core/chaincode/chaincode_support.go
,返回了ChaincodeSupport:为Peer提供执行链码的接口,主要功能有Launch():启动一个停止运行的链码,Stop():停止链码的运行,HandleChaincodeStream():处理链码流信息,Register():将链码注册到当前Peer节点 ,createCCMessage():创建一个交易,ExecuteLegacyInit():链码的实例化,Execute():执行链码并返回回原始的响应,processChaincodeExecutionResult():处理链码的执行结果,InvokeInit():调用链码的Init方法,Invoke():调用链码,execute():执行一个交易
启动
AdminServer
startAdminServer(listenAddr, peerServer.Server(), metricsProvider)
core/protos/peer/admin.go
文件,具有GetStatus(),StartServer(),GetModuleLogLevel(),SetModuleLogLevel()
等方法
创建
EndorserServer
pb.RegisterEndorserServer(peerServer.Server(), auth)
core/endorser/endorser.go
文件,注册背书服务器,提供了一个很重要的方法:ProcessProposal()
,这个方法值得看一下。
创建
GossipService
err = initGossipService(policyMgr, metricsProvider, peerServer, serializedIdentity, peerEndpoint.Address)
gossip/service/gossip_service.go
,具有InitializeChannel(),createSelfSignedData(),updateAnchors(),AddPayload()
等方法
部署系统链码。
初始化通道。
启动gRPC服务。
如果启用了profile,还会启动监听服务。
参考:https://www.cnblogs.com/cbkj-xd/p/11141717.html
菜鸟系列Fabric源码学习 — peer节点启动的更多相关文章
- 菜鸟系列Fabric源码学习—orderer服务启动
Fabric 1.4 orderer 服务启动流程 1.提要 orderer提供broadcast和deliver两个服务接口.orderer节点与各个peer节点通过grpc连接,orderer将所 ...
- 菜鸟系列Fabric源码学习 — 区块同步
Fabric 1.4 源码分析 区块同步 本文主要从源码层面介绍fabric peer同步区块过程,peer同步区块主要有2个过程: 1)peer组织的leader与orderer同步区块 2)pee ...
- 菜鸟系列Fabric源码学习 — committer记账节点
Fabric 1.4 源码分析 committer记账节点 本文档主要介绍committer记账节点如何初始化的以及committer记账节点的功能及其实现. 1. 简介 记账节点负责验证交易和提交账 ...
- 菜鸟系列Fabric源码学习—创建通道
通道创建源码解析 1. 与通道创建相关配置及操作命令 主要是configtx.yaml.通过应用通道的profile生成创建通道的配置文件. TwoOrgsChannel: Consortium: S ...
- 菜鸟系列Fabric源码学习 — MVCC验证
Fabric 1.4 源码分析 MVCC验证 读本节文档之前建议先查看[Fabric 1.4 源码分析 committer记账节点]章节. 1. MVCC简介 Multi-Version Concur ...
- 菜鸟学习Fabric源码学习 — 背书节点和链码容器交互
Fabric 1.4 源码分析 背书节点和链码容器交互 本文档主要介绍背书节点和链码容器交互流程,在Endorser背书节点章节中,无论是deploy.upgrade或者调用链码,最后都会调用Chai ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
随机推荐
- 查看线上日志利器less
less实用命令 搜索 很多关于命令的解释有点令人困惑,因为前字,forward是向前,before也是前面. 上表示backward 下表示forward 向下搜索 / - 使用一个模式进行搜索,并 ...
- 函数进阶(三) day14
目录 昨日内容 迭代器 可迭代对象 迭代器对象 for循环原理 三元表达式 列表推导式 字典生成式 zip 生成器表达式 生成器 yield 递归 今日内容 匿名函数 内置方法 掌握 了解 异常处理 ...
- vue cli3.3 以上版本配置vue.config.js 及反向代理操作解决跨域操作
const webpack = require('webpack') module.exports = { configureWebpack: { plugins: [ new webpack.Pro ...
- 聊一聊Java中的各种运算符
计算机之所以叫“计算机”,其最基本用途之一就是运算,对应刚刚接触Java的小伙伴而言,熟悉并掌握Java中的各种运算符及其在表达式中的运算优先级是十分必要的. 算术运算 算术运算主要用来处理数学中的加 ...
- Spring Boot (日志篇):Log4j2整合ELK,搭建实时日志平台
一.安装JDK1.8以上版本 1.从Oracle官网上下载Linux x64版本的 下载地址: http://www.oracle.com/technetwork/java/javase/downlo ...
- 中文¥乱码 vue js
/** * * 中文¥格式化,返回格式化后的¥100.00 * @param {any} money */utils.formatCNY = function (money) { let format ...
- OPTIONS 请求引发的分析
阅读提纲: 为什么会出现 OPTIONS 请求? 什么情况下会出现 OPTIONS 请求? OPTIONS 请求会发送什么内容? 跨域前端访问后端时,所有的 Ajax HTTP 请求都会先发送一个 O ...
- 通俗易懂了解React生命周期
1.前言 学习React时,学习组件的生命周期是非常重要的,了解了组件的"从无到有再到无"所经历的各个状态,对日后写高性能的组件会有很大的帮助. 2.生命周期图 React的生命周 ...
- T1
老师的作业提示里说有难题,也有水题,果真很水... 单纯的模拟加暴力 #include<iostream> using namespace std; int n; ; int cow[ma ...
- 爬虫学习--常用的正则表达式 Day3
在做爬虫经常遇到需要用正则校验数据时候,往往是在网上去找很久,结果找来的还是不很符合要求.所以我最近把开发中常用的一些正则表达式整理了一下,给自己留个底,也给朋友们做个参考. 一.校验数字的表达式 1 ...