Thanos源码专题【左扬精讲】——Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\api.go)

Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\api.go)

https://github.com/thanos-io/thanos/blob/v0.26.0/pkg/api/api.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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.
 
// Copyright 2016 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
// This package is a modified copy from
// github.com/prometheus/prometheus/web/api/v1@2121b4628baa7d9d9406aa468712a6a332e77aff.
 
package api
 
import (
    "encoding/json" // for json.Marshal
    "fmt"           // for fmt.Errorf
    "net/http"      // for http.Server
    "os"            // for os.Exit
    "runtime"       // for runtime.NumCPU
    "time"          // for time.Duration
 
    "github.com/NYTimes/gziphandler"        // for gziphandler.GzipHandler
    "github.com/go-kit/log"                 // for log.Logger
    "github.com/go-kit/log/level"           // for level.Info
    "github.com/opentracing/opentracing-go" // for opentracing.Tracer
    "github.com/prometheus/common/route"    // for route.NewRoute
    "github.com/prometheus/common/version"  // for version.Version
 
    extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" // for extpromhttp.MetricsHandler
    "github.com/thanos-io/thanos/pkg/logging"                  // for logging.WithContext
    "github.com/thanos-io/thanos/pkg/server/http/middleware"   // for middleware.Tracing
    "github.com/thanos-io/thanos/pkg/tracing"                  // for tracing.Tracer
)
 
type status string
 
const (
    StatusSuccess status = "success"
    StatusError   status = "error"
)
 
type ErrorType string
 
const (
    ErrorNone     ErrorType = ""
    ErrorTimeout  ErrorType = "timeout"   // 超时错误
    ErrorCanceled ErrorType = "canceled"  // 取消错误
    ErrorExec     ErrorType = "execution" // 执行错误
    ErrorBadData  ErrorType = "bad_data"  // 错误数据
    ErrorInternal ErrorType = "internal"  // 内部错误
)
 
// corsHeaders 是 CORS 头部的默认值。
var corsHeaders = map[string]string{
    "Access-Control-Allow-Headers""Accept, Accept-Encoding, Authorization, Content-Type, Origin",
    "Access-Control-Allow-Methods""GET, OPTIONS",
    "Access-Control-Allow-Origin":   "*",
    "Access-Control-Expose-Headers": "Date",
}
 
// ThanosVersion contains build information about Thanos.
// ThanosVersion 包含构建信息。比如版本号、修订版、分支、构建用户、构建日期、Go版本。
type ThanosVersion struct {
    Version   string `json:"version"`   // 版本号
    Revision  string `json:"revision"// 修订版
    Branch    string `json:"branch"`    // 分支
    BuildUser string `json:"buildUser"` // 构建用户
    BuildDate string `json:"buildDate"` // 构建日期
    GoVersion string `json:"goVersion"` // Go版本号
}
 
// BuildInfo 是初始化的 ThanosVersion 对象。
var BuildInfo = &ThanosVersion{
    Version:   version.Version,   // 版本号,比如 v0.18.0
    Revision:  version.Revision,  // 修订版,比如 123456789abcdef0
    Branch:    version.Branch,    // 分支,比如 master
    BuildUser: version.BuildUser, // 构建用户,比如 root@localhost
    BuildDate: version.BuildDate, // 构建日期,比如 2020-01-01T00:00:00Z
    GoVersion: version.GoVersion, // Go的版本号,比如 go-v1.24.0
}
 
type ApiError struct {
    Typ ErrorType
    Err error
}
 
// Error 返回API错误的字符串表示。
// 使用fmt.Sprintf格式化字符串,将e.Typ和e.Err拼接成一个错误消息。
func (e *ApiError) Error() string {
    // 使用fmt.Sprintf格式化字符串,将e.Typ和e.Err拼接成一个错误消息
    return fmt.Sprintf("%s: %s", e.Typ, e.Err)
}
 
// RuntimeInfo contains runtime information about Thanos.
type RuntimeInfo struct {
    StartTime      time.Time `json:"startTime"`      // 启动时间
    CWD            string    `json:"CWD"`            // 当前工作目录
    GoroutineCount int       `json:"goroutineCount"` // 协程数量
    GOMAXPROCS     int       `json:"GOMAXPROCS"`     // GOMAXPROCS的值,表示可以同时运行的协程数量
    GOGC           string    `json:"GOGC"`           // GOGC的值,表示垃圾回收的目标百分比
    GODEBUG        string    `json:"GODEBUG"`        // GODEBUG的值,表示调试标志
}
 
// RuntimeInfoFn returns updated runtime information about Thanos.
// 返回一个RuntimeInfo结构体,包含Thanos的运行时信息。
type RuntimeInfoFn func() RuntimeInfo
 
type response struct {
    Status    status      `json:"status"`              // 状态,比如 "success" 或 "error"
    Data      interface{} `json:"data,omitempty"`      // 数据,比如查询结果
    ErrorType ErrorType   `json:"errorType,omitempty"` // 错误类型,比如 "timeout" 或 "internal"
    Error     string      `json:"error,omitempty"`     // 错误信息,比如 "timeout: context canceled"
    Warnings  []string    `json:"warnings,omitempty"// 警告信息,比如 "some warning"
}
 
