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

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

https://github.com/thanos-io/thanos/blob/v0.26.0/pkg/api/query/v1.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 v1

import (
	"context"  // context 用于传递上下文信息,比如取消信号、截止时间等。
	"math"     // math 包提供了基本的数学运算函数,比如加、减、乘、除等。
	"net/http" // net/http 包提供了 HTTP 客户端和服务器的实现。
	"sort"     // sort 包提供了对切片进行排序的函数。
	"strconv"  // strconv 包提供了字符串和基本数据类型之间的转换函数。
	"strings"  // strings 包提供了许多实用的字符串处理函数,比如查找、替换等。
	"time"     // time 包提供了时间的表示、计算和格式化功能。

	"github.com/go-kit/log"                                   // log 包提供了日志记录的功能。
	"github.com/opentracing/opentracing-go"                   // opentracing-go 包提供了分布式追踪的功能。
	"github.com/pkg/errors"                                   // errors 包提供了错误处理的功能。
	"github.com/prometheus/client_golang/prometheus"          // prometheus 包提供了 Prometheus 监控指标的收集和暴露功能。
	"github.com/prometheus/client_golang/prometheus/promauto" // promauto 包提供了 Prometheus 监控指标的自动注册功能。
	"github.com/prometheus/common/model"                      // model 包提供了 Prometheus 数据模型的定义和操作功能。
	"github.com/prometheus/common/route"                      // route 包提供了 HTTP 路由的功能。
	"github.com/prometheus/prometheus/model/labels"           // labels 包提供了 Prometheus 标签的定义和操作功能。
	"github.com/prometheus/prometheus/model/timestamp"        // timestamp 包提供了 Prometheus 时间戳的定义和操作功能。
	"github.com/prometheus/prometheus/promql"                 // promql 包提供了 Prometheus 查询语言的解析和执行功能。
	"github.com/prometheus/prometheus/promql/parser"          // parser 包提供了 Prometheus 查询语言的解析功能。
	"github.com/prometheus/prometheus/storage"                // storage 包提供了 Prometheus 数据存储的功能。

	"github.com/prometheus/prometheus/util/stats" // stats 包提供了 Prometheus 数据统计的功能。

	"github.com/thanos-io/thanos/pkg/api"                      // api 包提供了 Thanos API 的定义和实现功能。
	"github.com/thanos-io/thanos/pkg/exemplars"                // exemplars 包提供了 Thanos 示例数据的功能。
	"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"    // exemplarspb 包提供了 Thanos 示例数据的序列化功能。
	extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" // extpromhttp 包提供了 Thanos 扩展 Prometheus 监控指标的功能。
	"github.com/thanos-io/thanos/pkg/gate"                     // gate 包提供了 Thanos 限流的功能。
	"github.com/thanos-io/thanos/pkg/logging"                  // logging 包提供了 Thanos 日志记录的功能。
	"github.com/thanos-io/thanos/pkg/metadata"                 // metadata 包提供了 Thanos 元数据的功能。
	"github.com/thanos-io/thanos/pkg/metadata/metadatapb"      // metadatapb 包提供了 Thanos 元数据的序列化功能。
	"github.com/thanos-io/thanos/pkg/query"                    // query 包提供了 Thanos query 的功能。
	"github.com/thanos-io/thanos/pkg/rules"                    // rules 包提供了 Thanos rules 的功能。
	"github.com/thanos-io/thanos/pkg/rules/rulespb"            // rulespb 包提供了 Thanos rules 的序列化功能。
	"github.com/thanos-io/thanos/pkg/runutil"                  // runutil 包提供了 Thanos runutil 的功能。
	"github.com/thanos-io/thanos/pkg/store/storepb"            // storepb 包提供了 Thanos store 的序列化功能。
	"github.com/thanos-io/thanos/pkg/targets"                  // targets 包提供了 Thanos target 的功能。
	"github.com/thanos-io/thanos/pkg/targets/targetspb"        // targetspb 包提供了 Thanos target 的序列化功能。
	"github.com/thanos-io/thanos/pkg/tracing"                  // tracing 包提供了 Thanos tracing 的功能。
)

const (
	DedupParam               = "dedup"                 // DedupParam 是用于去重的参数。
	PartialResponseParam     = "partial_response"      // PartialResponseParam 是用于部分响应的参数。
	MaxSourceResolutionParam = "max_source_resolution" // MaxSourceResolutionParam 是用于最大源分辨率的参数。
	ReplicaLabelsParam       = "replicaLabels[]"       // ReplicaLabelsParam 是用于副本标签的参数。
	MatcherParam             = "match[]"               // MatcherParam 是用于匹配的参数。
	StoreMatcherParam        = "storeMatch[]"          // StoreMatcherParam 是用于存储匹配的参数。
	Step                     = "step"                  // Step 是用于步长的参数。
	Stats                    = "stats"                 // Stats 是用于统计的参数。
)

// QueryAPI is an API used by Thanos Querier.
// QueryAPI 类型是用于 Thanos Querier 的 API。
type QueryAPI struct {
	baseAPI         *api.BaseAPI           // 	baseAPI 是 QueryAPI 的基础 API。
	logger          log.Logger             // logger 是用于记录日志的对象。
	gate            gate.Gate              // gate 是用于控制访问的对象。
	queryableCreate query.QueryableCreator // queryableCreate 是用于创建查询对象的函数。
	// queryEngine returns appropriate promql.Engine for a query with a given step.
	// queryEngin 返回一个适用于给定步长的 promql.Engine。
	queryEngine func(int64) *promql.Engine // queryEngine 是一个函数,用于根据给定的步长返回一个 promql.Engine。
	ruleGroups  rules.UnaryClient          // ruleGroups 是用于规则组的对象。
	targets     targets.UnaryClient        // targets 是用于目标的对象。
	metadatas   metadata.UnaryClient       // metadatas 是用于元数据的对象。
	exemplars   exemplars.UnaryClient      // exemplars 是用于示例的对象。

	enableAutodownsampling              bool // enableAutodownsampling 是用于启用自动降采样的参数。
	enableQueryPartialResponse          bool // enableQueryPartialResponse 是用于启用查询部分响应的参数。
	enableRulePartialResponse           bool // enableRulePartialResponse 是用于启用规则部分响应的参数。
	enableTargetPartialResponse         bool // enableTargetPartialResponse 是用于启用目标部分响应的参数。
	enableMetricMetadataPartialResponse bool // enableMetricMetadataPartialResponse 是用于启用指标元数据部分响应的参数。
	enableExemplarPartialResponse       bool // enableExemplarPartialResponse 是用于启用示例部分响应的参数。
	enableQueryPushdown                 bool // enableQueryPushdown 是用于启用查询下推的参数。
	disableCORS                         bool // disableCORS 是用于禁用 CORS 的参数。

	replicaLabels  []string                      // replicaLabels 是用于副本标签的参数。
	endpointStatus func() []query.EndpointStatus // endpointStatus 是用于获取端点状态的函数。

	defaultRangeQueryStep                  time.Duration        // defaultRangeQueryStep 是用于默认范围查询步长的参数。
	defaultInstantQueryMaxSourceResolution time.Duration        // defaultInstantQueryMaxSourceResolution 是用于默认即时查询最大源分辨率的参数。
	defaultMetadataTimeRange               time.Duration        // defaultMetadataTimeRange 是用于默认元数据时间范围的参数。
	queryRangeHist                         prometheus.Histogram // queryRangeHist 是用于查询范围直方图的参数。
}

