Fabric源碼分析之三啓動流程代碼Peer分析

一、安裝啓動方式

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中很重要的一個部分,可以說,他是基礎,他提供了一系列相關的操作命令和通信的細節,這都需要後面不斷的詳細分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章