// SetCORS enables cross-site script calls.
// SetCORS 函数用于设置跨源资源共享(CORS)相关的响应头。
//
// 参数:
// w http.ResponseWriter: 用于写入响应的HTTP响应写入器。
func SetCORS(w http.ResponseWriter) {
    // 遍历 corsHeaders 映射
    for h, v := range corsHeaders {
        // 设置响应头,键为 h,值为 v
        w.Header().Set(h, v)
    }
}
 
// ApiFunc 是一个函数类型,用于处理HTTP请求,并返回响应数据、警告信息以及可能的错误。
type ApiFunc func(r *http.Request) (interface{}, []error, *ApiError)
 
// BaseAPI 是一个HTTP API的基础类型,用于处理HTTP请求,并返回响应数据、警告信息以及可能的错误。
type BaseAPI struct {
    logger      log.Logger        // 日志记录器,用于记录日志信息。
    flagsMap    map[string]string // 标志映射,用于存储命令行标志的值。
    runtimeInfo RuntimeInfoFn     // 运行时信息获取函数,用于获取Thanos的当前运行状态。
    buildInfo   *ThanosVersion    // 构建信息,包含Thanos的版本、修订版等信息。
    Now         func() time.Time  // 当前时间的获取函数,用于记录请求处理的时间。
    disableCORS bool              // 是否禁用CORS,如果为true,则不会设置CORS相关的响应头。
}
 
// NewBaseAPI returns a new initialized BaseAPI type.
// NewBaseAPI 创建一个新的 BaseAPI 实例
//
// 参数:
//
//  logger: 日志记录器
//  disableCORS: 是否禁用CORS
//  flagsMap: 标志映射
//
// 返回值:
//
//  返回一个指向 BaseAPI 实例的指针
func NewBaseAPI(logger log.Logger, disableCORS bool, flagsMap map[string]string) *BaseAPI {
 
    // 初始化BaseAPI结构体
    return &BaseAPI{
        // 日志记录器
        logger: logger,
        // 标志映射
        flagsMap: flagsMap,
        // 运行时信息获取函数
        runtimeInfo: GetRuntimeInfoFunc(logger),
        // 构建信息
        buildInfo: BuildInfo,
        // 是否禁用CORS
        disableCORS: disableCORS,
        // 获取当前时间函数
        Now: time.Now,
    }
}
 
// Register registers the common API endpoints.
// Register 方法用于注册HTTP路由处理函数
//
// 参数:
//
//  r: HTTP路由对象
//  tracer: OpenTracing的Tracer对象
//  logger: 日志记录器对象
//  ins: 仪器化中间件
//  logMiddleware: HTTP服务器日志中间件
//
// 说明:
//
//  该方法会将不同的HTTP请求路径和处理函数绑定到HTTP路由对象上。
//  通过仪器化中间件对请求进行监控和日志记录。
func (api *BaseAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) {
    // 获取仪器化中间件
    instr := GetInstr(tracer, logger, ins, logMiddleware, api.disableCORS)
 
    // 注册 OPTIONS 方法处理函数
    r.Options("/*path", instr("options", api.options))
 
    // 注册获取状态标志的方法处理函数
    r.Get("/status/flags", instr("status_flags", api.flags))
    // 注册获取运行时信息的方法处理函数
    r.Get("/status/runtimeinfo", instr("status_runtime", api.serveRuntimeInfo))
    // 注册获取构建信息的方法处理函数
    r.Get("/status/buildinfo", instr("status_build", api.serveBuildInfo))
}
 