// NewQueryAPI returns an initialized QueryAPI type.
// NewQueryAPI 创建并返回一个 QueryAPI 实例。
//
// 参数:
// - logger: 日志记录器
// - endpointStatus: 获取端点状态的函数
// - qe: 创建 PromQL 查询引擎的函数
// - c: 创建可查询实例的函数
// - ruleGroups: 规则组客户端
// - targets: 目标客户端
// - metadatas: 元数据客户端
// - exemplars: 示例客户端
// - enableAutodownsampling: 是否启用自动降采样
// - enableQueryPartialResponse: 是否启用查询部分响应
// - enableRulePartialResponse: 是否启用规则部分响应
// - enableTargetPartialResponse: 是否启用目标部分响应
// - enableMetricMetadataPartialResponse: 是否启用元数据部分响应
// - enableExemplarPartialResponse: 是否启用示例部分响应
// - enableQueryPushdown: 是否启用查询下推
// - replicaLabels: 副本标签列表
// - flagsMap: 标志映射
// - defaultRangeQueryStep: 默认范围查询步长
// - defaultInstantQueryMaxSourceResolution: 默认即时查询最大源分辨率
// - defaultMetadataTimeRange: 默认元数据时间范围
// - disableCORS: 是否禁用 CORS
// - gate: 限流
// - reg: Prometheus 注册中心
//
// 返回值:
// - 返回一个 QueryAPI 实例
func NewQueryAPI(
	logger log.Logger,
	endpointStatus func() []query.EndpointStatus,
	qe func(int64) *promql.Engine,
	c query.QueryableCreator,
	ruleGroups rules.UnaryClient,
	targets targets.UnaryClient,
	metadatas metadata.UnaryClient,
	exemplars exemplars.UnaryClient,
	enableAutodownsampling bool,
	enableQueryPartialResponse bool,
	enableRulePartialResponse bool,
	enableTargetPartialResponse bool,
	enableMetricMetadataPartialResponse bool,
	enableExemplarPartialResponse bool,
	enableQueryPushdown bool,
	replicaLabels []string,
	flagsMap map[string]string,
	defaultRangeQueryStep time.Duration,
	defaultInstantQueryMaxSourceResolution time.Duration,
	defaultMetadataTimeRange time.Duration,
	disableCORS bool,
	gate gate.Gate,
	reg *prometheus.Registry,
) *QueryAPI {
	// 初始化 QueryAPI 结构体并返回
	return &QueryAPI{
		// 创建基础 API
		baseAPI: api.NewBaseAPI(logger, disableCORS, flagsMap),
		// 记录器
		logger: logger,
		// 查询引擎
		queryEngine: qe,
		// 创建可查询实例的函数
		queryableCreate: c,
		// 限流门
		gate: gate,
		// 规则组客户端
		ruleGroups: ruleGroups,
		// 目标客户端
		targets: targets,
		// 元数据客户端
		metadatas: metadatas,
		// 示例客户端
		exemplars: exemplars,

		// 自动降采样开关
		enableAutodownsampling: enableAutodownsampling,
		// 查询部分响应开关
		enableQueryPartialResponse: enableQueryPartialResponse,
		// 规则部分响应开关
		enableRulePartialResponse: enableRulePartialResponse,
		// 目标部分响应开关
		enableTargetPartialResponse: enableTargetPartialResponse,
		// 元数据部分响应开关
		enableMetricMetadataPartialResponse: enableMetricMetadataPartialResponse,
		// 示例部分响应开关
		enableExemplarPartialResponse: enableExemplarPartialResponse,
		// 查询下推开关
		enableQueryPushdown: enableQueryPushdown,
		// 副本标签列表
		replicaLabels: replicaLabels,
		// 端点状态获取函数
		endpointStatus: endpointStatus,
		// 默认范围查询步长
		defaultRangeQueryStep: defaultRangeQueryStep,
		// 默认即时查询最大源分辨率
		defaultInstantQueryMaxSourceResolution: defaultInstantQueryMaxSourceResolution,
		// 默认元数据时间范围
		defaultMetadataTimeRange: defaultMetadataTimeRange,
		// 禁用 CORS 开关
		disableCORS: disableCORS,

		// 创建查询范围直方图
		queryRangeHist: promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
			Name:    "thanos_query_range_requested_timespan_duration_seconds",
			Help:    "A histogram of the query range window in seconds",
			Buckets: prometheus.ExponentialBuckets(15*60, 2, 12),
		}),
	}
}

// Register the API's endpoints in the given router.
// Register 方法用于将 QueryAPI 的各个处理函数注册到路由中。
//
// 参数:
//
//	r *route.Router: 路由实例,用于注册处理函数。
//	tracer opentracing.Tracer: 用于追踪请求的 Tracer 实例。
//	logger log.Logger: 用于记录日志的 Logger 实例。
//	ins extpromhttp.InstrumentationMiddleware: 用于监控 HTTP 请求的中间件。
//	logMiddleware *logging.HTTPServerMiddleware: 用于记录 HTTP 请求日志的中间件。
func (qapi *QueryAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) {
	// 注册基础API
	qapi.baseAPI.Register(r, tracer, logger, ins, logMiddleware)

	// 获取API的Instrumentation
	instr := api.GetInstr(tracer, logger, ins, logMiddleware, qapi.disableCORS)

	// 注册GET和POST请求处理函数到/query路由
	r.Get("/query", instr("query", qapi.query))  // GET /query
	r.Post("/query", instr("query", qapi.query)) // POST /query

	// 注册GET和POST请求处理函数到/query_range路由
	r.Get("/query_range", instr("query_range", qapi.queryRange))  // GET /query_range
	r.Post("/query_range", instr("query_range", qapi.queryRange)) // POST /query_range

	// 注册GET请求处理函数到/label/:name/values路由
	r.Get("/label/:name/values", instr("label_values", qapi.labelValues)) // GET /label/:name/values

	// 注册GET和POST请求处理函数到/series路由
	r.Get("/series", instr("series", qapi.series))  // GET /series
	r.Post("/series", instr("series", qapi.series)) // POST /series

	// 注册GET和POST请求处理函数到/labels路由
	r.Get("/labels", instr("label_names", qapi.labelNames))  // GET /labels
	r.Post("/labels", instr("label_names", qapi.labelNames)) // POST /labels

	// 注册GET请求处理函数到/stores路由
	r.Get("/stores", instr("stores", qapi.stores)) // GET /stores

	// 注册GET请求处理函数到/rules路由
	r.Get("/rules", instr("rules", NewRulesHandler(qapi.ruleGroups, qapi.enableRulePartialResponse))) // GET /rules

	// 注册GET请求处理函数到/targets路由
	r.Get("/targets", instr("targets", NewTargetsHandler(qapi.targets, qapi.enableTargetPartialResponse))) // GET /targets

	// 注册GET请求处理函数到/metadata路由
	r.Get("/metadata", instr("metadata", NewMetricMetadataHandler(qapi.metadatas, qapi.enableMetricMetadataPartialResponse))) // GET /metadata

	// 注册GET和POST请求处理函数到/query_exemplars路由
	r.Get("/query_exemplars", instr("exemplars", NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse)))  // GET /query_exemplars
	r.Post("/query_exemplars", instr("exemplars", NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse))) // POST /query_exemplars
}

type queryData struct {
	ResultType parser.ValueType  `json:"resultType"`      // ResultType 是查询结果的类型,例如 vector、matrix 等。
	Result     parser.Value      `json:"result"`          // Result 是查询结果的详细数据。它的类型取决于 ResultType。
	Stats      *stats.QueryStats `json:"stats,omitempty"` // Stats 是查询的统计信息,例如查询耗时、查询的标签等。
	// Additional Thanos Response field.
	Warnings []error `json:"warnings,omitempty"` // Warnings 是查询过程中可能出现的警告信息。
}

