本文从Fabric-ca源码入手,简单分析server启动时的过程。Fabric-ca源码可以从github.com下载,本文以v1.4.6为例进行简单分析。

Fabric-ca是有go语言编写的,与C/C++类似,程序都有一个mian()函数,不同的是,go的main函数必须存在于package main中:

  1. // fabric-ca/cmd/fabric-ca-server/main.go
  2. package main
  3. import "os"
  4. var (
  5. blockingStart = true
  6. )
  7. // The fabric-ca server main
  8. func main() {
  9. if err := RunMain(os.Args); err != nil {
  10. os.Exit(1)
  11. }
  12. }
  13. // RunMain is the fabric-ca server main
  14. func RunMain(args []string) error {
  15. // Save the os.Args
  16. saveOsArgs := os.Args
  17. os.Args = args
  18. cmdName := ""
  19. if len(args) > 1 {
  20. cmdName = args[1]
  21. }
  22. scmd := NewCommand(cmdName, blockingStart)
  23. // Execute the command
  24. err := scmd.Execute()
  25. // Restore original os.Args
  26. os.Args = saveOsArgs
  27. return err
  28. }

从上述代码中可以看出,程序执行时会调用RunMain()函数,而在RunMain()中调用NewCommand()来生成一个*ServerCmd对象,之后调用该对象的Execute()方法。那么接下来就是分析NewCommand()函数了。

  1. // fabric-ca/cmd/fabric-ca-server/servercmd.go
  2. // ServerCmd encapsulates cobra command that provides command line interface
  3. // for the Fabric CA server and the configuration used by the Fabric CA server
  4. type ServerCmd struct {
  5. // name of the fabric-ca-server command (init, start, version)
  6. name string
  7. // rootCmd is the cobra command
  8. rootCmd *cobra.Command
  9. // My viper instance
  10. myViper *viper.Viper
  11. // blockingStart indicates whether to block after starting the server or not
  12. blockingStart bool
  13. // cfgFileName is the name of the configuration file
  14. cfgFileName string
  15. // homeDirectory is the location of the server's home directory
  16. homeDirectory string
  17. // serverCfg is the server's configuration
  18. cfg *lib.ServerConfig
  19. }
  20. // NewCommand returns new ServerCmd ready for running
  21. func NewCommand(name string, blockingStart bool) *ServerCmd {
  22. s := &ServerCmd{
  23. name: name,
  24. blockingStart: blockingStart,
  25. myViper: viper.New(),
  26. }
  27. s.init()
  28. return s
  29. }

可以看到,在NewCommand()函数中只有三个操作:构造一个*ServerCmd的对象、调用它的init()函数,然后返回该对象。

