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

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

https://github.com/thanos-io/thanos/blob/v0.26.0/pkg/api/query/grpc.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.
 
package v1
 
import (
    "context"
    "time"
 
    "github.com/prometheus/prometheus/promql"
    "github.com/thanos-io/thanos/pkg/api/query/querypb"
    "github.com/thanos-io/thanos/pkg/query"
    "github.com/thanos-io/thanos/pkg/store/labelpb"
    "github.com/thanos-io/thanos/pkg/store/storepb/prompb"
    "google.golang.org/grpc"
)
 
type GRPCAPI struct {
    now                         func() time.Time           // now 是一个函数,用于获取当前时间。
    queryableCreate             query.QueryableCreator     // queryableCreate 是一个函数,用于创建查询对象。
    queryEngine                 func(int64) *promql.Engine // queryEngine 是一个函数,用于创建查询引擎。
    defaultMaxResolutionSeconds time.Duration              // defaultMaxResolutionSeconds 是一个时间间隔,用于设置默认的最大分辨率秒数。
}
 
// NewGRPCAPI 创建一个新的GRPCAPI实例
// now: 当前时间函数,返回当前时间的 time.Time 对象
// creator: 可查询的创建者,实现了 query.QueryableCreator 接口
// queryEngine: 查询引擎函数,接受一个 int64 类型的参数并返回一个指向 promql.Engine 的指针
// defaultMaxResolutionSeconds: 默认的最大解析秒数,类型为 time.Duration
// 返回值:一个指向 GRPCAPI 结构体的指针
func NewGRPCAPI(now func() time.Time, creator query.QueryableCreator, queryEngine func(int64) *promql.Engine, defaultMaxResolutionSeconds time.Duration) *GRPCAPI {
    // 初始化GRPCAPI结构体
    return &GRPCAPI{
        // 当前时间函数
        now: now,
        // 可查询的创建者
        queryableCreate: creator,
        // 查询引擎函数
        queryEngine: queryEngine,
        // 默认的最大解析秒数
        defaultMaxResolutionSeconds: defaultMaxResolutionSeconds,
    }
}
 
// RegisterQueryServer 函数接收一个 querypb.QueryServer 类型的参数,并返回一个函数。
// 该返回的函数接收一个 *grpc.Server 类型的参数,用于在 gRPC 服务器上注册 QueryServer。
//
// 参数:
//
//  queryServer: querypb.QueryServer 类型的参数,需要被注册的查询服务器。
//
// 返回值:
//
//  返回一个函数,该函数接收一个 *grpc.Server 类型的参数,并在 gRPC 服务器上注册 QueryServer。
func RegisterQueryServer(queryServer querypb.QueryServer) func(*grpc.Server) {
    // 返回一个函数,该函数接收一个 *grpc.Server 类型的参数
    return func(s *grpc.Server) {
        // 在 gRPC 服务器上注册 QueryServer
        // querypb.RegisterQueryServer 是 gRPC 提供的注册函数
        querypb.RegisterQueryServer(s, queryServer)
    }
}
 
// Query 函数是 GRPCAPI 结构体中的一个方法,用于处理查询请求。
// 它接收一个 *querypb.QueryRequest 类型的请求和一个 querypb.Query_QueryServer 类型的服务器接口,
// 返回一个 error 类型的错误值。
//
// 参数:
//
//  request: *querypb.QueryRequest,包含查询请求信息的结构体指针。
//  server: querypb.Query_QueryServer,用于与客户端通信的服务器接口。
//
// 返回值:
//
//  返回一个 error 类型的错误值,如果查询过程中出现错误则返回该错误,否则返回 nil。
func (g *GRPCAPI) Query(request *querypb.QueryRequest, server querypb.Query_QueryServer) error {
    // 获取上下文环境
    ctx := context.Background()
    var ts time.Time
    // 判断请求中的时间戳是否为0
    if request.TimeSeconds == 0 {
        // 如果为0,则使用当前时间
        ts = g.now()
    } else {
        // 否则,将请求中的时间戳转换为time.Time类型
        ts = time.Unix(request.TimeSeconds, 0)
    }
 
    // 判断请求中的超时时间是否不为0
    if request.TimeoutSeconds != 0 {
        var cancel context.CancelFunc
        // 将请求中的超时时间转换为time.Duration类型
        timeout := time.Duration(request.TimeoutSeconds) * time.Second
        // 创建一个带超时的上下文环境
        ctx, cancel = context.WithTimeout(ctx, timeout)
        defer cancel() // 确保在函数结束时调用cancel函数
    }
 
    // 获取请求中的最大分辨率
    maxResolution := request.MaxResolutionSeconds
    // 判断请求中的最大分辨率是否为0
    if request.MaxResolutionSeconds == 0 {
        // 如果为0,则使用默认的最大分辨率
        maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000
    }
 
    // 将请求中的StoreMatchers转换为LabelMatchers
    storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers)
    if err != nil {
        return err
    }
 
    // 创建一个查询引擎
    qe := g.queryEngine(request.MaxResolutionSeconds)
    // 创建一个可查询的对象
    queryable := g.queryableCreate(
        request.EnableDedup,           // 是否启用去重
        request.ReplicaLabels,         // 复制标签
        storeMatchers,                 // 标签匹配器
        maxResolution,                 // 最大分辨率
        request.EnablePartialResponse, // 是否启用部分响应
        request.EnableQueryPushdown,   // 是否启用查询下推
        false,                         // 是否启用查询缓存
    )
    // 创建一个即时查询对象
    qry, err := qe.NewInstantQuery(queryable, request.Query, ts)
    if err != nil {
        return err
    }
 
    // 执行查询
    result := qry.Exec(ctx)
    // 发送查询警告
    if err := server.Send(querypb.NewQueryWarningsResponse(result.Warnings)); err != nil {
        return nil
    }
 
    // 根据查询结果的类型进行处理
    switch vector := result.Value.(type) {
    case promql.Scalar:
        // 处理标量类型的结果
        series := &prompb.TimeSeries{
            Samples: []prompb.Sample{{Value: vector.V, Timestamp: vector.T}},
        }
        if err := server.Send(querypb.NewQueryResponse(series)); err != nil {
            return err
        }
    case promql.Vector:
        // 处理向量类型的结果
        for _, sample := range vector {
            series := &prompb.TimeSeries{
                Labels:  labelpb.ZLabelsFromPromLabels(sample.Metric),
                Samples: prompb.SamplesFromPromqlPoints([]promql.Point{sample.Point}),
            }
            if err := server.Send(querypb.NewQueryResponse(series)); err != nil {
                return err
            }
        }
 
        return nil
    }
 
    return nil
}
 