// options 方法目前不执行任何操作,直接返回三个空值
//
// 参数:
//
//  r *http.Request: 传入的HTTP请求
//
// 返回值:
//
//  interface{}: 返回的接口类型的值,此处为空
//  []error: 返回的错误列表,此处为空
//  *ApiError: 返回的API错误信息,此处为空
func (api *BaseAPI) options(r *http.Request) (interface{}, []error, *ApiError) {
    // 此函数目前不执行任何操作,直接返回三个空值
    return nil, nil, nil
}
 
// flags 函数用于获取 BaseAPI 实例的 flagsMap。
//
// 参数:
//
//  r *http.Request: HTTP 请求对象,此参数在本函数中未被使用。
//
// 返回值:
//
//  interface{}: 返回 api 的 flagsMap。
//  []error: 返回错误信息列表,本函数始终返回 nil。
//  *ApiError: 返回 ApiError 对象,本函数始终返回 nil。
func (api *BaseAPI) flags(r *http.Request) (interface{}, []error, *ApiError) {
    // 直接返回api的flagsMap,错误信息列表为空,ApiError为空
    return api.flagsMap, nil, nil
}
 
// serveRuntimeInfo 处理HTTP请求,返回运行时信息
//
// 参数:
//
//  r *http.Request: HTTP请求对象
//
// 返回值:
//
//  interface{}: 返回的运行时信息
//  []error: 错误列表,如果没有错误则返回nil
//  *ApiError: Api错误对象,如果没有错误则返回nil
func (api *BaseAPI) serveRuntimeInfo(r *http.Request) (interface{}, []error, *ApiError) {
    // 调用api的runtimeInfo方法获取运行时信息
    return api.runtimeInfo(), nil, nil
}
 
// serveBuildInfo 是 BaseAPI 结构体的方法,用于处理 HTTP 请求并返回构建信息。
//
// 参数:
//
//  r *http.Request: 传入的 HTTP 请求对象。
//
// 返回值:
//
//  interface{}: 返回的构建信息,类型为 BaseAPI 结构体中的 buildInfo 字段。
//  []error: 错误列表,本方法直接返回 nil,表示无错误。
//  *ApiError: ApiError 指针,本方法直接返回 nil,表示无 ApiError。
func (api *BaseAPI) serveBuildInfo(r *http.Request) (interface{}, []error, *ApiError) {
    // 直接返回buildInfo信息,无错误,也无ApiError
    return api.buildInfo, nil, nil
}
 
// GetRuntimeInfoFunc 返回一个返回 RuntimeInfo 类型的函数,该函数包含有关运行时信息。
//
// 参数:
//
//  logger: log.Logger 类型,用于记录日志
//
// 返回值:
//
//  RuntimeInfoFn 类型,一个返回 RuntimeInfo 类型的函数
func GetRuntimeInfoFunc(logger log.Logger) RuntimeInfoFn {
    // 获取当前工作目录
    CWD, err := os.Getwd()
    if err != nil {
        // 如果获取当前工作目录失败,则将CWD设置为错误信息
        CWD = "<error retrieving current working directory>"
        // 记录警告日志
        level.Warn(logger).Log("msg", "failed to retrieve current working directory", "err", err)
    }
 
    // 记录程序启动时间
    birth := time.Now()
 
    // 返回获取运行时信息的函数
    return func() RuntimeInfo {
        return RuntimeInfo{
            // 程序启动时间
            StartTime: birth,
            // 当前工作目录
            CWD: CWD,
            // 当前 Goroutine 数量
            GoroutineCount: runtime.NumGoroutine(),
            // GOMAXPROCS 的值
            GOMAXPROCS: runtime.GOMAXPROCS(0),
            // GOGC 的值
            GOGC: os.Getenv("GOGC"),
            // GODEBUG 的值
            GODEBUG: os.Getenv("GODEBUG"),
        }
    }
}
 
// InstrFunc 是一个函数类型,它接收一个字符串和一个 ApiFunc 作为参数,并返回一个 http.HandlerFunc。
type InstrFunc func(name string, f ApiFunc) http.HandlerFunc
 