// parseEnableDedupParam 解析请求中的去重参数,并返回是否启用去重的布尔值。
//
// 参数:
//
//	r *http.Request: 包含请求参数的 HTTP 请求对象
//
// 返回值:
//
//	enableDeduplication bool: 是否启用去重的布尔值
//	err *api.ApiError: 如果发生错误,则返回错误信息;否则为 nil
func (qapi *QueryAPI) parseEnableDedupParam(r *http.Request) (enableDeduplication bool, _ *api.ApiError) {
	// 默认启用去重
	enableDeduplication = true

	// 检查请求中是否包含去重参数
	// r.FormValue 返回请求中指定参数的值,如果参数不存在则返回空字符串
	if val := r.FormValue(DedupParam); val != "" {
		// 将字符串转换为布尔值
		var err error
		// enabelDeduplication 是一个布尔值,表示是否启用去重,strconv.ParseBool 用于将字符串转换为布尔值。
		enableDeduplication, err = strconv.ParseBool(val)
		// 如果转换失败,返回错误
		if err != nil {
			// 返回错误,包括错误类型和错误信息,api.ApiError 是一个自定义的错误类型,用于返回 API 相关的错误信息。
			return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", DedupParam)}
		}
	}
	return enableDeduplication, nil
}

// parseReplicaLabelsParam 解析请求表单,并返回replicaLabels参数值列表
//
// 参数:
//
//	r *http.Request: HTTP请求对象
//
// 返回值:
//
//	replicaLabels []string: replicaLabels参数值列表
//	_ *api.ApiError: 如果出现错误,则返回ApiError对象;否则返回nil
func (qapi *QueryAPI) parseReplicaLabelsParam(r *http.Request) (replicaLabels []string, _ *api.ApiError) {
	// 解析请求表单
	if err := r.ParseForm(); err != nil {
		// 解析表单出错时返回ApiError
		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
	}

	// 初始化replicaLabels为qapi的replicaLabels
	replicaLabels = qapi.replicaLabels
	// 当提供了查询参数时,覆盖cli标志
	// Overwrite the cli flag when provided as a query parameter.
	if len(r.Form[ReplicaLabelsParam]) > 0 {
		// 使用请求表单中的replicaLabels覆盖qapi的replicaLabels
		replicaLabels = r.Form[ReplicaLabelsParam]
	}

	return replicaLabels, nil
}

// parseStoreDebugMatchersParam 解析请求中的storeDebugMatchers参数
//
// 参数:
//
//	r *http.Request: HTTP请求对象
//
// 返回值:
//
//	[][]*labels.Matcher: 解析后的storeMatchers列表
//	*api.ApiError: 如果发生错误,则返回API错误对象,否则返回nil
//
// 说明:
//
//	该函数解析HTTP请求中的storeDebugMatchers参数,并将其转换为[][]*labels.Matcher类型的storeMatchers列表。
//	如果请求解析失败或参数解析出错,则返回相应的API错误对象。
func (qapi *QueryAPI) parseStoreDebugMatchersParam(r *http.Request) (storeMatchers [][]*labels.Matcher, _ *api.ApiError) {
	// 解析表单,r.ParseForm()会解析URL中的查询参数,并将其存储在r.Form中。
	if err := r.ParseForm(); err != nil {
		// 解析表单出错,返回内部错误。api.ApiError 是一个自定义的错误类型,用于返回 API 相关的错误信息。
		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
	}

	// 遍历表单中的参数, r.Form[StoreMatcherParam] 获取所有名为 StoreMatcherParam 的参数值列表。
	for _, s := range r.Form[StoreMatcherParam] {
		// 解析指标选择器。parser.ParseMetricSelector 用于解析指标选择器字符串,返回一个匹配器的切片。
		matchers, err := parser.ParseMetricSelector(s)
		if err != nil {
			// 解析出错,返回数据错误
			return nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
		// 将解析出的匹配器添加到storeMatchers中, storeMatchers 是一个 [][]*labels.Matcher 类型的切片。
		storeMatchers = append(storeMatchers, matchers)
	}

	return storeMatchers, nil
}

// parseDownsamplingParamMillis 函数从HTTP请求中提取最大源分辨率参数值,并将其转换为毫秒数。
// 如果请求中未提供参数值,或者启用了自动降采样,则使用默认值。
// 如果提供的参数值无法解析为有效的时间间隔,则返回错误。
//
// 参数:
// r *http.Request: 包含请求的HTTP请求对象。
// defaultVal time.Duration: 当启用自动降采样或请求中未提供参数值时要使用的默认值。
//
// 返回值:
// maxResolutionMillis int64: 解析后的最大源分辨率(以毫秒为单位)。
// *api.ApiError: 如果发生错误,则返回API错误对象;否则为nil。
func (qapi *QueryAPI) parseDownsamplingParamMillis(r *http.Request, defaultVal time.Duration) (maxResolutionMillis int64, _ *api.ApiError) {
	// 初始化最大源分辨率为0秒
	maxSourceResolution := 0 * time.Second

	// 从请求中获取最大源分辨率参数的值
	val := r.FormValue(MaxSourceResolutionParam)

	// 如果启用自动降采样或参数值为"auto",则使用默认值
	if qapi.enableAutodownsampling || (val == "auto") {
		maxSourceResolution = defaultVal
	}

	// 如果参数值不为空且不为"auto"
	if val != "" && val != "auto" {
		var err error
		// 解析参数值为时间间隔
		maxSourceResolution, err = parseDuration(val)
		if err != nil {
			// 如果解析失败,返回错误
			return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", MaxSourceResolutionParam)}
		}
	}

	// 如果最大源分辨率小于0,返回错误
	if maxSourceResolution < 0 {
		return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("negative '%s' is not accepted. Try a positive integer", MaxSourceResolutionParam)}
	}

	// 将最大源分辨率转换为毫秒,并返回
	return int64(maxSourceResolution / time.Millisecond), nil
}

// parsePartialResponseParam 解析请求中的部分响应参数,并返回是否启用部分响应和错误信息
//
// 参数:
//
//	r *http.Request:HTTP 请求对象
//	defaultEnablePartialResponse bool:默认是否启用部分响应
//
// 返回值:
//
//	enablePartialResponse bool:是否启用部分响应
//	*api.ApiError:错误信息,如果解析失败,则返回错误信息,否则返回 nil
func (qapi *QueryAPI) parsePartialResponseParam(r *http.Request, defaultEnablePartialResponse bool) (enablePartialResponse bool, _ *api.ApiError) {
	// 覆盖作为查询参数提供的cli标志
	// Overwrite the cli flag when provided as a query parameter.
	if val := r.FormValue(PartialResponseParam); val != "" {
		// 解析查询参数
		var err error
		defaultEnablePartialResponse, err = strconv.ParseBool(val)
		if err != nil {
			// 如果解析错误,则返回错误
			// Return an error if parsing fails
			return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", PartialResponseParam)}
		}
	}
	return defaultEnablePartialResponse, nil
}