*ServerCmd.init()函数中:

  1. // fabric-ca/cmd/fabric-ca-server/servercmd.go
  2. // init initializes the ServerCmd instance
  3. // It intializes the cobra root and sub commands and
  4. // registers command flgs with viper
  5. func (s *ServerCmd) init() {
  6. // root command
  7. rootCmd := &cobra.Command{
  8. Use: cmdName,
  9. Short: longName,
  10. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  11. err := s.configInit()
  12. if err != nil {
  13. return err
  14. }
  15. cmd.SilenceUsage = true
  16. util.CmdRunBegin(s.myViper)
  17. return nil
  18. },
  19. }
  20. s.rootCmd = rootCmd
  21. // initCmd represents the server init command
  22. initCmd := &cobra.Command{
  23. Use: "init",
  24. Short: fmt.Sprintf("Initialize the %s", shortName),
  25. Long: "Generate the key material needed by the server if it doesn't already exist",
  26. }
  27. initCmd.RunE = func(cmd *cobra.Command, args []string) error {
  28. if len(args) > 0 {
  29. return errors.Errorf(extraArgsError, args, initCmd.UsageString())
  30. }
  31. err := s.getServer().Init(false)
  32. if err != nil {
  33. util.Fatal("Initialization failure: %s", err)
  34. }
  35. log.Info("Initialization was successful")
  36. return nil
  37. }
  38. s.rootCmd.AddCommand(initCmd)
  39. // startCmd represents the server start command
  40. startCmd := &cobra.Command{
  41. Use: "start",
  42. Short: fmt.Sprintf("Start the %s", shortName),
  43. }
  44. startCmd.RunE = func(cmd *cobra.Command, args []string) error {
  45. if len(args) > 0 {
  46. return errors.Errorf(extraArgsError, args, startCmd.UsageString())
  47. }
  48. err := s.getServer().Start()
  49. if err != nil {
  50. return err
  51. }
  52. return nil
  53. }
  54. s.rootCmd.AddCommand(startCmd)
  55. var versionCmd = &cobra.Command{
  56. Use: "version",
  57. Short: "Prints Fabric CA Server version",
  58. Run: func(cmd *cobra.Command, args []string) {
  59. fmt.Print(metadata.GetVersionInfo(cmdName))
  60. },
  61. }
  62. s.rootCmd.AddCommand(versionCmd)
  63. s.registerFlags()
  64. }
  65. // registerFlags registers command flags with viper
  66. func (s *ServerCmd) registerFlags() {
  67. // Get the default config file path
  68. cfg := util.GetDefaultConfigFile(cmdName)
  69. // All env variables must be prefixed
  70. s.myViper.SetEnvPrefix(envVarPrefix)
  71. s.myViper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
  72. // Set specific global flags used by all commands
  73. pflags := s.rootCmd.PersistentFlags()
  74. pflags.StringVarP(&s.cfgFileName, "config", "c", "", "Configuration file")
  75. pflags.MarkHidden("config")
  76. // Don't want to use the default parameter for StringVarP. Need to be able to identify if home directory was explicitly set
  77. pflags.StringVarP(&s.homeDirectory, "home", "H", "", fmt.Sprintf("Server's home directory (default \"%s\")", filepath.Dir(cfg)))
  78. util.FlagString(s.myViper, pflags, "boot", "b", "",
  79. "The user:pass for bootstrap admin which is required to build default config file")
  80. // Register flags for all tagged and exported fields in the config
  81. s.cfg = &lib.ServerConfig{}
  82. tags := map[string]string{
  83. "help.csr.cn": "The common name field of the certificate signing request to a parent fabric-ca-server",
  84. "help.csr.serialnumber": "The serial number in a certificate signing request to a parent fabric-ca-server",
  85. "help.csr.hosts": "A list of comma-separated host names in a certificate signing request to a parent fabric-ca-server",
  86. }
  87. err := util.RegisterFlags(s.myViper, pflags, s.cfg, nil)
  88. if err != nil {
  89. panic(err)
  90. }
  91. caCfg := &lib.CAConfig{}
  92. err = util.RegisterFlags(s.myViper, pflags, caCfg, tags)
  93. if err != nil {
  94. panic(err)
  95. }
  96. }
  97. // Configuration file is not required for some commands like version
  98. func (s *ServerCmd) configRequired() bool {
  99. return s.name != version
  100. }
  101. // getServer returns a lib.Server for the init and start commands
  102. func (s *ServerCmd) getServer() *lib.Server {
  103. return &lib.Server{
  104. HomeDir: s.homeDirectory,
  105. Config: s.cfg,
  106. BlockingStart: s.blockingStart,
  107. CA: lib.CA{
  108. Config: &s.cfg.CAcfg,
  109. ConfigFilePath: s.cfgFileName,
  110. },
  111. }
  112. }

