Fabric-ca client端初始化过程源码分析
本文从Fabric-ca源码入手,以newRegisterCommand()
函数为例,简单分析client启动时的过程。Fabric-ca源码可以从github.com下载,本文以v1.4.6为例进行简单分析。
与server相似,本文也是从main.go开始:
// fabric-ca/cmd/fabric-ca-client/main.go package main import ( ... ) // The fabric-ca client main func main() { if err := command.RunMain(os.Args); err != nil { os.Exit(1) } }
main()
函数只是调用了package command
中的RunMain()
函数,所以接下来我们以RunMain()
函数为起点来简析client端的启动过程,其中包括client端的初始化以及跟server端的初次交互:
// fabric-ca/cmd/fabric-ca-client/command/root.go package command import "os" // RunMain is the fabric-ca client main func RunMain(args []string) error { // Save the os.Args saveOsArgs := os.Args os.Args = args // Execute the command cmdName := "" if len(args) > 1 { cmdName = args[1] } ccmd := NewCommand(cmdName) err := ccmd.Execute() // Restore original os.Args os.Args = saveOsArgs return err }
不难看出,与server类似,都是先通过NewCommand()
函数来添加命令,之后再通过Execute()
来执行操作:
// fabric-ca/cmd/fabric-ca-client/command/clientcmd.go // ClientCmd encapsulates cobra command that provides command line interface // for the Fabric CA client and the configuration used by the Fabric CA client type ClientCmd struct { // name of the sub command name string // rootCmd is the base command for the Hyerledger Fabric CA client rootCmd *cobra.Command // My viper instance myViper *viper.Viper // cfgFileName is the name of the configuration file cfgFileName string // homeDirectory is the location of the client's home directory homeDirectory string // clientCfg is the client's configuration clientCfg *lib.ClientConfig // cfgAttrs are the attributes specified via flags or env variables // and translated to Attributes field in registration cfgAttrs []string // cfgAttrReqs are the attribute requests specified via flags or env variables // and translated to the AttrReqs field in enrollment cfgAttrReqs []string // cfgCsrNames are the certificate signing request names specified via flags // or env variables cfgCsrNames []string // csrCommonName is the certificate signing request common name specified via the flag csrCommonName string // gencrl command argument values crlParams crlArgs // revoke command argument values revokeParams revokeArgs // profileMode is the profiling mode, cpu or mem or empty profileMode string // profileInst is the profiling instance object profileInst interface { Stop() } // Dynamically configuring identities dynamicIdentity identityArgs // Dynamically configuring affiliations dynamicAffiliation affiliationArgs // Set to log level logLevel string } // NewCommand returns new ClientCmd ready for running func NewCommand(name string) *ClientCmd { c := &ClientCmd{ myViper: viper.New(), } c.name = strings.ToLower(name) c.init() return c } ... // init initializes the ClientCmd instance // It intializes the cobra root and sub commands and // registers command flgs with viper func (c *ClientCmd) init() { ... c.rootCmd.AddCommand(c.newRegisterCommand(), newEnrollCmd(c).getCommand(), c.newReenrollCommand(), c.newRevokeCommand(), newGetCAInfoCmd(c).getCommand(), c.newGenCsrCommand(), c.newGenCRLCommand(), c.newIdentityCommand(), c.newAffiliationCommand(), createCertificateCommand(c)) c.rootCmd.AddCommand(&cobra.Command{ Use: "version", Short: "Prints Fabric CA Client version", Run: func(cmd *cobra.Command, args []string) { fmt.Print(metadata.GetVersionInfo(cmdName)) }, }) c.registerFlags() ... }
在NewCommand()
函数中,创建了一个*ClientCmd
的对象,之后,调用该对象的init()
方法。在init()
方法中,首先实例化了*ClientCmd.rootCmd
,其中会要执行checkAndEnableProfiling()
来检查运行环境:
// checkAndEnableProfiling checks for the FABRIC_CA_CLIENT_PROFILE_MODE // env variable, if it is set to "cpu", cpu profiling is enbled; // if it is set to "heap", heap profiling is enabled func (c *ClientCmd) checkAndEnableProfiling() error { ... } // registerFlags registers command flags with viper func (c *ClientCmd) registerFlags() { ... }
之后会调用AddCommand()
函数来添加newRegisterCommand()
、newEnrollCmd(c).getCommand()
、newReenrollCommand()
、newRevokeCommand()
、newGetCAInfoCmd(c).getCommand()
、newGenCsrCommand()
、newGenCRLCommand()
、newIdentityCommand()
、newAffiliationCommand()
、createCertificateCommand()
和获取client版本的命令。随后执行c.registerFlags()
操作。registerFlags()
函数中主要是注册一些命令行参数,这里就不细究了。
newRegisterCommand()
// fabric-ca/cmd/fabric-ca-client/command/register.go func (c *ClientCmd) newRegisterCommand() *cobra.Command { ... } // The client register main logic func (c *ClientCmd) runRegister() error { ... }
在newRegisterCommand()
函数中,实例化了一个*cobra.Command
命令对象,该对象中包含两个命令:ConfigInit()
和runRegister()
。ConfigInit()
命令是为fabric-ca-client命令初始化一些配置,这里不做说明,而runRegister()
中包含客户端注册的主要逻辑:首先实例化一个lib.Client
结构体,之后导入client端的身份凭证client.LoadMyIdentity()
,随后发起注册*Identity.Register()
。
// fabric-ca/lib/client.go // Client is the fabric-ca client object type Client struct { // The client's home directory HomeDir string `json:"homeDir,omitempty"` // The client's configuration Config *ClientConfig // Denotes if the client object is already initialized initialized bool // File and directory paths keyFile, certFile, idemixCredFile, idemixCredsDir, ipkFile, caCertsDir string // The crypto service provider (BCCSP) csp bccsp.BCCSP // HTTP client associated with this Fabric CA client httpClient *http.Client // Public key of Idemix issuer issuerPublicKey *idemix.IssuerPublicKey } // LoadMyIdentity loads the client's identity from disk func (c *Client) LoadMyIdentity() (*Identity, error) { ... return c.LoadIdentity(c.keyFile, c.certFile, c.idemixCredFile) } // LoadIdentity loads an identity from disk func (c *Client) LoadIdentity(keyFile, certFile, idemixCredFile string) (*Identity, error) { ... return c.NewIdentity(creds) } // NewIdentity creates a new identity func (c *Client) NewIdentity(creds []credential.Credential) (*Identity, error) { ... return NewIdentity(c, name, creds), nil }
如上,在LoadMyIdentity()
中会先调用Init()
来初始化client,然后再调用LoadIdentity()
函数,从硬盘中导入身份凭证。在LoadIdentity()
中同样会调用Init()
来初始化client,之后会调用NewCredential()
接口来导入x509和idemin格式的证书。证书读取完成后,调用NewIdentity()
来创建新的身份认证。至此,身份导入过程就完成了,接下来就是注册了。
//fabric-ca/lib/identity.go // Register registers a new identity // @param req The registration request func (i *Identity) Register(req *api.RegistrationRequest) (rr *api.RegistrationResponse, err error) { ... }
在Register()
,会将认证请求序列后,通过Post
请求发送给服务端,并将服务端的应答返回给调用者。
// Post sends arbitrary request body (reqBody) to an endpoint. // This adds an authorization header which contains the signature // of this identity over the body and non-signature part of the authorization header. // The return value is the body of the response. func (i *Identity) Post(endpoint string, reqBody []byte, result interface{}, queryParam map[string]string) error { ... }
至此,注册命令就结束了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具