// parseStep 函数用于解析查询参数中的步骤时间,并返回步骤时间和可能发生的错误。
//
// 参数:
// r *http.Request:HTTP 请求对象,用于获取查询参数。
// defaultRangeQueryStep time.Duration:默认的步骤时间。
// rangeSeconds int64:范围秒数,用于计算默认步骤时间。
//
// 返回值:
// time.Duration:解析后的步骤时间。
// *api.ApiError:可能发生的错误。
func (qapi *QueryAPI) parseStep(r *http.Request, defaultRangeQueryStep time.Duration, rangeSeconds int64) (time.Duration, *api.ApiError) {
	// 覆盖作为查询参数提供的cli标志
	// Overwrite the cli flag when provided as a query parameter.
	if val := r.FormValue(Step); val != "" {
		var err error
		defaultRangeQueryStep, err = parseDuration(val)
		if err != nil {
			return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", Step)}
		}
		return defaultRangeQueryStep, nil
	}
	// 使用默认步骤的方式使其与UI一致
	// Default step is used this way to make it consistent with UI.
	d := time.Duration(math.Max(float64(rangeSeconds/250), float64(defaultRangeQueryStep/time.Second))) * time.Second
	return d, nil
}

// query 函数是 QueryAPI 结构体中的一个方法,用于处理 HTTP 请求并返回查询结果。
//
// 参数:
//
//	r *http.Request: HTTP 请求对象。
//
// 返回值:
//
//	interface{}: 查询结果数据。
//	[]error: 查询过程中产生的警告信息。
//	*api.ApiError: 查询过程中产生的 API 错误信息。
func (qapi *QueryAPI) query(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 解析时间参数。
	// parseTimeParam 函数用于解析时间参数,并将其转换为 time.Time 类型。
	// qapi.baseAPI.Now() 返回当前时间。
	ts, err := parseTimeParam(r, "time", qapi.baseAPI.Now())
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 获取请求上下文
	ctx := r.Context()
	// 解析超时参数。r.FormValue("timeout") 用于获取请求中的 "timeout" 参数,该参数指定了查询的超时时间。
	if to := r.FormValue("timeout"); to != "" {
		var cancel context.CancelFunc
		// 解析超时时间参数, parseDuration 函数用于解析超时时间参数,并将其转换为 time.Duration 类型。
		timeout, err := parseDuration(to)
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}

		// 设置超时上下文
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}

	// 解析去重参数, qapi.parseEnableDedupParam 函数用于解析请求中的去重参数,并返回是否启用去重的布尔值和可能发生的 API 错误。
	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析副本标签参数, qapi.parseReplicaLabelsParam 函数用于解析请求中的副本标签参数,并返回副本标签的字符串切片和可能发生的 API 错误。
	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析调试匹配器参数, qapi.parseStoreDebugMatchersParam 函数用于解析请求中的调试匹配器参数,并返回是否启用调试匹配器的布尔值和可能发生的 API 错误。
	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析部分响应参数, qapi.parsePartialResponseParam 函数用于解析请求中的部分响应参数,并返回是否启用部分响应的布尔值和可能发生的 API 错误。
	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析降采样参数, qapi.parseDownsamplingParamMillis 函数用于解析请求中的降采样参数,并返回最大源分辨率的毫秒数和可能发生的 API 错误。
	maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, qapi.defaultInstantQueryMaxSourceResolution)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 获取查询引擎, qapi.queryEngine 函数返回一个查询引擎实例,该实例用于执行即时查询。
	qe := qapi.queryEngine(maxSourceResolution)

	// 启动PromQL追踪跨度, tracing.StartSpan 函数用于启动一个PromQL追踪跨度。tracing.StartSpan 函数用于启动一个追踪跨度,该跨度与 PromQL 的即时查询相关联。
	// We are starting promQL tracing span here, because we have no control over promQL code.
	span, ctx := tracing.StartSpan(ctx, "promql_instant_query")
	defer span.Finish()

	// 创建即时查询, qe.NewInstantQuery 函数用于创建一个新的即时查询实例。该实例将执行 PromQL 表达式,并返回查询结果。
	qry, err := qe.NewInstantQuery(qapi.queryableCreate(enableDedup, replicaLabels, storeDebugMatchers, maxSourceResolution, enablePartialResponse, qapi.enableQueryPushdown, false), r.FormValue("query"), ts)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// tracing.DoInSpan 函数用于在指定的上下文中执行一个操作,并创建一个追踪跨度。tracing.DoInSpan 函数用于在给定的上下文和追踪跨度中执行一个操作。
	tracing.DoInSpan(ctx, "query_gate_ismyturn", func(ctx context.Context) {
		err = qapi.gate.Start(ctx)
	})
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	// qapi.gate.Done 函数用于完成查询操作,释放资源。qapi.gate.Start 在执行查询之前获取锁,确保只有一个查询可以同时运行。
	defer qapi.gate.Done()

	res := qry.Exec(ctx) // qry.Exec 函数执行查询,并返回一个包含结果和警告的 Result 对象。

	// res.Err 检查查询结果中的错误。如果存在错误,则根据错误的类型返回相应的 API 错误信息。
	if res.Err != nil {
		// res.Err.(type) 是 Go 语言中的类型断言,用于检查 res.Err 的具体类型。
		switch res.Err.(type) {
		case promql.ErrQueryCanceled: // 查询被取消
			return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err}
		case promql.ErrQueryTimeout: // 查询超时
			return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err}
		case promql.ErrStorage: // 存储错误
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: res.Err}
		}
		// 其他错误类型,返回执行错误的 API 错误信息
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err}
	}

	// 如果请求参数中包含"stats",则在响应中包含可选的统计信息字段
	var qs *stats.QueryStats

	// r.FormValues(Stats) 返回一个包含请求参数中所有 "stats" 值的切片。如果切片不为空,则创建一个新的 QueryStats 对象。
	// 【问】:为什么要在这种情况下创建一个新的 QueryStats 对象?
	// 【答】:stats.NewQueryStats(qry.Stats()) 创建一个新的 QueryStats 对象,并将查询的统计信息传递给它。QueryStats 对象用于存储查询的统计信息,例如查询的持续时间、查询的执行时间、查询的内存使用情况等。这些信息可以帮助用户了解查询的性能和资源使用情况。
	if r.FormValue(Stats) != "" {
		qs = stats.NewQueryStats(qry.Stats())
	}
	return &queryData{
		ResultType: res.Value.Type(), // 查询结果的类型,例如向量、标量等。
		Result:     res.Value,        // 查询结果,可以是向量、标量等。
		Stats:      qs,               // 可选的统计信息字段,如果请求参数中包含 "stats",则包含查询的统计信息。
	}, res.Warnings, nil // 返回查询结果、警告和错误信息。
}

