InfluxDB源码解析

源码分析按从下至上的,还原是从上至下的。

在influxdb/cmd/influxd/main.go文件,是influxdb服务端程序入口,是服务端main()函数所在处。

fun main() {
   rand.Seed(time.Now().UnixNano())

   m := NewMain()
   if err := m.Run(os.Args[1:]...); err != nil {
      fmt.Fprintln(os.Stderr, err)
      os.Exit(1)
   }
}

调用构造函数NewMain()生成一个Main结构体实例m,再用实例m调用结构体的方法Run(args …String)。

下图方法、函数所在位置:influxdb/cmd/influxd/main.go文件

// Main represents the program execution.
type Main struct {
   Stdin   io.Reader
   Stdout  io.Writer
   Stderr  io.Writer
}

// NewMain return a new instance of Main.
func NewMain() *Main {
   return &Main{
      Stdin:  os.Stdin,
     
Stdout: os.Stdout,
     
Stderr: os.Stderr,
  
}
}

 

// Run determines and runs the command specified by the CLI args.
func (m *Main) Run(args ...string) error {

   name, args := cmd.ParseCommandName(args)
   // Extract name from args.
   switch name {
   case "", "run":
      cmd := run.NewCommand()
      // Tell the server the build details.
      cmd.Version = version
      cmd.Commit = commit
      cmd.Branch = branch

      if err := cmd.Run(args...); err != nil {
         return fmt.Errorf("run: %s", err)
      }

……

在main包的Run方法中调用github.com/influxdata/influxdb/cmd包中的func ParseCommandName(args []string) (string, []string) {}函数(如下图所示),返回命令名称和参数列表。如果第一个参数不带破折号,则直接赋值给name,否则判断是不是-h,-help,--help三个中的一个,是的话就赋值字符串help给变量name。如果第一个参数是help并且参数长度大于2,并且第二个参数不是以破折号开头,则返回第二个参数,和参数列表-h。如果name不为空,则返回name参数列表args[1:]。否则返回空,和参数裂变args。该函数也被其他几个模块的main函数调用,比如influxdb/cmd/influxd/main.go、influxdb/cmd/influxtools/main.go等。

下面函数所在位置:influxdb/cmd/parse.go

// ParseCommandName extracts the command name and args from the args list.
func ParseCommandName(args []string) (string, []string) {
   // Retrieve command name as first argument.
   var name string
   if len(args) > 0 {
      if !strings.HasPrefix(args[0], "-") {
         name = args[0]
      } else if args[0] == "-h" || args[0] == "-help" || args[0] == "--help" {
         // Special case -h immediately following binary name
         name = "help"
      }
   }

   // If command is "help" and has an argument then rewrite args to use "-h".
   if name == "help" && len(args) > 2 && !strings.HasPrefix(args[1], "-") {
      return args[1], []string{"-h"}
   }

   // If a named command is specified then return it with its arguments.
   if name != "" {
      return name, args[1:]
   }
   return "", args
}

在main包的Run方法中,根据命令名称,调用不同模块的,比如,如果是run命令(从源码可以看出,如果没指定命令名称,则默认是run命令),则调用influxdb/cmd/influxd/run包中的NewCommand()构造函数,实例化一个Command结构体的对象cmd,再进行一些初始化,然后利用该对象调用其自身的Run方法。入参是之前在命令行中输入的除了命令名称的参数列表。

下图函数所在位置:influxdb/cmd/influxd/run/command.go文件。

func (cmd *Command) Run(args ...string) error {
   // Parse the command line flags.
   options, err := cmd.ParseFlags(args...)
   if err != nil {
      return err
   }

   config, err := cmd.ParseConfig(options.GetConfigPath())
   if err != nil {
      return fmt.Errorf("parse config: %s", err)
   }

   // Apply any environment variables on top of the parsed config
   if err := config.ApplyEnvOverrides(cmd.Getenv); err != nil {
      return fmt.Errorf("apply env config: %v", err)
   }

   // Validate the configuration.
   if err := config.Validate(); err != nil {
      return fmt.Errorf("%s. To generate a valid configuration file run `influxd config > influxdb.generated.conf`", err)
   }

   var logErr error
   if cmd.Logger, logErr = config.Logging.New(cmd.Stderr); logErr != nil {
      // assign the default logger
      cmd.Logger = logger.New(cmd.Stderr)
   }

   // Attempt to run pprof on :6060 before startup if debug pprof enabled.
   if config.HTTPD.DebugPprofEnabled {
      runtime.SetBlockProfileRate(int(1 * time.Second))
      runtime.SetMutexProfileFraction(1)
      go func() { http.ListenAndServe("localhost:6060", nil) }()
   }

   // Print sweet InfluxDB logo.
   if !config.Logging.SuppressLogo && logger.IsTerminal(cmd.Stdout) {
      fmt.Fprint(cmd.Stdout, logo)
   }

   // Mark start-up in log.
   cmd.Logger.Info("InfluxDB starting",
     
zap.String("version", cmd.Version),
     
zap.String("branch", cmd.Branch),
     
zap.String("commit", cmd.Commit))
   cmd.Logger.Info("Go runtime",
     
zap.String("version", runtime.Version()),
     
zap.Int("maxprocs", runtime.GOMAXPROCS(0)))

   // If there was an error on startup when creating the logger, output it now.
   if logErr != nil {
      cmd.Logger.Error("Unable to configure logger", zap.Error(logErr))
   }

   // Write the PID file.
   if err := cmd.writePIDFile(options.PIDFile); err != nil {
      return fmt.Errorf("write pid file: %s", err)
   }
   cmd.pidfile = options.PIDFile

   if config.HTTPD.PprofEnabled {
      // Turn on block and mutex profiling.
      runtime.SetBlockProfileRate(int(1 * time.Second))
      runtime.SetMutexProfileFraction(1) // Collect every sample
   }

   // Create server from config and start it.
   buildInfo := &BuildInfo{
      Version: cmd.Version,
     
Commit:  cmd.Commit,
     
Branch:  cmd.Branch,
     
Time:    cmd.BuildTime,
  
}
   s, err := NewServer(config, buildInfo)
   if err != nil {
      return fmt.Errorf("create server: %s", err)
   }
   s.Logger = cmd.Logger
   s.CPUProfile = options.CPUProfile
   s.MemProfile = options.MemProfile
   if err := s.Open(); err != nil {
      return fmt.Errorf("open server: %s", err)
   }
   cmd.Server = s

   // Begin monitoring the server's error channel.
   go cmd.monitorServerErrors()

   return nil
}

在run包中的Run(args...)方法中,先调用ParseFlags(args...)方法解析相应的参数,ParseFlags(args...)方法里又调用了flag包来完成解析,并得到配置选项options。比如我们使用-config influxd.conf参数,会得到一个仅含有ConfigPath的Options对象。再利用run包中的Command结构体的ParseConfig(path string)得到Config对象。利用options.GetConfigPath()得到配置文件路径。利用run包下NewServer函数以及之前得到的config和新构造的buildinfo得到influxd/cmd/influxd/run/server.go文件中Server结构体的实例s,最后利用实例s调用s.Open()方法开启服务。

而在Server结构体的Open()方法中,调用Server的appendOpenTSDBService(c opentsdb.Config)方法添加了 opentsdb的服务。实际上是将实现了server.go文件中Service接口的opentsdb.Service实现类添加到run.Service类型的slice里面去。

for _, i := range s.config.OpenTSDBInputs {
   if err := s.appendOpenTSDBService(i); err != nil {
      return err
   }
}

 

func (s *Server) appendOpenTSDBService(c opentsdb.Config) error {
   if !c.Enabled {
      return nil
   }
   srv, err := opentsdb.NewService(c)
   if err != nil {
      return err
   }
   srv.PointsWriter = s.PointsWriter
   srv.MetaClient = s.MetaClient
   s.Services = append(s.Services, srv)
   return nil
}

 

// Service represents a service attached to the server.
type Service interface {
   WithLogger(log *zap.Logger)
   Open() error
   Close() error
}

添加完所有的services之后,使用for range循环对所有的service执行打开服务操作。其中具有的Service有opentsdb.Service、httpd.Service等。利用多态的性质,分别打开每个具体的服务。

for _, service := range s.Services {
   if err := service.Open(); err != nil {
      return fmt.Errorf("open service: %s", err)
   }
}

如下图所示,都是实现了run.Service接口的模块。

InfluxDB源码解析_第1张图片

在httpd模块中,在httpd.NewService(c Config)函数里,实例化Service的时候,同时调用了NewHandler(c Config)函数,实例化了一个Handler,而实例化Handler时,又调用了h.AddRoutes(routes ...Route)方法,AddRoutes实现了对Handler成员变量mux的初始化。

除了run之外的其他命令如下所示:

case "backup":
   name := backup.NewCommand()
   if err := name.Run(args...); err != nil {
      return fmt.Errorf("backup: %s", err)
   }
case "restore":
   name := restore.NewCommand()
   if err := name.Run(args...); err != nil {
      return fmt.Errorf("restore: %s", err)
   }
case "config":
   if err := run.NewPrintConfigCommand().Run(args...); err != nil {
      return fmt.Errorf("config: %s", err)
   }
case "version":
   if err := NewVersionCommand().Run(args...); err != nil {
      return fmt.Errorf("version: %s", err)
   }
case "help":
   if err := help.NewCommand().Run(args...); err != nil {
      return fmt.Errorf("help: %s", err)
   }
default:
   return fmt.Errorf(`unknown command "%s"`+"\n"+`Run 'influxd help' for usage`+"\n\n", name)
}

 

你可能感兴趣的:(笔记,时序数据库,Golang学习,influxdb,源码解析)