在函数中,首先是创建了一个*cobra.Command的对象,在该对象中的主要有两个操作:执行配置初始化以及util.CmdRunBegin()操作,之后将该对象赋值给s.rootCmd;接下来又创建了三个*cobra.Command对象:initCmdstartCmdversionCmd,分别是服务初始化命令、启动命令和获取server版本命令,创建完成后将这三个命令使用AddCommand()添加到s.rootCmd,随后执行s.registerFlags()操作。registerFlags()函数中主要是注册一些命令行参数,这里就不细究了。在 initCmdstartCmd中都用到了getServer()函数,该函数返回一个*libServer对象:

  1. // fabric-ca/lib/server.go
  2. // Server is the fabric-ca server
  3. type Server struct {
  4. // The home directory for the server.
  5. HomeDir string
  6. // BlockingStart determines if Start is blocking.
  7. // It is non-blocking by default.
  8. BlockingStart bool
  9. // The server's configuration
  10. Config *ServerConfig
  11. // Metrics are the metrics that the server tracks for API calls.
  12. Metrics servermetrics.Metrics
  13. // Operations is responsible for the server's operation information.
  14. Operations operationsServer
  15. // CA is the default certificate authority for the server.
  16. CA
  17. // metrics for database requests
  18. dbMetrics *db.Metrics
  19. // mux is used to server API requests
  20. mux *gmux.Router
  21. // listener for this server
  22. listener net.Listener
  23. // An error which occurs when serving
  24. serveError error
  25. // caMap is a list of CAs by name
  26. caMap map[string]*CA
  27. // caConfigMap is a list CA configs by filename
  28. caConfigMap map[string]*CAConfig
  29. // levels currently supported by the server
  30. levels *dbutil.Levels
  31. wait chan bool
  32. mutex sync.Mutex
  33. }

initCmd

initCmd中,调用了*libServer对象Init()函数,该函数中调用了init()函数来执行server的初始化:

  1. // init initializses the server leaving the DB open
  2. func (s *Server) init(renew bool) (err error) {
  3. s.Config.Operations.Metrics = s.Config.Metrics
  4. s.Operations = operations.NewSystem(s.Config.Operations)
  5. s.initMetrics()
  6. serverVersion := metadata.GetVersion()
  7. err = calog.SetLogLevel(s.Config.LogLevel, s.Config.Debug)
  8. if err != nil {
  9. return err
  10. }
  11. log.Infof("Server Version: %s", serverVersion)
  12. s.levels, err = metadata.GetLevels(serverVersion)
  13. if err != nil {
  14. return err
  15. }
  16. log.Infof("Server Levels: %+v", s.levels)
  17. s.mux = gmux.NewRouter()
  18. // Initialize the config
  19. err = s.initConfig()
  20. if err != nil {
  21. return err
  22. }
  23. // Initialize the default CA last
  24. err = s.initDefaultCA(renew)
  25. if err != nil {
  26. return err
  27. }
  28. // Successful initialization
  29. return nil
  30. }

init()函数中,首先调用initMetrics()函数来初始化一系列的系统参数,之后是initConfig()来初始化配置,initDefaultCA()来初始化CA信息。

initConfig()函数里面内容很简单,这里就不赘述了。

initDefaultCA()函数中首先调用initCA()来创建一个CA,之后再addCA()到server中。在initCA()中,

  1. // fabric-ca/lib/ca.go
  2. // CA represents a certificate authority which signs, issues and revokes certificates
  3. type CA struct {
  4. // The home directory for the CA
  5. HomeDir string
  6. // The CA's configuration
  7. Config *CAConfig
  8. // The file path of the config file
  9. ConfigFilePath string
  10. // The database handle used to store certificates and optionally
  11. // the user registry information, unless LDAP it enabled for the
  12. // user registry function.
  13. db db.FabricCADB
  14. // The crypto service provider (BCCSP)
  15. csp bccsp.BCCSP
  16. // The certificate DB accessor
  17. certDBAccessor *CertDBAccessor
  18. // The user registry
  19. registry user.Registry
  20. // The signer used for enrollment
  21. enrollSigner signer.Signer
  22. // Idemix issuer
  23. issuer idemix.Issuer
  24. // The options to use in verifying a signature in token-based authentication
  25. verifyOptions *x509.VerifyOptions
  26. // The attribute manager
  27. attrMgr *attrmgr.Mgr
  28. // The tcert manager for this CA
  29. tcertMgr *tcert.Mgr
  30. // The key tree
  31. keyTree *tcert.KeyTree
  32. // The server hosting this CA
  33. server *Server
  34. // DB levels
  35. levels *dbutil.Levels
  36. // CA mutex
  37. mutex sync.Mutex
  38. }
  39. ...
  40. func initCA(ca *CA, homeDir string, config *CAConfig, server *Server, renew bool) error {
  41. ca.HomeDir = homeDir
  42. ca.Config = config
  43. ca.server = server
  44. err := ca.init(renew)
  45. if err != nil {
  46. return err
  47. }
  48. log.Debug("Initializing Idemix issuer...")
  49. ca.issuer = idemix.NewIssuer(ca.Config.CA.Name, ca.HomeDir,
  50. &ca.Config.Idemix, ca.csp, idemix.NewLib())
  51. err = ca.issuer.Init(renew, ca.db, ca.levels)
  52. if err != nil {
  53. return errors.WithMessage(err, fmt.Sprintf("Failed to initialize Idemix issuer for CA '%s'", err.Error()))
  54. }
  55. return nil
  56. }