// queryRange 方法用于处理HTTP请求,并返回查询结果、错误列表和API错误。
// 参数 r 表示传入的HTTP请求。
// 返回值包括:
// - interface{} 类型的查询结果。
// - []error 类型的错误列表。
// - *api.ApiError 类型的API错误。
func (qapi *QueryAPI) queryRange(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 解析开始时间
	start, err := parseTime(r.FormValue("start"))
	if err != nil {
		// 如果解析开始时间失败,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}
	// 解析结束时间
	end, err := parseTime(r.FormValue("end"))
	if err != nil {
		// 如果解析结束时间失败,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}
	// 如果结束时间在开始时间之前,返回错误
	if end.Before(start) {
		err := errors.New("end timestamp must not be before start time")
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 解析查询步长。qapi.parseStep 方法用于解析请求中的步长参数,并返回一个表示查询步长的整数和一个可能的 API 错误。
	// qapi.defaultRangeQueryStep 是一个默认的查询步长,用于在解析请求时如果没有指定步长则使用。默认值是 30 秒。
	// int64(end.Sub(start)/time.Second) 是一个表示查询时间范围内的秒数的整数,用于计算查询步长。
	step, apiErr := qapi.parseStep(r, qapi.defaultRangeQueryStep, int64(end.Sub(start)/time.Second))
	if apiErr != nil {
		// 如果解析步长失败,返回错误
		return nil, nil, apiErr
	}

	// 如果步长小于等于0,返回错误
	if step <= 0 {
		// 如果步长小于等于0,返回错误,错误的中文意思是:“不接受零或负查询分辨率步长。尝试一个正整数”
		err := errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer")
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// end.Sub(start)/step > 11000 是一个条件,用于检查查询时间范围内返回点的数量是否超过 11,000 个。如果超过,则返回错误。
	if end.Sub(start)/step > 11000 {
		// 如果超过,返回错误,错误的中文意思是:“超出了11,000个点时每个时间序列的最大分辨率。尝试减少查询分辨率(?step=XX)”。
		err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 获取请求上下文
	ctx := r.Context()
	// 解析请求超时时间
	if to := r.FormValue("timeout"); to != "" {
		var cancel context.CancelFunc
		// parseDuartion(to) 函数用于解析超时时间,如果解析失败,返回错误
		timeout, err := parseDuration(to)
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}

		// 设置超时上下文
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}

	// 解析去重参数, qapi.parseEnableDedupParam 方法用于解析请求中的去重参数,并返回一个表示是否启用去重的布尔值和一个可能的 API 错误。
	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析副本标签参数, qapi.parseReplicaLabelsParam 方法用于解析请求中的副本标签参数,并返回一个表示是否启用副本标签的布尔值和一个可能的 API 错误。
	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析存储调试匹配器参数, qapi.parseStoreDebugMatchersParam 方法用于解析请求中的存储调试匹配器参数,并返回一个表示是否启用存储调试匹配器的布尔值和一个可能的 API 错误。
	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析下采样参数, qapi.parseDownsamplingParamMillis 方法用于解析请求中的下采样参数,并返回一个表示最大源分辨率的整数和一个可能的 API 错误。
	maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, step/5)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析部分响应参数, qapi.parsePartialResponseParam 方法用于解析请求中的部分响应参数,并返回一个表示是否启用部分响应的布尔值和一个可能的 API 错误。
	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 获取查询引擎, qe 方法用于获取查询引擎实例。它接受一个表示最大源分辨率的整数作为参数,并返回一个查询引擎接口和一个可能的错误。
	qe := qapi.queryEngine(maxSourceResolution)

	// 记录查询范围, qapi.queryRangeHist 是一个用于记录查询范围的直方图,它接受一个表示查询持续时间(以秒为单位)的浮点数作为参数。这里,它记录了查询范围的持续时间。
	qapi.queryRangeHist.Observe(end.Sub(start).Seconds())

	// 开始PromQL追踪跨度, tracing.StartSpan 方法用于开始一个新的追踪跨度(span),它接受一个上下文和一个字符串作为参数,并返回该跨度和更新后的上下文。这里,它创建了一个名为 "promql_range_query" 的追踪跨度,并记录了查询的开始时间。这里,它记录了查询的开始时间("start"),并将其作为追踪跨度的标签。
	span, ctx := tracing.StartSpan(ctx, "promql_range_query")
	defer span.Finish()

	// 创建新的范围查询, qe.NewRangeQuery 方法用于创建一个新的范围查询,它接受一个表示是否启用去重、副本标签、存储调试匹配器、最大源分辨率、部分响应和查询推送的布尔值,以及查询表达式、开始时间、结束时间和步长作为参数,并返回一个范围查询和一个可能的错误。这里,它创建了一个新的范围查询,并记录了查询的开始时间("start")和结束时间("end")。这里,它记录了查询的开始时间("start")和结束时间("end")。
	qry, err := qe.NewRangeQuery(
		// 启用去重、副本标签、存储调试匹配器、最大源分辨率、部分响应和查询推送的布尔值
		qapi.queryableCreate(
			enableDedup,                      // 启用去重
			replicaLabels,                    // 启用副本标签
			storeDebugMatchers,               // 启用存储调试匹配器
			maxSourceResolution,              // 最大源分辨率
			enablePartialResponse,            // 启用部分响应
			qapi.enableQueryPushdown, false), // 启用查询推送down
		r.FormValue("query"), // query 表单字段中的查询表达式
		start,                // 开始时间
		end,                  // 结束时间
		step,                 // 步长
	)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 执行查询, query_gate_ismyturn 是一个追踪跨度,它用于跟踪查询 gate 是否处于打开状态。这里,它使用 tracing.DoInSpan 方法来执行查询,并在查询完成后关闭查询门。这里,它首先尝试获取查询 gate 的状态,如果成功则执行查询。
	tracing.DoInSpan(ctx, "query_gate_ismyturn", func(ctx context.Context) {
		err = qapi.gate.Start(ctx)
	})
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	defer qapi.gate.Done() // 关闭查询 gate

	// 执行查询
	res := qry.Exec(ctx)
	if res.Err != nil {
		switch res.Err.(type) {
		// 如果查询被取消,返回错误
		case promql.ErrQueryCanceled:
			return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err}
		// 如果查询超时,返回错误
		case promql.ErrQueryTimeout:
			return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err}
		}
		// 如果查询执行出错,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err}
	}

	// 如果请求中包含"stats"参数,则在响应中包含统计信息
	var qs *stats.QueryStats
	if r.FormValue(Stats) != "" {
		qs = stats.NewQueryStats(qry.Stats())
	}
	return &queryData{
		ResultType: res.Value.Type(),
		Result:     res.Value,
		Stats:      qs,
	}, res.Warnings, nil
}

// labelValues 函数用于处理获取标签值的HTTP请求
//
// 参数:
//
//	r *http.Request: 传入的HTTP请求
//
// 返回值:
//
//	interface{}: 获取到的标签值切片
//	[]error: 错误信息切片(当前未使用)
//	*api.ApiError: API错误对象
func (qapi *QueryAPI) labelValues(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 获取请求上下文
	ctx := r.Context()
	// 从请求上下文中获取参数name
	name := route.Param(ctx, "name")

	// 验证name是否符合标签名正则表达式
	if !model.LabelNameRE.MatchString(name) {
		// 如果不符合,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid label name: %q", name)}
	}

	// 解析请求中的时间范围
	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
	if err != nil {
		// 如果解析失败,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 解析请求中的partial_response参数
	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
	if apiErr != nil {
		// 如果解析失败,返回错误
		return nil, nil, apiErr
	}

	// 解析请求中的store_debug_matchers参数
	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
	if apiErr != nil {
		// 如果解析失败,返回错误
		return nil, nil, apiErr
	}

	// 解析请求中的Matcher参数
	var matcherSets [][]*labels.Matcher
	for _, s := range r.Form[MatcherParam] {
		// 解析每个Matcher参数
		matchers, err := parser.ParseMetricSelector(s)
		if err != nil {
			// 如果解析失败,返回错误
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
		// 将解析得到的matchers添加到matcherSets中
		matcherSets = append(matcherSets, matchers)
	}

	// 创建Querier, qapi.queryableCreate 是一个函数,用于创建一个可查询的实例。这里它被用来创建Querier对象,该对象可以执行标签值查询等操作。
	q, err := qapi.queryableCreate(true, nil, storeDebugMatchers, 0, enablePartialResponse, qapi.enableQueryPushdown, true).
		Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		// 如果创建Querier失败,返回错误
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	// 关闭Querier时记录日志
	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelValues")

	// 存储标签值的切片和警告信息
	var (
		vals     []string
		warnings storage.Warnings
	)
	// 如果matcherSets不为空
	if len(matcherSets) > 0 {
		// 存储每次调用LabelValues返回的警告信息
		var callWarnings storage.Warnings
		// 存储所有标签值的集合
		labelValuesSet := make(map[string]struct{})
		// 遍历每个matcherSet
		for _, matchers := range matcherSets {
			// 调用LabelValues获取标签值
			vals, callWarnings, err = q.LabelValues(name, matchers...)
			if err != nil {
				// 如果调用失败,返回错误
				return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
			}
			// 将每次调用返回的警告信息添加到warnings中
			warnings = append(warnings, callWarnings...)
			// 将每个标签值添加到labelValuesSet中
			for _, val := range vals {
				labelValuesSet[val] = struct{}{}
			}
		}

		// 将labelValuesSet转换为切片并排序
		vals = make([]string, 0, len(labelValuesSet))
		for val := range labelValuesSet {
			vals = append(vals, val)
		}
		sort.Strings(vals)
	} else {
		// 如果matcherSets为空,直接调用LabelValues获取标签值
		vals, warnings, err = q.LabelValues(name)
		if err != nil {
			// 如果调用失败,返回错误
			return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
		}
	}

	// 如果vals为空,则初始化为空切片
	if vals == nil {
		vals = make([]string, 0)
	}

	// 返回标签值切片、警告信息和nil错误
	return vals, warnings, nil
}

// series 函数用于处理时间序列查询请求。
//
// 参数:
//   - r: *http.Request, HTTP请求对象,包含查询参数和表单数据。
//
// 返回值:
//   - interface{}, 查询结果,具体类型取决于查询结果。
//   - []error, 错误列表,可能包含多个错误。
//   - *api.ApiError, API错误,如果发生API级别的错误则返回此错误。
func (qapi *QueryAPI) series(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 解析请求表单
	if err := r.ParseForm(); err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
	}

	// 检查请求表单中是否提供了匹配参数
	if len(r.Form[MatcherParam]) == 0 {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("no match[] parameter provided")}
	}

	// 解析时间范围,parseMetadataTimeRange 用于解析请求中的时间范围参数,例如从和到的时间。如果解析失败,则返回错误信息。
	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 初始化匹配器集合
	var matcherSets [][]*labels.Matcher
	for _, s := range r.Form[MatcherParam] {
		// 解析每个匹配器字符串
		matchers, err := parser.ParseMetricSelector(s)
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
		// 将解析后的匹配器添加到匹配器集合中
		matcherSets = append(matcherSets, matchers)
	}

	// 解析去重参数
	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析副本标签参数
	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析存储调试匹配器参数
	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析部分响应参数
	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 创建查询器, qapi.queryableCrete 用于创建查询器实例,该函数接受一系列参数并返回一个Querier接口的实现。
	q, err := qapi.queryableCreate(enableDedup, replicaLabels, storeDebugMatchers, math.MaxInt64, enablePartialResponse, qapi.enableQueryPushdown, true).
		Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	// 确保查询器在函数结束时关闭
	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable series")

	// 初始化存储指标集合和系列集合
	var (
		metrics = []labels.Labels{}
		sets    []storage.SeriesSet
	)
	// 对每个匹配器集合执行选择操作
	for _, mset := range matcherSets {
		sets = append(sets, q.Select(false, nil, mset...))
	}

	// 合并系列集合
	set := storage.NewMergeSeriesSet(sets, storage.ChainedSeriesMerge)
	// 遍历合并后的系列集合
	for set.Next() {
		// 将当前系列的标签添加到指标集合中
		metrics = append(metrics, set.At().Labels())
	}
	// 检查是否有错误发生
	if set.Err() != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: set.Err()}
	}
	return metrics, set.Warnings(), nil
}