// GetInstr returns a http HandlerFunc with the instrumentation middleware.
func GetInstr(
    tracer opentracing.Tracer, // OpenTracing的Tracer对象
    logger log.Logger, // 日志记录器
    ins extpromhttp.InstrumentationMiddleware, // Prometheus的InstrumentationMiddleware
    logMiddleware *logging.HTTPServerMiddleware, // 日志中间件
    disableCORS bool, // 是否禁用CORS
) InstrFunc {
    instr := func(name string, f ApiFunc) http.HandlerFunc {
        // 定义HTTP处理函数
        hf := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // 如果不禁用CORS,则设置CORS响应头
            if !disableCORS {
                // 设置CORS响应头
                SetCORS(w)
            }
 
            // 调用ApiFunc处理请求
            if data, warnings, err := f(r); err != nil {
                // 如果发生错误,返回错误响应
                RespondError(w, err, data)
            } else if data != nil {
                // 如果返回数据不为空,返回正常响应
                Respond(w, data, warnings)
            } else {
                // 如果没有返回数据,返回204 No Content状态码
                w.WriteHeader(http.StatusNoContent)
            }
        })
 
        // 返回带有追踪功能的HTTP中间件
        return tracing.HTTPMiddleware(tracer, name, logger,
            ins.NewHandler(name,
                // 使用gzip压缩处理
                gziphandler.GzipHandler(
                    // 使用RequestID中间件
                    middleware.RequestID(
                        // 使用日志中间件处理
                        logMiddleware.HTTPMiddleware(name, hf),
                    ),
                ),
            ),
        )
    }
    return instr
}
 
// Respond 函数处理 HTTP 响应,将给定数据编码为 JSON 并返回给客户端。
//
// 参数:
//
//  w http.ResponseWriter: HTTP 响应写入器,用于发送响应数据。
//  data interface{}: 要返回给客户端的数据。
//  warnings []error: 警告信息列表,如果列表不为空,将设置 Cache-Control 为 no-store。
func Respond(w http.ResponseWriter, data interface{}, warnings []error) {
    // 设置响应头,指定内容类型为 application/json
    w.Header().Set("Content-Type", "application/json")
 
    // 如果存在警告信息,设置缓存控制为 no-store
    if len(warnings) > 0 {
        w.Header().Set("Cache-Control", "no-store")
    }
 
    // 设置 HTTP 状态码为 200 OK
    w.WriteHeader(http.StatusOK)
 
    // 初始化响应结构体
    resp := &response{
        Status: StatusSuccess,
        Data:   data,
    }
 
    // 遍历警告信息,将其添加到响应结构体的警告列表中
    for _, warn := range warnings {
        resp.Warnings = append(resp.Warnings, warn.Error())
    }
 
    // 将响应结构体编码为 JSON 并写入响应体
    _ = json.NewEncoder(w).Encode(resp)
}
 
// RespondError 函数用于处理错误响应
//
// 参数:
//   - w http.ResponseWriter: HTTP 响应写入器
//   - apiErr *ApiError: 封装了错误信息的结构体指针
//   - data interface{}: 其他需要返回的数据
func RespondError(w http.ResponseWriter, apiErr *ApiError, data interface{}) {
    // 设置响应头的内容类型为 JSON
    w.Header().Set("Content-Type", "application/json")
    // 设置响应头禁止缓存
    w.Header().Set("Cache-Control", "no-store")
 
    var code int
    switch apiErr.Typ {
    case ErrorBadData:
        // 数据错误,返回400 Bad Request
        code = http.StatusBadRequest
    case ErrorExec:
        // 执行错误,返回422 Unprocessable Entity
        code = 422
    case ErrorCanceled, ErrorTimeout:
        // 请求被取消或超时,返回503 Service Unavailable
        code = http.StatusServiceUnavailable
    case ErrorInternal:
        // 内部错误,返回500 Internal Server Error
        code = http.StatusInternalServerError
    default:
        // 默认情况,返回500 Internal Server Error
        code = http.StatusInternalServerError
    }
    // 设置响应状态码
    w.WriteHeader(code)
 
    // 使用 JSON 编码将响应数据写入响应体
    _ = json.NewEncoder(w).Encode(&response{
        // 状态码
        Status: StatusError,
        // 错误类型
        ErrorType: apiErr.Typ,
        // 错误信息
        Error: apiErr.Err.Error(),
        // 其他数据
        Data: data,
    })
}

 

posted @   左扬  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
levels of contents
点击右上角即可分享
微信分享提示