func (g *GRPCAPI) QueryRange(request *querypb.QueryRangeRequest, srv querypb.Query_QueryRangeServer) error {
    // 创建一个背景上下文
    ctx := context.Background()
    // 如果请求中有超时时间设置,则创建带超时的上下文
    if request.TimeoutSeconds != 0 {
        var cancel context.CancelFunc
        ctx, cancel = context.WithTimeout(ctx, time.Duration(request.TimeoutSeconds))
        defer cancel()
    }
 
    // 获取最大分辨率
    maxResolution := request.MaxResolutionSeconds
    // 如果没有设置最大分辨率,则使用默认值
    if request.MaxResolutionSeconds == 0 {
        maxResolution = g.defaultMaxResolutionSeconds.Milliseconds() / 1000
    }
 
    // 将请求中的StoreMatchers转换为LabelMatchers
    storeMatchers, err := querypb.StoreMatchersToLabelMatchers(request.StoreMatchers)
    if err != nil {
        return err
    }
 
    // 创建查询引擎
    qe := g.queryEngine(request.MaxResolutionSeconds)
    // 创建可查询对象
    queryable := g.queryableCreate(
        request.EnableDedup,           // 请求中的副本标签
        request.ReplicaLabels,         // 请求中的副本标签
        storeMatchers,                 // 请求中的StoreMatchers
        maxResolution,                 // 请求中的最大分辨率
        request.EnablePartialResponse, // 请求中的部分响应
        request.EnableQueryPushdown,   // 请求中的查询下推
        false,                         // 请求中的降采样
    )
 
    // 获取查询的起始和结束时间
    startTime := time.Unix(request.StartTimeSeconds, 0)
    endTime := time.Unix(request.EndTimeSeconds, 0)
    // 获取查询的时间间隔
    interval := time.Duration(request.IntervalSeconds) * time.Second
 
    // 创建范围查询
    qry, err := qe.NewRangeQuery(queryable, request.Query, startTime, endTime, interval)
    if err != nil {
        return err
    }
 
    // 执行查询
    result := qry.Exec(ctx)
    // 发送查询结果的警告信息
    if err := srv.Send(querypb.NewQueryRangeWarningsResponse(result.Warnings)); err != nil {
        return err
    }
 
    // 根据查询结果的类型进行处理
    switch matrix := result.Value.(type) {
    case promql.Matrix:
        // 遍历查询结果中的每个时间序列
        for _, series := range matrix {
            // 将时间序列转换为协议缓冲区格式
            series := &prompb.TimeSeries{
                Labels:  labelpb.ZLabelsFromPromLabels(series.Metric),  // 将Prometheus的标签转换为Label格式
                Samples: prompb.SamplesFromPromqlPoints(series.Points), // 将Prometheus的点转换为Sample格式
            }
            // 发送时间序列到客户端
            if err := srv.Send(querypb.NewQueryRangeResponse(series)); err != nil {
                return err
            }
        }
 
        return nil
    }
 
    return nil
}

 

posted @   左扬  阅读(0)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2023-02-18 Istio从入门到精通——Istio Deployment Models
2021-02-18 RocketMQ(4.8.0)——DefaultMQPullConsumer 启动流程
2021-02-18 RocketMQ(4.8.0)——默认的两种消费者核心属性和方法
2021-02-18 RocketMQ(4.8.0)——消费流程
2021-02-18 RocketMQ(4.8.0)——生产者最佳实践总结
levels of contents
点击右上角即可分享
微信分享提示