// labelNames 函数用于处理HTTP请求,并返回接口、错误列表和ApiError指针。
// 参数 r 表示HTTP请求对象。
//
// 返回结果:
// - 接口:返回标签名称的切片。
// - 错误列表:返回可能发生的错误列表。
// - ApiError指针:如果发生API错误,将返回ApiError指针。
func (qapi *QueryAPI) labelNames(r *http.Request) (interface{}, []error, *api.ApiError) {
	// 解析请求中的时间范围参数,如果解析失败,则返回错误信息。
	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}

	// 解析部分响应参数
	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	// 解析存储调试匹配器参数
	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
	if apiErr != nil {
		return nil, nil, apiErr
	}

	var matcherSets [][]*labels.Matcher
	for _, s := range r.Form[MatcherParam] {
		// 解析度量选择器参数
		matchers, err := parser.ParseMetricSelector(s)
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
		matcherSets = append(matcherSets, matchers)
	}

	// 创建查询对象
	q, err := qapi.queryableCreate(true, nil, storeDebugMatchers, 0, enablePartialResponse, qapi.enableQueryPushdown, true).
		Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	// 确保查询器在函数结束时关闭
	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelNames")

	var (
		names    []string
		warnings storage.Warnings
	)

	// 如果匹配器集合不为空
	if len(matcherSets) > 0 {
		var callWarnings storage.Warnings
		// 创建标签名集合
		labelNamesSet := make(map[string]struct{})
		for _, matchers := range matcherSets {
			// 获取标签名集合
			names, callWarnings, err = q.LabelNames(matchers...)
			if err != nil {
				return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
			}
			// 将每次调用返回的警告信息添加到warnings中
			warnings = append(warnings, callWarnings...)
			// 将每个标签名添加到labelNamesSet中
			for _, val := range names {
				labelNamesSet[val] = struct{}{}
			}
		}

		// 将标签名集合转换为字符串切片并排序
		names = make([]string, 0, len(labelNamesSet))
		// 遍历标签名集合
		for name := range labelNamesSet {
			names = append(names, name)
		}
		sort.Strings(names) // 对标签名进行排序
	} else {
		// 如果匹配器集合为空,则获取所有标签名
		names, warnings, err = q.LabelNames()
	}

	if err != nil {
		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}
	}
	if names == nil {
		// 如果标签名集合为空,则初始化为空切片
		names = make([]string, 0)
	}

	return names, warnings, nil
}

// stores 处理函数用于从QueryAPI结构体中获取所有组件类型的状态列表。
//
// 参数:
//
//	_ *http.Request: 请求对象,本函数不使用该参数。
//
// 返回值:
//
//	interface{}: 包含每种组件类型的状态列表的map,键为组件类型字符串,值为对应组件类型的状态列表。
//	[]error: 错误列表,本函数始终返回空列表。
//	*api.ApiError: ApiError对象,本函数始终返回nil。
func (qapi *QueryAPI) stores(_ *http.Request) (interface{}, []error, *api.ApiError) {
	// 创建一个map来存储每种组件类型的状态列表
	statuses := make(map[string][]query.EndpointStatus)

	// 遍历所有端点状态
	for _, status := range qapi.endpointStatus() {
		// 如果无法获取组件类型,则忽略该端点
		// Don't consider an endpoint if we cannot retrieve component type.
		if status.ComponentType == nil {
			continue
		}
		// 将端点状态添加到对应组件类型的列表中
		statuses[status.ComponentType.String()] = append(statuses[status.ComponentType.String()], status)
	}
	// 返回所有组件类型的状态列表、空错误列表和空ApiError
	return statuses, nil, nil
}

