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
}
}

浙公网安备 33010602011771号