调用*CA.init()函数来初始化一个CA服务,之后使用idemix.NewIssuer()实例化一个*CA.issuer对象:

  1. //fabric-ca/lib/server/idemix/issuer.go
  2. type issuer struct {
  3. name string
  4. homeDir string
  5. cfg *Config
  6. idemixLib Lib
  7. db db.FabricCADB
  8. csp bccsp.BCCSP
  9. // The Idemix credential DB accessor
  10. credDBAccessor CredDBAccessor
  11. // idemix issuer credential for the CA
  12. issuerCred IssuerCredential
  13. // A random number used in generation of Idemix nonces and credentials
  14. idemixRand *amcl.RAND
  15. rc RevocationAuthority
  16. nm NonceManager
  17. isInitialized bool
  18. mutex sync.Mutex
  19. }
  20. // NewIssuer returns an object that implements Issuer interface
  21. func NewIssuer(name, homeDir string, config *Config, csp bccsp.BCCSP, idemixLib Lib) Issuer {
  22. issuer := issuer{name: name, homeDir: homeDir, cfg: config, csp: csp, idemixLib: idemixLib}
  23. return &issuer
  24. }
  25. func (i *issuer) Init(renew bool, db db.FabricCADB, levels *dbutil.Levels) error {
  26. if i.isInitialized {
  27. return nil
  28. }
  29. i.mutex.Lock()
  30. defer i.mutex.Unlock()
  31. // After obtaining a lock, check again to see if issuer has been initialized by another thread
  32. if i.isInitialized {
  33. return nil
  34. }
  35. if db == nil || reflect.ValueOf(db).IsNil() || !db.IsInitialized() {
  36. log.Debugf("Returning without initializing Idemix issuer for CA '%s' as the database is not initialized", i.Name())
  37. return nil
  38. }
  39. i.db = db
  40. err := i.cfg.init(i.homeDir)
  41. if err != nil {
  42. return err
  43. }
  44. err = i.initKeyMaterial(renew)
  45. if err != nil {
  46. return err
  47. }
  48. i.credDBAccessor = NewCredentialAccessor(i.db, levels.Credential)
  49. log.Debugf("Intializing revocation authority for issuer '%s'", i.Name())
  50. i.rc, err = NewRevocationAuthority(i, levels.RAInfo)
  51. if err != nil {
  52. return err
  53. }
  54. log.Debugf("Intializing nonce manager for issuer '%s'", i.Name())
  55. i.nm, err = NewNonceManager(i, &wallClock{}, levels.Nonce)
  56. if err != nil {
  57. return err
  58. }
  59. i.isInitialized = true
  60. return nil
  61. }

随后调用issuer.Init()函数来初始化idemix证书服务。

startCmd