// NewTargetsHandler created handler compatible with HTTP /api/v1/targets https://prometheus.io/docs/prometheus/latest/querying/api/#targets
// which uses gRPC Unary Targets API.
// NewTargetsHandler 函数用于创建一个新的目标处理器。
// 该处理器根据请求中的参数,从客户端获取目标列表,并返回这些目标、警告信息和API错误(如果有的话)。
//
// 参数:
//
//	client targets.UnaryClient: 用于获取目标列表的客户端。
//	enablePartialResponse bool: 是否启用部分响应。如果启用,则在获取目标时,如果某些目标无法获取,则返回这些目标的警告信息,而不是直接返回错误。
//
// 返回值:
//
//	func(*http.Request) (interface{}, []error, *api.ApiError): 一个函数,该函数接受一个HTTP请求,并返回一个接口(表示获取到的目标列表),一个错误切片(表示警告信息),以及一个API错误(如果有的话)。
func NewTargetsHandler(client targets.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError) {
	// 定义部分响应策略,默认为ABORT
	ps := storepb.PartialResponseStrategy_ABORT
	// 如果启用部分响应,则将策略更改为WARN
	if enablePartialResponse {
		ps = storepb.PartialResponseStrategy_WARN
	}

	return func(r *http.Request) (interface{}, []error, *api.ApiError) {
		// 从请求URL中获取state参数
		stateParam := r.URL.Query().Get("state")
		// 将state参数转换为大写,并尝试从targetspb.TargetsRequest_State_value中查找对应的枚举值
		state, ok := targetspb.TargetsRequest_State_value[strings.ToUpper(stateParam)]
		// 如果未找到对应的枚举值
		if !ok {
			// 如果state参数不为空且未找到对应的枚举值,则返回错误信息
			if stateParam != "" {
				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid targets parameter state='%v'", stateParam)}
			}
			// 如果state参数为空,则默认使用ANY状态
			state = int32(targetspb.TargetsRequest_ANY)
		}

		// 构造TargetsRequest请求
		req := &targetspb.TargetsRequest{
			State:                   targetspb.TargetsRequest_State(state),
			PartialResponseStrategy: ps,
		}

		// 调用客户端的Targets方法获取目标列表
		t, warnings, err := client.Targets(r.Context(), req)
		// 如果发生错误,则返回错误信息
		if err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving targets")}
		}

		// 返回获取到的目标列表和警告信息
		return t, warnings, nil
	}
}

// NewRulesHandler created handler compatible with HTTP /api/v1/rules https://prometheus.io/docs/prometheus/latest/querying/api/#rules
// which uses gRPC Unary Rules API.
// NewRulesHandler 创建一个处理HTTP请求的函数,该函数用于处理与规则相关的请求。
// 参数:
//
//	client:rules.UnaryClient接口的实现,用于处理与规则相关的请求。
//	enablePartialResponse:布尔值,表示是否启用部分响应。
//
// 返回值:
//
//	返回的函数接受一个*http.Request类型的参数,返回一个interface{}类型的响应,一个[]error类型的错误列表,以及一个*api.ApiError类型的API错误。
func NewRulesHandler(client rules.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError) {
	// 设置部分响应策略
	ps := storepb.PartialResponseStrategy_ABORT
	if enablePartialResponse {
		ps = storepb.PartialResponseStrategy_WARN
	}

	return func(r *http.Request) (interface{}, []error, *api.ApiError) {
		// 开始追踪HTTP请求处理过程
		span, ctx := tracing.StartSpan(r.Context(), "receive_http_request")
		defer span.Finish()

		// 声明变量
		var (
			groups   *rulespb.RuleGroups
			warnings storage.Warnings
			err      error
		)

		// 获取请求参数
		typeParam := r.URL.Query().Get("type")
		// 将 typeParam 转换为大写,并尝试从 rulespb.RulesRequest_Type_value 中查找对应的枚举值
		typ, ok := rulespb.RulesRequest_Type_value[strings.ToUpper(typeParam)]
		if !ok {
			// 如果参数类型无效
			if typeParam != "" {
				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid rules parameter type='%v'", typeParam)}
			}
			// 默认为ALL类型
			typ = int32(rulespb.RulesRequest_ALL)
		}

		// 解析表单数据
		if err := r.ParseForm(); err != nil {
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf("error parsing request form='%v'", MatcherParam)}
		}

		// TODO(bwplotka): Allow exactly the same functionality as query API: passing replica, dedup and partial response as HTTP params as well.
		// 创建RulesRequest请求
		req := &rulespb.RulesRequest{
			Type:                    rulespb.RulesRequest_Type(typ), // 设置规则类型
			PartialResponseStrategy: ps,                             // 设置部分响应策略
			MatcherString:           r.Form[MatcherParam],           // 设置匹配器字符串
		}
		// 在追踪上下文中执行获取规则操作
		tracing.DoInSpan(ctx, "retrieve_rules", func(ctx context.Context) {
			groups, warnings, err = client.Rules(ctx, req)
		})
		if err != nil {
			// 如果获取规则失败
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf("error retrieving rules: %v", err)}
		}
		return groups, warnings, nil
	}
}

// NewExemplarsHandler creates handler compatible with HTTP /api/v1/query_exemplars https://prometheus.io/docs/prometheus/latest/querying/api/#querying-exemplars
// which uses gRPC Unary Exemplars API.
// NewExemplarsHandler 创建一个处理Exemplars请求的处理器。
// 该处理器返回一个函数,该函数接收一个http.Request对象,并返回一个接口、错误列表和ApiError指针。
//
// 参数:
// - client: exemplars.UnaryClient接口,用于与存储层进行通信。
// - enablePartialResponse: bool类型,表示是否启用部分响应。
//
// 返回值:
//   - func(*http.Request) (interface{}, []error, *api.ApiError): 接收http.Request对象的函数,
//     返回一个接口、错误列表和ApiError指针。
func NewExemplarsHandler(client exemplars.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError) {
	// storepb.PartialResponseStrategy_ABORT 是部分响应策略的默认值,表示在部分响应策略中,如果遇到错误,则直接返回错误。
	ps := storepb.PartialResponseStrategy_ABORT
	if enablePartialResponse {
		// 如果启用部分响应,则设置为警告策略
		ps = storepb.PartialResponseStrategy_WARN
	}

	return func(r *http.Request) (interface{}, []error, *api.ApiError) {
		// 创建跟踪跨度
		span, ctx := tracing.StartSpan(r.Context(), "exemplar_query_request")
		defer span.Finish()

		// 声明存储返回数据的变量
		var (
			data     []*exemplarspb.ExemplarData
			warnings storage.Warnings
			err      error
		)

		// 解析请求中的起始时间参数
		start, err := parseTimeParam(r, "start", infMinTime)
		if err != nil {
			// 如果解析起始时间参数出错,则返回错误
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}
		// 解析请求中的结束时间参数
		end, err := parseTimeParam(r, "end", infMaxTime)
		if err != nil {
			// 如果解析结束时间参数出错,则返回错误
			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
		}

		// 创建请求
		req := &exemplarspb.ExemplarsRequest{
			Start:                   timestamp.FromTime(start),
			End:                     timestamp.FromTime(end),
			Query:                   r.FormValue("query"),
			PartialResponseStrategy: ps,
		}

		// 在跨度内执行获取示例的操作
		tracing.DoInSpan(ctx, "retrieve_exemplars", func(ctx context.Context) {
			data, warnings, err = client.Exemplars(ctx, req)
		})

		if err != nil {
			// 如果获取示例出错,则返回内部错误
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving exemplars")}
		}
		return data, warnings, nil
	}
}

var (
	infMinTime = time.Unix(math.MinInt64/1000+62135596801, 0)
	infMaxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999)
)

