一、安裝啓動方式
Fabric的啓動有幾種方法,一種是使用Docker從官方下載相應的版本然後啓動,一種是直接源碼編譯啓動,這兩種方式沒有本質的不同。同時,最新的Fabric提供了Solo和Raft兩種共識服務機制(不討論舊版本的KAFKA等),在數據的保存機制上提供了LevelDB和CouchDB兩種方式。
因此,有必要把這些環境都指定下來,才能更好的分析源碼。這裏主要以Raft共識爲主,leveldb數據庫爲基礎進行分析。安裝啓動的方式以源碼編譯爲主。
之所以選取這種形式,主要是這種形式更容易理解,把相關複雜的與源碼無關的環境配置選項從中摘除,以便清晰的展現代碼運行的路徑。
二、啓動流程
重點是Peer和Orderer兩個模塊,先分析Peer:
1、入口函數:
//cobra是一個開源Golang命令包,可以快速創建命令行界面,在以太坊中用的CLI這個包
var mainCmd = &cobra.Command{
Use: "peer"}
func main() {
//下面這段代碼,主要是用來處理環境變量的處理方式
viper.SetEnvPrefix(common.CmdRoot)
viper.AutomaticEnv()
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
//處理命令格式,包括所有的peer相關的命令
mainFlags := mainCmd.PersistentFlags()
mainFlags.String("logging-level", "", "Legacy logging level flag")
viper.BindPFlag("logging_level", mainFlags.Lookup("logging-level"))
mainFlags.MarkHidden("logging-level")
//添加相關的命令,如版本、通道等
mainCmd.AddCommand(version.Cmd())
mainCmd.AddCommand(node.Cmd())
mainCmd.AddCommand(chaincode.Cmd(nil))
mainCmd.AddCommand(clilogging.Cmd(nil))
mainCmd.AddCommand(channel.Cmd(nil))
// 執行並處理異常,主要的任務由cobra完成,此處僅在錯誤時返回一個非零值
if mainCmd.Execute() != nil {
os.Exit(1)
}
}
看代碼,這太簡單了,簡單的簡直不像話。可是,一般來說,話少事大,同理,代碼少,估計背後的服務啥的啓動的少不了。挑一個常用的比如node.Cmd,進去看看:
func startCmd() *cobra.Command {
// Set the flags on the node start command.
flags := nodeStartCmd.Flags()
flags.BoolVarP(&chaincodeDevMode, "peer-chaincodedev", "", false,
"Whether peer in chaincode development mode")
return nodeStartCmd
}
//返回的是下面的類實例
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)
},
}
這個裏頭有一個Command結構體:
type Command struct {
// Use is the one-line usage message.
Use string
// Aliases is an array of aliases that can be used instead of the first word in Use.
Aliases []string
// SuggestFor is an array of command names for which this command will be suggested -
// similar to aliases but only suggests.
SuggestFor []string
// Short is the short description shown in the 'help' output.
Short string
// Long is the long message shown in the 'help <this-command>' output.
Long string
// Example is examples of how to use the command.
Example string
// ValidArgs is list of all valid non-flag arguments that are accepted in bash completions
ValidArgs []string
// Expected arguments
Args PositionalArgs
// ArgAliases is List of aliases for ValidArgs.
// These are not suggested to the user in the bash completion,
// but accepted if entered manually.
ArgAliases []string
// BashCompletionFunction is custom functions used by the bash autocompletion generator.
BashCompletionFunction string
// Deprecated defines, if this command is deprecated and should print this string when used.
Deprecated string
// Hidden defines, if this command is hidden and should NOT show up in the list of available commands.
Hidden bool
......
// commands is the list of commands supported by this program.
commands []*Command
// parent is a parent command for this command.
parent *Command
// Max lengths of commands' string lengths for use in padding.
commandsMaxUseLen int
commandsMaxCommandPathLen int
commandsMaxNameLen int
// commandsAreSorted defines, if command slice are sorted or not.
commandsAreSorted bool
// commandCalledAs is the name or alias value used to call this command.
commandCalledAs struct {
name string
called bool
}
// args is actual args parsed from flags.
args []string
// flagErrorBuf contains all error messages from pflag.
flagErrorBuf *bytes.Buffer
// flags is full set of flags.
flags *flag.FlagSet
// pflags contains persistent flags.
pflags *flag.FlagSet
// lflags contains local flags.
lflags *flag.FlagSet
// iflags contains inherited flags.
iflags *flag.FlagSet
// parentsPflags is all persistent flags of cmd's parents.
parentsPflags *flag.FlagSet
// globNormFunc is the global normalization function
// that we can use on every pflag set and children commands
globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName
// output is an output writer defined by user.
output io.Writer
// usageFunc is usage func defined by user.
usageFunc func(*Command) error
......
helpCommand *Command
// versionTemplate is the version template defined by user.
versionTemplate string
}
有一點嚇人的長,不過有註釋,再加上命名還是挺規範的,所以看看也沒啥難度,就是有點多,估計經常得來回切換着看。現在只關心和Run有關係的Action,其它暫時可以放一下。
看一下命令的執行過程:
func main() {
......
//將相關加入的命令執行
if mainCmd.Execute() != nil {
os.Exit(1)
}
}
//調用
func (c *Command) Execute() error {
_, err := c.ExecuteC()
return err
}
func (c *Command) ExecuteC() (cmd *Command, err error) {
// 僅運行根命令
if c.HasParent() {
return c.Root().ExecuteC()
}
//預處理函數
if preExecHookFn != nil {
preExecHookFn(c)
}
// 默認初始化幫助
c.InitDefaultHelpCmd()
......
//關鍵是這裏
err = cmd.execute(flags)
......
return cmd, err
}
func (c *Command) execute(a []string) (err error) {
......
//初始化默認的幫助和版本標記
c.InitDefaultHelpFlag()
c.InitDefaultVersionFlag()
//分析輸入的標記
err = c.ParseFlags(a)
if err != nil {
return c.FlagErrorFunc()(c, err)
}
......
//判斷執行函數是否存在
if !c.Runnable() {
return flag.ErrHelp
}
c.preRun()
//解析參數
argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
if err := c.ValidateArgs(argWoFlags); err != nil {
return err
}
//下來就是一大票各種執行,包括父子命令和不同的命令格式情況下調用不同的命令形式運行
for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPreRun != nil {
p.PersistentPreRun(c, argWoFlags)
break
}
}
if c.PreRunE != nil {
if err := c.PreRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PreRun != nil {
c.PreRun(c, argWoFlags)
}
if err := c.validateRequiredFlags(); err != nil {
return err
}
if c.RunE != nil {
if err := c.RunE(c, argWoFlags); err != nil {
return err
}
} else {
c.Run(c, argWoFlags)
}
if c.PostRunE != nil {
if err := c.PostRunE(c, argWoFlags); err != nil {
return err
}
} else if c.PostRun != nil {
c.PostRun(c, argWoFlags)
}
for p := c; p != nil; p = p.Parent() {
if p.PersistentPostRunE != nil {
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
return err
}
break
} else if p.PersistentPostRun != nil {
p.PersistentPostRun(c, argWoFlags)
break
}
}
return nil
}
看到最後大片的if…else了吧,就是一開始命令中的設置。這樣就明白了,一個命令執行,比如"peer node start -o 127.0.0.1:7091 --peer-chaincodedev=true",就可以從這裏分析。
後面的AddCommand都是如此,包括日誌,鏈碼和通道相關,再看一下通道相關的命令:
func Cmd(cf *ChannelCmdFactory) *cobra.Command {
AddFlags(channelCmd)
channelCmd.AddCommand(createCmd(cf))
channelCmd.AddCommand(fetchCmd(cf))
channelCmd.AddCommand(joinCmd(cf))
channelCmd.AddCommand(listCmd(cf))
channelCmd.AddCommand(updateCmd(cf))
channelCmd.AddCommand(signconfigtxCmd(cf))
channelCmd.AddCommand(getinfoCmd(cf))
return channelCmd
}
func createCmd(cf *ChannelCmdFactory) *cobra.Command {
createCmd := &cobra.Command{
Use: "create",
Short: "Create a channel",
Long: "Create a channel and write the genesis block to a file.",
RunE: func(cmd *cobra.Command, args []string) error {
return create(cmd, args, cf)
},
}
flagList := []string{
"channelID",
"file",
"outputBlock",
"timeout",
}
attachFlags(createCmd, flagList)
return createCmd
}
好,流程明白了,回頭看一下Start命令最後啓動的server(args)。
2、服務啓動
func serve(args []string) error {
// MSP的處理,只支持標準
mspType := mgmt.GetLocalMSP().GetType()
if mspType != msp.FABRIC {
panic("Unsupported msp type " + msp.ProviderTypeToString(mspType))
}
......
//使用ACL啓動aclmgmt,創建ACL訪問控制列表
aclProvider := aclmgmt.NewACLProvider(
aclmgmt.ResourceGetter(peer.GetStableChannelConfig),
)
//註冊平臺相關語言
pr := platforms.NewRegistry(
&golang.Platform{},
&node.Platform{},
&java.Platform{},
&car.Platform{},
)
//定義鏈碼提供者實例
deployedCCInfoProvider := &lscc.DeployedCCInfoProvider{}
//獲取通道管理者
identityDeserializerFactory := func(chainID string) msp.IdentityDeserializer {
return mgmt.GetManagerForChain(chainID)
}
//處理相關環境操作保存相關地址、端口、證書及TLS等。
opsSystem := newOperationsSystem()
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 := 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,
},
)
......//處理參數部分省略
//下面拿到緩存的節點信息(主要是地址)後開始網絡服務
peerEndpoint, err := peer.GetPeerEndpoint()
if err != nil {
err = fmt.Errorf("Failed to get Peer Endpoint: %s", err)
return err
}
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")
serverConfig, err := peer.GetServerConfig()
if err != nil {
logger.Fatalf("Error loading secure config for peer (%s)", err)
}
//限流,最大2500
throttle := comm.NewThrottle(grpcMaxConcurrency)
serverConfig.Logger = flogging.MustGetLogger("core.comm").With("server", "PeerServer")
serverConfig.ServerStatsHandler = comm.NewServerStatsHandler(metricsProvider)
//設置攔截器,監控和RPC
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,
)
//創建節點RPC服務
peerServer, err := peer.NewPeerServer(listenAddr, serverConfig)
if err != nil {
logger.Fatalf("Failed to create peer server (%s)", err)
}
//處理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
//策略柱果木欖果,對通道ID和變量等
policyCheckerProvider := func(resourceName string) deliver.PolicyCheckerFunc {
return func(env *cb.Envelope, channelID string) error {
return aclProvider.CheckACL(resourceName, channelID, env)
}
}
//創建分發和過濾區塊事件服務
abServer := peer.NewDeliverEventsServer(mutualTLS, policyCheckerProvider, &peer.DeliverChainManager{}, metricsProvider)
pb.RegisterDeliverServer(peerServer.Server(), abServer)
// Initialize chaincode service
//設置本地鏈碼安裝位置;創建自簽名CA,啓動GRPC服務
chaincodeSupport, ccp, sccp, packageProvider := startChaincodeServer(peerHost, aclProvider, pr, opsSystem)
logger.Debugf("Running peer")
// Start the Admin server-這個是重點,管理員可以管理通道等
startAdminServer(listenAddr, peerServer.Server(), serverConfig)
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)
expirationLogger := flogging.MustGetLogger("certmonitor")
crypto.TrackExpiration(
serverConfig.SecOpts.UseTLS,
serverConfig.SecOpts.Certificate,
comm.GetCredentialSupport().GetClientCertificate().Certificate,
serializedIdentity,
expirationLogger.Warnf, // This can be used to piggyback a metric event in the future
time.Now(),
time.AfterFunc)
policyMgr := peer.NewChannelPolicyManagerGetter()
// Initialize gossip component--初始化網絡間通信的Gossip協議服務
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)
}
//鏈碼的元數據更新
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")
}
//通道監聽器註冊
cceventmgmt.GetMgr().Register(cid, sub)
}, ccp, sccp, txvalidator.MapBasedPluginMapper(validationPluginsByName),
pr, deployedCCInfoProvider, membershipInfoProvider, metricsProvider)
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")
// 創建GRPC服務協程
serve := make(chan error)
// Start profiling http endpoint if enabled-如定義配置文件,啓動RPC服務
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 },
}))
logger.Infof("Started peer with ID=[%s], network ID=[%s], address=[%s]", peerEndpoint.Id, networkID, peerEndpoint.Address)
// 檢查帳本是否重置check to see if the peer ledgers have been reset
preResetHeights, err := kvledger.LoadPreResetHeight()
if err != nil {
return fmt.Errorf("error loading prereset height: %s", err)
}
for cid, height := range preResetHeights {
logger.Infof("Ledger rebuild: channel [%s]: preresetHeight: [%d]", cid, height)
}
......
// 下面啓動節點服務
auth := authHandler.ChainFilters(serverEndorser, authFilters...)
// Register the Endorser server
pb.RegisterEndorserServer(peerServer.Server(), auth)
go func() {
var grpcErr error
if grpcErr = peerServer.Start(); grpcErr != nil {
grpcErr = fmt.Errorf("grpc server exited with error: %s", grpcErr)
}
serve <- grpcErr
}()
// Block until grpc server exits-//通道一直在此阻塞服務
return <-serve
}
又一大片,從剛開始接觸Golang就發現這個語言的一個特點,稍微複雜的一點功能,就是一片代碼,不像c++啥的拆來拆去。吐槽完畢,代碼還得分析:
func (gServer *GRPCServer) Start() error {
return gServer.server.Serve(gServer.listener)
}
func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock()
s.printf("serving")
s.serve = true
if s.lis == nil {
// Serve called after Stop or GracefulStop.
s.mu.Unlock()
lis.Close()
return ErrServerStopped
}
s.serveWG.Add(1)
defer func() {
s.serveWG.Done()
select {
// Stop or GracefulStop called; block until done and return nil.
case <-s.quit:
<-s.done
default:
}
}()
ls := &listenSocket{Listener: lis}
s.lis[ls] = true
if channelz.IsOn() {
ls.channelzID = channelz.RegisterListenSocket(ls, s.channelzID, "")
}
s.mu.Unlock()
defer func() {
s.mu.Lock()
if s.lis != nil && s.lis[ls] {
ls.Close()
delete(s.lis, ls)
}
s.mu.Unlock()
}()
var tempDelay time.Duration // how long to sleep on accept failure
for {
rawConn, err := lis.Accept()
if err != nil {
if ne, ok := err.(interface {
Temporary() bool
}); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
s.mu.Lock()
s.printf("Accept error: %v; retrying in %v", err, tempDelay)
s.mu.Unlock()
timer := time.NewTimer(tempDelay)
select {
case <-timer.C:
case <-s.quit:
timer.Stop()
return nil
}
continue
}
s.mu.Lock()
s.printf("done serving; Accept = %v", err)
s.mu.Unlock()
select {
case <-s.quit:
return nil
default:
}
return err
}
tempDelay = 0
// Start a new goroutine to deal with rawConn so we don't stall this Accept
// loop goroutine.
//
// Make sure we account for the goroutine so GracefulStop doesn't nil out
// s.conns before this conn can be added.
s.serveWG.Add(1)
go func() {
s.handleRawConn(rawConn)
s.serveWG.Done()
}()
}
}
看到這裏,纔看到了熟悉的套路,熟悉的配方,網線監聽,網絡服務的處理。到此處,基本一個Peer整體的流程的主幹就清晰了。
3、說明
需要說明的是,在Server函數中,沒有重點分析系統鏈碼和帳本相關的處理,可以看一下相關的代碼註釋即可。沒有什麼太複雜的內容。
其實Peer節點中的代碼還有很多,不過這裏只是分析其啓動的流程,相關的代碼會在後面的相關部分進行展開。抓住重點,暫時扔掉細節,先從宏觀上把握脈絡。總結一下:
獲取msp類型
設置資源訪問策略 aclmgmt.NewACLProvider,測試可RegisterACLProvider()
初始化本地賬本管理器 ledgermgmt.Initialize(peer.ConfigTxProcessors)
創建grpc服務1:PeerServer
創建grpc服務2:DeliverEvents(分發事件),註冊到peerServer上
創建grpc服務3:鏈碼管理服務
創建grpc服務4 : Admin註冊到PeerServer上
隱私數據處理: 使用goosip協議進行分發
啓動發現服務5:registerDiscoveryService
創建grpc服務6:背書服務
初始化goosip連接與加密組件
registerProverService:根據版本1.4前後不同的處理。
安裝系統連碼 initSysCCs並初始化peer.Initialize(func(cid string) {}
啓動PeerServer Grpc服務
設置日誌模塊
和代碼對比着流程,一一的看代碼,就可以把整個的peer的內容看得清清楚楚。
三、總結
Peer的代碼是整個Fabric中很重要的一個部分,可以說,他是基礎,他提供了一系列相關的操作命令和通信的細節,這都需要後面不斷的詳細分析。