startCmd()命令中,调用了*lib.Server.Start()函数:

  1. // fabric-ca/lib/server.go
  2. // Start the fabric-ca server
  3. func (s *Server) Start() (err error) {
  4. log.Infof("Starting server in home directory: %s", s.HomeDir)
  5. s.serveError = nil
  6. if s.listener != nil {
  7. return errors.New("server is already started")
  8. }
  9. // Initialize the server
  10. err = s.init(false)
  11. if err != nil {
  12. err2 := s.closeDB()
  13. if err2 != nil {
  14. log.Errorf("Close DB failed: %s", err2)
  15. }
  16. return err
  17. }
  18. // Register http handlers
  19. s.registerHandlers()
  20. log.Debugf("%d CA instance(s) running on server", len(s.caMap))
  21. // Start operations server
  22. err = s.startOperationsServer()
  23. if err != nil {
  24. return err
  25. }
  26. err = s.Operations.RegisterChecker("server", s)
  27. if err != nil {
  28. return nil
  29. }
  30. // Start listening and serving
  31. err = s.listenAndServe()
  32. if err != nil {
  33. err2 := s.closeDB()
  34. if err2 != nil {
  35. log.Errorf("Close DB failed: %s", err2)
  36. }
  37. return err
  38. }
  39. return nil
  40. }

其中再次调用了init()函数,但这次参数为false,表明这次不用重新初始化默认的CA服务了。之后调用了registerHandlers()函数,用来注册所有提供服务的终端句柄。接着调用startOperationsServer()来开启服务:

  1. // operationsServer defines the contract required for an operations server
  2. type operationsServer interface {
  3. metrics.Provider
  4. Start() error
  5. Stop() error
  6. Addr() string
  7. RegisterChecker(component string, checker healthz.HealthChecker) error
  8. }
  9. func (s *Server) startOperationsServer() error {
  10. err := s.Operations.Start()
  11. if err != nil {
  12. return err
  13. }
  14. return nil
  15. }

startOperationsServer()中,使用了operationsServer.Start(),在operationsServer中定义了server提供操作接口。

之后调用了operationsServer.RegisterChecker()接口来检查server的健康状态。随后,调用了*Server.listenAndServe()开始监听和提供服务:

  1. // Starting listening and serving
  2. func (s *Server) listenAndServe() (err error) {
  3. var listener net.Listener
  4. var clientAuth tls.ClientAuthType
  5. var ok bool
  6. c := s.Config
  7. // Set default listening address and port
  8. if c.Address == "" {
  9. c.Address = DefaultServerAddr
  10. }
  11. if c.Port == 0 {
  12. c.Port = DefaultServerPort
  13. }
  14. addr := net.JoinHostPort(c.Address, strconv.Itoa(c.Port))
  15. var addrStr string
  16. if c.TLS.Enabled {
  17. log.Debug("TLS is enabled")
  18. addrStr = fmt.Sprintf("https://%s", addr)
  19. // If key file is specified and it does not exist or its corresponding certificate file does not exist
  20. // then need to return error and not start the server. The TLS key file is specified when the user
  21. // wants the server to use custom tls key and cert and don't want server to auto generate its own. So,
  22. // when the key file is specified, it must exist on the file system
  23. if c.TLS.KeyFile != "" {
  24. if !util.FileExists(c.TLS.KeyFile) {
  25. return fmt.Errorf("File specified by 'tls.keyfile' does not exist: %s", c.TLS.KeyFile)
  26. }
  27. if !util.FileExists(c.TLS.CertFile) {
  28. return fmt.Errorf("File specified by 'tls.certfile' does not exist: %s", c.TLS.CertFile)
  29. }
  30. log.Debugf("TLS Certificate: %s, TLS Key: %s", c.TLS.CertFile, c.TLS.KeyFile)
  31. } else if !util.FileExists(c.TLS.CertFile) {
  32. // TLS key file is not specified, generate TLS key and cert if they are not already generated
  33. err = s.autoGenerateTLSCertificateKey()
  34. if err != nil {
  35. return fmt.Errorf("Failed to automatically generate TLS certificate and key: %s", err)
  36. }
  37. }
  38. cer, err := util.LoadX509KeyPair(c.TLS.CertFile, c.TLS.KeyFile, s.csp)
  39. if err != nil {
  40. return err
  41. }
  42. if c.TLS.ClientAuth.Type == "" {
  43. c.TLS.ClientAuth.Type = defaultClientAuth
  44. }
  45. log.Debugf("Client authentication type requested: %s", c.TLS.ClientAuth.Type)
  46. authType := strings.ToLower(c.TLS.ClientAuth.Type)
  47. if clientAuth, ok = clientAuthTypes[authType]; !ok {
  48. return errors.New("Invalid client auth type provided")
  49. }
  50. var certPool *x509.CertPool
  51. if authType != defaultClientAuth {
  52. certPool, err = LoadPEMCertPool(c.TLS.ClientAuth.CertFiles)
  53. if err != nil {
  54. return err
  55. }
  56. }
  57. config := &tls.Config{
  58. Certificates: []tls.Certificate{*cer},
  59. ClientAuth: clientAuth,
  60. ClientCAs: certPool,
  61. MinVersion: tls.VersionTLS12,
  62. MaxVersion: tls.VersionTLS12,
  63. CipherSuites: stls.DefaultCipherSuites,
  64. }
  65. listener, err = tls.Listen("tcp", addr, config)
  66. if err != nil {
  67. return errors.Wrapf(err, "TLS listen failed for %s", addrStr)
  68. }
  69. } else {
  70. addrStr = fmt.Sprintf("http://%s", addr)
  71. listener, err = net.Listen("tcp", addr)
  72. if err != nil {
  73. return errors.Wrapf(err, "TCP listen failed for %s", addrStr)
  74. }
  75. }
  76. s.listener = listener
  77. log.Infof("Listening on %s", addrStr)
  78. err = s.checkAndEnableProfiling()
  79. if err != nil {
  80. s.closeListener()
  81. return errors.WithMessage(err, "TCP listen for profiling failed")
  82. }
  83. // Start serving requests, either blocking or non-blocking
  84. if s.BlockingStart {
  85. return s.serve()
  86. }
  87. s.wait = make(chan bool)
  88. go s.serve()
  89. return nil
  90. }
  91. func (s *Server) serve() error {
  92. listener := s.listener
  93. if listener == nil {
  94. // This can happen as follows:
  95. // 1) listenAndServe above is called with s.BlockingStart set to false
  96. // and returns to the caller
  97. // 2) the caller immediately calls s.Stop, which sets s.listener to nil
  98. // 3) the go routine runs and calls this function
  99. // So this prevents the panic which was reported in
  100. // in https://jira.hyperledger.org/browse/FAB-3100.
  101. return nil
  102. }
  103. s.serveError = http.Serve(listener, s.mux)
  104. log.Errorf("Server has stopped serving: %s", s.serveError)
  105. s.closeListener()
  106. err := s.closeDB()
  107. if err != nil {
  108. log.Errorf("Close DB failed: %s", err)
  109. }
  110. if s.wait != nil {
  111. s.wait <- true
  112. }
  113. return s.serveError
  114. }

