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
}
posted @   左扬  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源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概述
levels of contents
点击右上角即可分享
微信分享提示