Thanos源码专题【左扬精讲】——Thanos main.go(release-0.26)源码阅读和分析(详解 cmd/main.go )
Thanos源码专题精讲——Thanos main.go(release-0.26)源码阅读和分析(详解 cmd/main.go )
https://github.com/thanos-io/thanos/blob/v0.26.0/cmd/thanos/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 | // Copyright (c) The Thanos Authors. // Licensed under the Apache License 2.0. package main import ( "context" "fmt" "io" "os" "os/signal" "path/filepath" "runtime" "runtime/debug" "syscall" "github.com/go-kit/log" // 导入go-kit日志包 "github.com/go-kit/log/level" // 导入go-kit日志级别包 "github.com/oklog/run" // 导入oklog运行包 "github.com/opentracing/opentracing-go" // 导入opentracing-go包 "github.com/pkg/errors" // 导入错误处理包 "github.com/prometheus/client_golang/prometheus" // 导入prometheus客户端包 "github.com/prometheus/client_golang/prometheus/collectors" // 导入prometheus客户端收集器包 "github.com/prometheus/common/version" // 导入prometheus版本包 "go.uber.org/automaxprocs/maxprocs" // 导入自动调整最大进程数包 "gopkg.in/alecthomas/kingpin.v2" // 导入kingpin命令行参数包 "github.com/thanos-io/thanos/pkg/extkingpin" // 导入扩展kingpin命令行参数包 "github.com/thanos-io/thanos/pkg/logging" // 导入日志包 "github.com/thanos-io/thanos/pkg/tracing/client" // 导入追踪客户端包 ) // main 函数是程序的入口点。它执行以下步骤: // 1. 设置在内存访问错误时发生 panic。 // 2. 根据环境变量 DEBUG 设置互斥锁和阻塞剖析。 // 3. 创建一个新的应用程序实例。 // 4. 添加一些命令行标志,包括调试名称、日志级别、日志格式和追踪配置。 // 5. 注册应用程序的不同组件。 // 6. 解析命令行参数。 // 7. 根据日志级别和格式创建一个新的日志记录器。 // 8. 根据容器中的 CPU 限制自动调整 GOMAXPROCS。 // 9. 创建一个新的度量标准注册表,并注册一些收集器。 // 10. 创建一个新的组来运行命令。 // 11. 创建一个新的追踪器,并设置全局追踪器。 // 12. 创建一个信号通道来分发重新加载事件到子命令。 // 13. 设置命令。 // 14. 监听终止信号。 // 15. 监听重新加载信号。 // 16. 运行组。 // 17. 在退出时打印一条信息。 func main() { // We use mmaped resources in most of the components so hardcode PanicOnFault to true. This allows us to recover (if we can e.g if queries // are temporarily accessing unmapped memory). // 设置内存访问错误时发生 panic,这样程序在访问非法内存时会终止,以便快速发现问题。 debug.SetPanicOnFault(true) // If the DEBUG environment variable is set, enable mutex and block profiling. // 如果环境变量 DEBUG 被设置,则启用互斥锁和阻塞剖析,帮助开发者调试并发问题 if os.Getenv( "DEBUG" ) != "" { runtime.SetMutexProfileFraction(10) runtime.SetBlockProfileRate(10) } // Create a new app with the given name and version. 创建一个新的应用程序实例,并设置其名称和版本。这将用于解析命令行参数、注册子命令等。 app := extkingpin.NewApp(kingpin.New(filepath.Base(os.Args[0]), "A block storage based long-term storage for Prometheus." ).Version(version.Print( "thanos" ))) // Create a flag for the debug name. 创建一个标志,用于指定调试名称。这将作为日志行前缀添加到日志中,有助于在多实例环境中区分不同进程的输出。 debugName := app.Flag( "debug.name" , "Name to add as prefix to log lines." ).Hidden().String() // Create a flag for the log level. 创建一个标志,用于指定日志级别。这将控制记录到标准输出的消息的详细程度。 logLevel := app.Flag( "log.level" , "Log filtering level." ). Default( "info" ).Enum( "error" , "warn" , "info" , "debug" ) // Create a flag for the log format. 创建一个标志,用于指定日志格式。这将控制日志消息的输出格式。例如,logfmt 格式更适合人类阅读,而 json 格式更适用于机器处理。 logFormat := app.Flag( "log.format" , "Log format to use. Possible options: logfmt or json." ). Default(logging.LogFormatLogfmt).Enum(logging.LogFormatLogfmt, logging.LogFormatJSON) // Create a flag for the tracing configuration. 创建一个标志,用于指定跟踪配置。这将允许用户指定要使用的跟踪后端和相关的配置选项。例如,Jaeger、Zipkin 或 Datadog。 tracingConfig := extkingpin.RegisterCommonTracingFlags(app) // Register the different components of the app. // 【问】这里为什么要注册这么多组件? // 【答】组件化设计使得每个功能模块可以独立配置、启用或禁用,增强了可维护性和可扩展性。每个 register* 函数负责为应用程序注册一个独立的组件。组件化的设计让每个功能模块可以灵活配置和扩展。 registerSidecar(app) // 注册边车组件。 registerStore(app) // 注册存储组件。 registerQuery(app) // 注册查询组件。 registerRule(app) // 注册规则组件。 registerCompact(app) // 注册压缩组件。 registerTools(app) // 注册工具组件。 registerReceive(app) // 注册接收组件。 registerQueryFrontend(app) // 注册查询前端组件。 // Parse the command line arguments. 解析命令行参数。这将根据提供的标志和子命令设置应用程序的配置。 cmd, setup := app.Parse() // Create a new logger with the given log level and format. logger := logging.NewLogger(*logLevel, *logFormat, *debugName) // Running in container with limits but with empty/wrong value of GOMAXPROCS env var could lead to throttling by cpu // maxprocs will automate adjustment by using cgroups info about cpu limit if it set as value for runtime.GOMAXPROCS. // 自动调整 GOMAXPROCS,适应容器的 CPU 限制,避免因设置错误导致的性能问题 undo, err := maxprocs.Set(maxprocs.Logger( func (template string, args ... interface {}) { level.Debug(logger).Log( "msg" , fmt.Sprintf(template, args)) })) // undo 是一个函数,用于撤销 maxprocs.Set() 的设置。如果设置了 GOMAXPROCS 环境变量,它将使用 cgroups 信息自动调整。如果没有设置或值为空/错误,则不会进行任何更改。 defer undo() if err != nil { level.Warn(logger).Log( "warn" , errors.Wrapf(err, "failed to set GOMAXPROCS: %v" , err)) // 如果err不为nil,则记录警告日志 } // Create a new metrics registry. // 创建 Prometheus 的度量标准注册表,收集应用程序的各项性能指标(如 CPU、内存、进程信息) metrics := prometheus.NewRegistry() // Register the collectors to the metrics registry. // 创建 Prometheus 度量标准注册表并注册相关收集器,这样应用就能自动监控 CPU 使用率、内存使用情况等。 metrics.MustRegister( version.NewCollector( "thanos" ), // 应用版本收集器 collectors.NewGoCollector(), // Go 运行时指标收集器 collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), // 进程指标收集器 ) // Some packages still use default Register. Replace to have those metrics. // 将默认的注册器替换为 metrics,以便收集这些指标。这将确保所有指标都被收集,有助于监控应用程序的性能和健康状况。 prometheus.DefaultRegisterer = metrics // Create a new group to run the commands. // 创建一个新的 goroutine 组,用于运行命令。 var g run.Group // Create a new tracer. // 设置追踪器(tracing),可选的分布式追踪配置 var tracer opentracing.Tracer // Setup optional tracing. { var ( ctx = context.Background() closer io.Closer confContentYaml []byte ) confContentYaml, err = tracingConfig.Content() if err != nil { level.Error(logger).Log( "msg" , "getting tracing config failed" , "err" , err) os.Exit(1) } if len(confContentYaml) == 0 { tracer = client.NoopTracer() // 如果没有配置追踪,则使用 NoopTracer } else { tracer, closer, err = client.NewTracer(ctx, logger, metrics, confContentYaml) if err != nil { fmt.Fprintln(os.Stderr, errors.Wrapf(err, "tracing failed" )) os.Exit(1) } } // This is bad, but Prometheus does not support any other tracer injections than just global one. // TODO(bplotka): Work with basictracer to handle gracefully tracker mismatches, and also with Prometheus to allow // tracer injection. // 这是不好的,但 Prometheus 不支持除了全局追踪器之外的任何追踪器注入。 opentracing.SetGlobalTracer(tracer) ctx, cancel := context.WithCancel(ctx) // 创建一个带有取消功能的上下文,用于处理信号和取消操作 g.Add( func () error { <-ctx.Done() // 等待上下文取消信号 return ctx.Err() }, func (error) { if closer != nil { if err := closer.Close(); err != nil { // 关闭追踪器失败 level.Warn(logger).Log( "msg" , "closing tracer failed" , "err" , err) } } cancel() }) } // Create a signal channel to dispatch reload events to sub-commands. // 创建信号通道,处理程序终止(SIGINT, SIGTERM)和重新加载(SIGHUP)信号 reloadCh := make( chan struct {}, 1) // Setup the command. if err := setup(&g, logger, metrics, tracer, reloadCh, *logLevel == "debug" ); err != nil { // Use %+v for github.com/pkg/errors error to print with stack. level.Error(logger).Log( "err" , fmt.Sprintf( "%+v" , errors.Wrapf(err, "preparing %s command failed" , cmd))) os.Exit(1) } // Listen for termination signals. // 设置中断信号和重载信号处理函数,以便在接收到中断或重载信号时优雅地关闭应用程序。 { cancel := make( chan struct {}) g.Add( func () error { return interrupt(logger, cancel) // interrupt 函数处理程序终止信号(如 SIGINT, SIGTERM)。 }, func (error) { close(cancel) }) } // Listen for reload signals. { cancel := make( chan struct {}) g.Add( func () error { return reload(logger, cancel, reloadCh) // reload 函数处理重新加载信号(如 SIGHUP)。 }, func (error) { close(cancel) }) } // Run the group. // 运行所有添加到运行组中的任务,直到所有任务都完成或发生错误。 if err := g.Run(); err != nil { // Use %+v for github.com/pkg/errors error to print with stack. // 打印错误信息并退出程序。使用 %+v 格式化错误信息,以便包含堆栈跟踪。 level.Error(logger).Log( "err" , fmt.Sprintf( "%+v" , errors.Wrapf(err, "%s command failed" , cmd))) os.Exit(1) } level.Info(logger).Log( "msg" , "exiting" ) } // interrupt 函数用于处理中断信号和取消信号 // // 参数: // logger - 日志记录器,用于记录日志 // cancel - 一个只读通道,用于接收取消信号 // // 返回值: // error - 如果接收到取消信号,则返回错误信息;否则返回 nil func interrupt(logger log.Logger, cancel <- chan struct {}) error { // 创建一个信号通道 c := make( chan os.Signal, 1) // 监听 SIGINT 和 SIGTERM 信号 signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) select { case s := <-c: // 接收到信号,记录日志并返回 nil // 捕获到信号。正在退出。 level.Info(logger).Log( "msg" , "caught signal. Exiting." , "signal" , s) return nil case <-cancel: // 接收到取消信号,返回错误 // 取消 return errors.New( "canceled" ) } } // reload 函数用于监听操作系统信号,并在接收到 SIGHUP 信号时重新加载配置。 // // 参数: // - logger: log.Logger 类型,用于记录日志。 // - cancel: <-chan struct{} 类型,用于接收取消信号。 // - r: chan<- struct{} 类型,用于发送重新加载信号。 // // 返回值: // - error 类型,如果发生错误则返回错误信息,否则返回 nil。 func reload(logger log.Logger, cancel <- chan struct {}, r chan <- struct {}) error { // 创建一个长度为1的通道,用于接收信号 c := make( chan os.Signal, 1) // 通知通道c接收SIGHUP信号 signal.Notify(c, syscall.SIGHUP) for { select { case s := <-c: // 打印日志,表示捕获到信号,正在重新加载 level.Info(logger).Log( "msg" , "caught signal. Reloading." , "signal" , s) select { case r <- struct {}{}: // 打印日志,表示重新加载已调度 level.Info(logger).Log( "msg" , "reload dispatched." ) default : } case <-cancel: // 如果接收到取消信号,则返回错误 return errors.New( "canceled" ) } } } // getFlagsMap 将 kingpin.FlagModel 类型的切片转换为 map[string]string 类型 // 参数: // - flags: kingpin.FlagModel 类型的切片,包含所有标志位信息 // // 返回值: // - map[string]string: 包含所有非默认标志位名称及其值的映射 func getFlagsMap(flags []*kingpin.FlagModel) map [string]string { flagsMap := map [string]string{} // 创建一个空的kingpin命令行参数解析器实例,用于排除默认标志位 // Exclude kingpin default flags to expose only Thanos ones. boilerplateFlags := kingpin.New( "" , "" ).Version( "" ) // 遍历所有标志位 for _, f := range flags { // 如果标志位是默认标志位,则跳过 if boilerplateFlags.GetFlag(f.Name) != nil { continue } // 将非默认标志位添加到flagsMap中 flagsMap[f.Name] = f.Value.String() } return flagsMap } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2022-02-07 Go从入门到精通——指针
2022-02-07 Go从入门到精通——搭建Go开发环境
2022-02-07 Go从入门到精通——如何安装 Go 语言开发包?
2021-02-07 RocketMQ(4.8.0)——生产者
2021-02-07 RocketMQ概述