从上面的函数可以看出,Fabric-ca支持TLS服务。

在函数中调用checkAndEnableProfiling()来检查FABRIC_CA_SERVER_PROFILE_PORT是否可用:

  1. // checkAndEnableProfiling checks for FABRIC_CA_SERVER_PROFILE_PORT env variable
  2. // if it is set, starts listening for profiling requests at the port specified
  3. // by the environment variable
  4. func (s *Server) checkAndEnableProfiling() error {
  5. // Start listening for profile requests
  6. pport := os.Getenv(fabricCAServerProfilePort)
  7. if pport != "" {
  8. iport, err := strconv.Atoi(pport)
  9. if err != nil || iport < 0 {
  10. log.Warningf("Profile port specified by the %s environment variable is not a valid port, not enabling profiling",
  11. fabricCAServerProfilePort)
  12. } else {
  13. addr := net.JoinHostPort(s.Config.Address, pport)
  14. listener, err1 := net.Listen("tcp", addr)
  15. log.Infof("Profiling enabled; listening for profile requests on port %s", pport)
  16. if err1 != nil {
  17. return err1
  18. }
  19. go func() {
  20. log.Debugf("Profiling enabled; waiting for profile requests on port %s", pport)
  21. err := http.Serve(listener, nil)
  22. log.Errorf("Stopped serving for profiling requests on port %s: %s", pport, err)
  23. }()
  24. }
  25. }
  26. return nil
  27. }

最后调用server(),启动服务。

