不要因为没有掌声,而放弃梦想,我们需要的是坚持,而不是观众。
thanos组件版本:v0.16.0
Thanos Query
Thanos Query的作用
Thanos Query组件是http服务器 + grpc服务器,它的数据源是位于下游的已发现的实现STORE API的组件(例如Thanos Sidecar组件、Thanos Store组件、Thanos Ruler组件),同时实现了Prometheus官方的HTTP API。Thanos Query组件从下游处获得数据后,能进行合并、去重等操作,最后将结果返回给外部的客户端。因此,Thanos Query就是数据库中间件的角色。
使用github.com/oklog/run包来启动一组协程,这些协程的逻辑主要是启动了http server、grpc server、动态发现位于下游的实现STORE API的组件等。
架构
源码分析
启动参数
Thanos的启动命令格式如下,格式都是thanos开头(因为是同一个可执行二进制文件)。启动哪个组件,在于第一个参数,在本例子中是query,因此这条命令是启动query组件的逻辑。
1 | thanos query \ |
注意:
1、partial-response 一定要加上这个flag设立的目的是遵照数据一致性原则
如果1、2、3中有某个返回查询数据为空或者超时(默认是2分钟)的情况下,这次整体的结果是否还要保留:
开启的情况下 , 有点儿部分人成虎的感觉
不开启,则必须要求每个上游都要有结果才行,真一致性2、deduplicate
不去重的情况下,会看到相同数据了具体来自哪个数据源,尤其是prom和ruler源
去重开启,程序后台会对每次响应的数据源做个打分,选择优秀的源作为本次的gRPC对象
代码分析
main方法
来具体看看main方法。创建app对象,app对象包含了所有Thanos组件的启动函数,但真正启动时只从map中取出一个函数进行启动,取出哪个函数取决于启动命令。
1 | func main() { |
registerQuery函数
1 | func registerQuery(app *extkingpin.App) { |
runQuery函数
1 | //使用run.Group对象来启动http server、grpc server、服务发现协程。 |
QueryAPI结构体及其方法
1 | // QueryAPI is an API used by Thanos Query. |
querier结构体及其方法
实现了 Querier接口(github.com/prometheus/prometheus/storage/interface.go),此接口的核心方法是Select(…),这个方法在/query和/series等接口中都会被使用到。
1 | type querier struct { |
1 |
|
1 | // 获取指标,调用的是属性proxy的Series(...)方法 |
ProxyStore对象
1 | // ProxyStore implements the store API that proxies request to all given underlying stores. |
查询指标时,会从下游的所有的Store API的组件中查询指标以及进行合并、去重(如果设置了)
1 | /* |
小结
本文分析了代码的轮廓,还有许多细节没有被提及,但Thanos Query组件的代码结构清晰易懂,使用了github.com/oklog/run包来启动一组协程,编写http server和grpc server的思路、动态发现下游Store API组件的套路都值得模仿。
Thanos Sidecar
Thanos Sidecar的作用
Thanos Query组件和prometheus实例绑定在一起,三大作用:
- 作为访问代理,对客户端暴露grpc接口,业务逻辑是访问其绑定的prometheus实例的http接口,从而获取metrics和rule数据,最终返回给客户端;
- 如果开启对象存储功能,会将promethues tsdb目录下的所有block目录上传至指定的对象存储系统中;
- 监听promethues配置文件的变化,发现文件变化后也是访问prometheus实例的http接口让prometheus重载配置。
使用github.com/oklog/run包来启动一组协程,这些协程的逻辑主要是启动了http server、grpc server、动态发现位于下游的实现STORE API的组件等。
源码分析
启动参数
Thanos的启动命令格式如下,格式都是thanos开头(因为是同一个可执行二进制文件)。启动哪个组件,在于第一个参数,在本例子中是sidecar,因此这条命令是启动sidecar组件的逻辑。
1 | thanos sidecar \ |
代码分析
main方法
来具体看看main方法。创建app对象,app对象包含了所有Thanos组件的启动函数,但真正启动时只从map中取出一个函数进行启动,取出哪个函数取决于启动命令。
1 | func main() { |
registerSidecar方法
1 |
|
runSidecar方法
使用run.Group对象来启动http server、grpc server、传输block目录至对象存储的协程、监听prometheus配置文件的协程、定期检测prometheus实例存活的协程。
细节说明:
- 查看prometheus实例的心跳机制是通过/api/v1/status/config接口;
- 监听prometheus配置文件变化的工具包是github.com/fsnotify/fsnotify;
- 开启上传block功能,则每30s遍历prometheus tsdb目录下的所有的block目录(已上传的block或空block会被忽略,默认情况下被压缩过的block也会被忽略),并上传相应的文件至对象存储;
- 获取不到prometheus实例的external label或者prometheus没有配置external label,会导致sidecar启动失败。
1 | func runSidecar( |
小结
Thanos Sidecar组件的代码逻辑简单、易懂,通过http协议访问与其绑定的prometheus实例,从prometheus实例中获取到的数据则通过grpc接口对外进行暴露,遍历所有block目录进行文件上传,还有监听promethues配置文件变化的小功能。
参考: