1.背景
- 目前IoTDB主要的功能是针对时间序列的数据存储,分析,无法进行数据采集。
- Prometheus 提供的是一整套监控体系。(Prometheus允许用户使用他们的数据库,而不仅仅是普罗米修斯数据库来存储时间序列数据库。)
- 服务核心组件,通过pull metrics从Exporter拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)
- pushgateway:类似于一个中转站,Prometheus Server只会通过pull方式拉取数据但是某些节点因为某些原因只能通过Push方式推送数据,这个时候就需要pushgateway了,它负责的是接收push来的数据并暴露给Prometheus Server,以待其拉取。
- Exporters/Jobs:负责收集目标对象(host,container...)的性能数据,并通过HTTP接口供Prometheus Server获取
- Service Discovery:服务发现,Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等,基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮询这些Target获取监控数据。
- Alertmanager:从Prometheus Server端接收到alerts后,会进行去除重复数据,分组,并路由到对方的接收方式,发出警报,常见的接收方式有电子邮件,pagerduty等
2.目标
IoTDB集成Prometheus,实现IoTDB针对时间序列的数据采集。完善IoTDB的功能。
可以看到,Prometheus向IoTDB中写入数据通过中间件,Prometheus读取IoTDB的数据也通过中间件,中间件的主要作用是将Prometheus格式的数据和IoTDB所需格式的数据相互转换。
- IoTDB-Prometheus:实现Prometheus和IoTDB之间的模式映射将Prometheus的metric {label1="label1_value",label2="label2_value"}映射到时序
- 对正确性和性能的测试,不仅要集成Prometheus,也要知道在繁重的负载下是否可以很好的工作,例如:以非常高的频率生成数据
- 性能测试:Avalanche Code: https://github.com/open-fresh/avalanche
支持通过Prometheus remote_write API接受数据的服务的负载测试,进行性能测试, - 正确性测试:Prombench,Code: https://github.com/ncabatoff/prombench
向Prometheus插入数据,然后查询Prometheus,将我们认为发送的内容与我们希望存储的内容进行比较。进行正确性测试
- 性能测试:Avalanche Code: https://github.com/open-fresh/avalanche
3.设计(前提:IoTDB Go Client)
3.1 配置Prometheus 远程存储
在Prometheus配置文件中配置远程存储的地址(由IoTDB-Prometheus提供)
eg:
3.2 IoTDB-Prometheus(接收Prometheus传输过来的数据/查询IoTDB中数据)
IoTDB-Prometheus为外部go服务,使用go语言编译器编译生成可执行文件
前提
Golang编译器
包含go-client的IoTDB
Prometheus读取和写入远程存储都是通过HTTP协议,将数据以snappy方式压缩到协议缓冲区中
IoTDB-Prometheus获取传输过来的数据,进行解压缩,反序列化,转化为IoTDB的数据格式(需要实现的)
调用go-client和IoTDB服务端进行数据交互(需要实现的)
3.3 将Prometheus的数据格式转成成IoTDB的数据格式
3.3.1问题
问题:Prometheus中label顺序不敏感,而在IoTDB中是敏感的
Prometheus的时序构成
metric name
label key
label value
IoTDB的时序构成:
storage group
path(time series ID)
measurement
在Prometheus中,使用一个metric和label的集合代表一条时间序列,label的顺序不影响
eg:'host = server1, data_center = DC1` 和 `data_center = DC1, host = server1`相等。
在IoTDB中
一个path由多个部分组成,比如`root.sg1.DC1.server1` 由一个存储组 `root.sg1`和两个节点`DC1` 和`server1`构成
节点的顺序是需要考虑的,`root.sg1.DC1.server1` 和 `root.sg1.server1.DC1` 是两条不同的时序。
关键点:需要记录每个label对应的顺序,确保Prometheus中label顺序不同的同一条时序对应到IoTDB中也是一条时序
需要解决的事情
- 怎样映射label key和它对应的order
- 在不知道所有的label key的情况下,怎么维护他们之间的顺序
3.3.2 解决方案
- 主要思想:
- 内存中Map <Metric, Map <Label Key, Order> > table结构维护label之间的顺序
- Prometheus中时序根据label顺序对应到IoTDB
- 实例
- 添加时序
- Prometheus时序:
(1)country{state=A,city=B,town=C}
(2)country{tem=D}
(3))country{state=A,city=B,town=C,tem=D}
- 假设存储组数量为5,则("country").hashCode() % 5为2,对应的存储组为sg2,测点为country
(1)对应的记录label顺序的table为
metric_name
label_key
order
country
state
0 country
city
1 country
town
2 (2)对应的记录label顺序的table为
metric_name
label_key
order
country
state
0 country
city
1 country
town
2 country
tem 3 (3)对应的记录label顺序的table为
metric_name
label_key
order
country
state
0 country
city
1 country
town
2 country
tem 3 (1)对应IoTDB时序为root.sg2.country .A.B.C.
(2)对应IoTDB时序为root.sg2.country.ph.ph.ph.D
(3)对应IoTDB时序为root.sg2.country.A.B.C.D
为了重启时候对table的恢复,在IoTDB中记录数据
root.LABEL_INFO.metric_name
root.LABEL_INFO.label_name
root.LABEL_INFO.label_order
country
state
0
country
city
1 country
town
2 country
tem 3
- Prometheus时序:
- 查询数据
查询数据:查询country中city=B的数据,city的order为1,country中order的最大值为3
对应到IoTDB中的查询为
select * from root.sg0.country.*.B
- 添加时序
- 设计
允许用户设置IoTDB中SG的数量,例如,`SG_NUMBER`=5。可以是`iotdb-engine.properties`中的参数.然后得到5个sg,比如“sg0,sg1,…,sg4”。
metric
认为是IoTDB中的一个measurement
可以通过hash决定metric会对应到哪个存储组,eg Metric.hashcode() % SG_NUMBE‘
在内存中维护一个table去保存对应的映射,在内存中的结构为:Map <Metric, Map <Label Key, Order> > table(改进:考虑到内存开销,可以只将表的一部分作为缓存保存在内存中)------- 解决问题:怎样映射label key和它对应的order
在IoTDB中存储metric,label以及其对应的顺序,通过三条时序(以便在IoTDB重启的时候恢复内存中的table)
'root.system_p.label_info.metric_name'
root.system_p.label_info.label_name'
- 'root.system_p.label_info.label_order`
添加metric时维护map ------------ 解决问题:在不知道所有label的情况下维护map
首先判断table.get(metric_name)为空,则向时序'root.LABEL_INFO.metric_name'中添加数据点,时间戳可以是系统当前的时间或者是0,1,2,3,value是metric name,同时在table中添加对应的metric
其次根据metric_name,label_key判断table.get(metric_name).get(label_key)为空,则向时序'root.system_p.label_info.label_name'中添加数据点,时间戳可以是系统当前的时间或者是0,1,2,3,value是label_key
同时也要向时序'root.system_p.label_info.label_order`中添加数据点,时间戳和对应的'root.system_p.label_info.label_name'中添加的数据点的时间戳相同,但是value是对应的metric中的最大的order+1
table.get(metric_name)中添加对应的label_key和lable_order.
生成IoTDB对应的时序
- string paths[]数组记录时序节点,int largest记录metric中label的order的最大值
- 已有的label,paths[label_order]=label_value
- 新的label,paths[largest+1]=label_value
- 遍历paths数组,null的位置填充ph,得到时序root+sg_name+metric + paths.sub(0, paths.size()-1)
3.4 参考文档
- https://docs.google.com/document/d/1UYOUd0YomS6NUitG9Ko0gqzYapn6eqhFuy_5F3NKVxg/edit#heading=h.mdfygy3ywxxh
- https://docs.google.com/document/d/1DZL1L0GccLvHbMJyop62cbaSvu1tkJ6Pp1Fp6FsR09U/edit#heading=h.58lkrsg45cf2
- https://issues.apache.org/jira/projects/IOTDB/issues/IOTDB-519?filter=allopenissues&orderby=cf%5B12310310%5D+ASC%2C+priority+DESC%2C+updated+DESC
- https://prometheus.io/docs/prometheus/latest/storage/
- https://songjiayang.gitbooks.io/prometheus/content/exporter/text.html
- https://www.taosdata.com/cn/documentation/insert/#Prometheus%E7%9B%B4%E6%8E%A5%E5%86%99%E5%85%A5