// parseMetadataTimeRange 从HTTP请求中解析时间范围,并返回起始时间和结束时间
//
// 参数:
//
//	r: *http.Request, HTTP请求对象
//	defaultMetadataTimeRange: time.Duration, 默认的时间范围
//
// 返回值:
//
//	time.Time, 起始时间
//	time.Time, 结束时间
//	error, 错误信息
//
// 说明:
//
//	如果请求参数中未指定起始时间和结束时间,则默认从时间开始处获取时间范围。
//	如果defaultMetadataTimeRange为0,则默认时间范围为时间开始到时间结束。
func parseMetadataTimeRange(r *http.Request, defaultMetadataTimeRange time.Duration) (time.Time, time.Time, error) {
	// 如果未指定查询参数中的开始时间和结束时间,则默认获取从时间起点到当前时间的范围
	// If start and end time not specified as query parameter, we get the range from the beginning of time by default.
	var defaultStartTime, defaultEndTime time.Time
	if defaultMetadataTimeRange == 0 {
		defaultStartTime = infMinTime
		defaultEndTime = infMaxTime
	} else {
		now := time.Now()
		defaultStartTime = now.Add(-defaultMetadataTimeRange)
		defaultEndTime = now
	}

	// 解析查询参数中的开始时间
	// Parse the start time from the query parameter
	start, err := parseTimeParam(r, "start", defaultStartTime)
	if err != nil {
		return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}
	// 解析查询参数中的结束时间
	// Parse the end time from the query parameter
	end, err := parseTimeParam(r, "end", defaultEndTime)
	if err != nil {
		return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err}
	}
	// 如果结束时间在开始时间之前,则抛出错误
	// If the end time is before the start time, throw an error
	if end.Before(start) {
		return time.Time{}, time.Time{}, &api.ApiError{
			Typ: api.ErrorBadData,
			Err: errors.New("end timestamp must not be before start time"),
		}
	}

	return start, end, nil
}

// parseTimeParam 从HTTP请求中解析时间参数,并返回时间
//
// 参数:
//
//	r: *http.Request, HTTP请求对象
//	paramName: string, 参数名称
//	defaultValue: time.Time, 默认时间
//
// 返回值:
//
//	time.Time, 时间
//	error, 错误信息
//
// parseTimeParam 从HTTP请求中解析时间参数,并返回时间
//
// 参数:
//
//	r: *http.Request, HTTP请求对象
//	paramName: string, 参数名称
//	defaultValue: time.Time, 默认时间
//
// 返回值:
//
//	time.Time, 时间
//	error, 错误信息
func parseTimeParam(r *http.Request, paramName string, defaultValue time.Time) (time.Time, error) {
	// 从请求中获取参数值
	val := r.FormValue(paramName)
	// 如果参数值为空,则返回默认值
	if val == "" {
		return defaultValue, nil
	}
	// 解析时间值
	result, err := parseTime(val)
	// 如果解析过程中出现错误,则返回错误信息
	if err != nil {
		// 封装错误信息
		return time.Time{}, errors.Wrapf(err, "Invalid time value for '%s'", paramName)
	}
	// 返回解析后的时间值和错误信息
	return result, nil
}

// parseTime 函数尝试将字符串 s 解析为 time.Time 类型的时间对象,并返回该对象以及错误信息。
// 如果解析成功,则返回 time.Time 对象和 nil 错误信息。
// 如果解析失败,则返回 time.Time{} 和错误信息。
//
// 参数:
//
//	s string:需要解析的时间字符串
//
// 返回值:
//
//	time.Time:解析得到的时间对象
//	error:错误信息,如果解析成功则为 nil
func parseTime(s string) (time.Time, error) {
	// 尝试将字符串转换为浮点数
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		// 分离秒和纳秒部分
		s, ns := math.Modf(t)
		// 对纳秒部分进行四舍五入并转换为整数
		ns = math.Round(ns*1000) / 1000
		// 使用秒和纳秒部分构造时间对象
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	// 尝试按照RFC3339Nano格式解析时间字符串
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		// 解析成功,返回时间对象
		return t, nil
	}
	// 如果以上两种方式都失败,返回错误
	return time.Time{}, errors.Errorf("cannot parse %q to a valid timestamp", s)
}

// parseDuration 函数将字符串解析为 time.Duration 类型。
// 如果字符串可以成功转换为浮点数,则将其转换为秒数,并返回对应的 time.Duration。
// 如果转换后的秒数超出了 int64 的范围,则返回错误。
// 如果字符串无法转换为浮点数,则尝试使用 model.ParseDuration 函数解析。
// 如果解析成功,则返回对应的 time.Duration;否则返回错误。
func parseDuration(s string) (time.Duration, error) {
	// 尝试将字符串转换为浮点数
	if d, err := strconv.ParseFloat(s, 64); err == nil {
		// 将浮点数转换为秒数
		ts := d * float64(time.Second)
		// 检查转换后的秒数是否溢出int64范围
		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
			// 如果溢出,则返回错误
			return 0, errors.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
		}
		// 返回转换后的time.Duration
		return time.Duration(ts), nil
	}
	// 使用自定义的模型解析函数尝试解析字符串
	if d, err := model.ParseDuration(s); err == nil {
		// 返回解析后的time.Duration
		return time.Duration(d), nil
	}
	// 如果以上解析都失败,则返回错误
	return 0, errors.Errorf("cannot parse %q to a valid duration", s)
}

// NewMetricMetadataHandler creates handler compatible with HTTP /api/v1/metadata https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metric-metadata
// which uses gRPC Unary Metadata API.
// NewMetricMetadataHandler 创建一个处理指标元数据请求的函数。
// client: 元数据 UnaryClient 接口。
// enablePartialResponse: 是否启用部分响应。
// 返回值: 一个函数,该函数接受一个 *http.Request 参数,并返回一个 interface{} 类型的指标元数据、一个 []error 类型的错误列表,以及一个 *api.ApiError 类型的 API 错误。
func NewMetricMetadataHandler(client metadata.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError) {
	// 设置部分响应策略
	ps := storepb.PartialResponseStrategy_ABORT
	if enablePartialResponse {
		ps = storepb.PartialResponseStrategy_WARN
	}

	return func(r *http.Request) (interface{}, []error, *api.ApiError) {
		// 开始追踪HTTP请求
		span, ctx := tracing.StartSpan(r.Context(), "metadata_http_request")
		defer span.Finish()

		var (
			// 存储指标元数据
			t map[string][]metadatapb.Meta
			// 存储警告信息
			warnings storage.Warnings
			// 存储错误信息
			err error
		)

		// 构建指标元数据请求
		req := &metadatapb.MetricMetadataRequest{
			// 默认情况下,我们使用-1,表示没有限制。
			Limit:                   -1,
			Metric:                  r.URL.Query().Get("metric"),
			PartialResponseStrategy: ps,
		}

		// 获取URL中的limit参数
		limitStr := r.URL.Query().Get("limit")
		if limitStr != "" {
			// 将limit参数转换为整数
			limit, err := strconv.ParseInt(limitStr, 10, 32)
			if err != nil {
				// 返回API错误
				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid metric metadata limit='%v'", limit)}
			}
			req.Limit = int32(limit)
		}

		// 在span中执行获取元数据操作
		tracing.DoInSpan(ctx, "retrieve_metadata", func(ctx context.Context) {
			// 从客户端获取指标元数据
			t, warnings, err = client.MetricMetadata(ctx, req)
		})
		if err != nil {
			// 返回API错误
			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving metadata")}
		}

		// 返回指标元数据、警告信息和错误信息
		return t, warnings, nil
	}
}

 

  

 

 

posted @ 2025-02-14 16:27  左扬  阅读(32)  评论(0)    收藏  举报