至此,Fabric-ca server端的启动过程就完成了。当然,文章中省略了很多细节,比如服务的初始化过程、默认CA的生成过程、idemix的issuer证书生成过程等等,这些过程就需要各位自行了解了。

Fabric-ca server端初始化过程源码分析的更多相关文章

  1. Bootstrap初始化过程源码分析--netty客户端的启动

    Bootstrap初始化过程 netty的客户端引导类是Bootstrap,我们看一下spark的rpc中客户端部分对Bootstrap的初始化过程 TransportClientFactory.cr ...

  2. A2dp初始化流程源码分析

    蓝牙启动的时候,会涉及到各个profile 的启动.这篇文章分析一下,蓝牙中a2dp profile的初始化流程. 我们从AdapterState.java中对于USER_TURN_ON 消息的处理说 ...

  3. A2dp sink 初始化流程源码分析

    A2dp sink的初始化流程和A2dp 的初始化流程,基本一样,这里做简单分析.这里分析的android的版本是Android O. 我们先从service的启动说起吧. 下面 是启动的时候的log ...

  4. Netty入门一:服务端应用搭建 & 启动过程源码分析

    最近周末也没啥事就学学Netty,同时打算写一些博客记录一下(写的过程理解更加深刻了) 本文主要从三个方法来呈现:Netty核心组件简介.Netty服务端创建.Netty启动过程源码分析 如果你对Ne ...

  5. SpringSecurity 初始化流程源码

    SpringSecurity 初始化流程源码 本篇主要讲解 SpringSecurity初始化流程的源码部分,包括核心的 springSecurityFilterChain 是如何创建的,以及在介绍哪 ...

  6. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  7. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  8. Dubbo消费方服务调用过程源码分析

    参考:dubbo消费方服务调用过程源码分析dubbo基于spring的构建分析Dubbo概述--调用过程dubbo 请求调用过程分析dubbo集群容错机制代码分析1dubbo集群容错策略的代码分析2d ...

  9. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  10. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

随机推荐

  1. 火山引擎DataLeap如何解决SLA治理难题(一):应用场景与核心概念介绍

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 基于火山引擎分布式治理的理念,数据平台数据治理团队自研了火山引擎DataLeap SLA保障平台,目前已在字节内部 ...

  2. Solon 小技巧收集 - 页面跳转(重定向)

    @XMapping("/") public void jump(XContext ctx){ ctx.redirect("http://www.noear.org&quo ...

  3. Java 事件链

    Java中的事件机制的参与者有3种角色: 1. event object:就是事件产生时具体的"事件",用于listener的相应的方法之中,作为参数,一般存在于listerner ...

  4. #1198:Farm Irrigation(DFS + 并查集)

    Farm Irrigation **Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) T ...

  5. POJ 1456 Supermarket【贪心 + 并查集】

    http://poj.org/problem?id=1456 题意:给你 N 件不同的商品,每件商品最多可以买一次.每件物品对应两个值 pi di pi 表示物品的价值,di 表示可以买的最迟时间(也 ...

  6. vivo 商城前端架构升级-总览篇

    本文首发于 vivo互联网技术 微信公众号链接: https://mp.weixin.qq.com/s/vD9yvYNaxTQBLABik6aqNg作者:官网商城前端团队 [背景] 一年前 vivo ...

  7. 5G“乍到”,图扑带你了解室内定位可视化的实现与新突破

    前言 现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的  ...

  8. 5 Englishi 词根

    词根 1 ced/cess = go 行走 precede    pre=before   ced =go unprecedentedly  un   pre  ced +ed变成adj  +ly 变 ...

  9. Webpack Vue瘦身,感受快到飞起的加载速度!

    症结 在利用webpack脚手架搭建vue项目后,往往最终打包的.js和.css文件过于庞大,造成首次加载的时候白屏时间过长,影响用户体验,下图为未经任何优化直接npm run build之后的情况: ...

  10. SNMP 使用总结

    转载请注明出处: 1.SNMP简介 SNMP(Simple Network Management Protocol,简单网络管理协议)是一种用于网络设备和系统的管理协议.它允许网络管理员监控和管理网络 ...