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