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

// 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 @ 2025-02-13 14:43  左扬  阅读(18)  评论(0)    收藏  举报