【一个构想】pull方式获取expoter上的数据,如何更加精简?

作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


背景

已知:在prometheus中,每个业务节点通过prometheus client API 来在本地汇聚数据。
然后提供HTTP协议,通过 /metrics 路径把业务节点上的metric数据暴露给prometheus.

协议采用文本格式+GZIP压缩,虽然GZIP压缩率比较高,但是文本协议终归还是不够精简。

思路

如何让每个expoter上的数据传输尽可能的精简呢?

  1. 二进制协议
  2. 通过字典来合并重复的字符串
  3. 既然是一次完整的采样,那么时间戳一定都是一样的。只要采用一个时间戳就行了。
  4. 浮点数如果没有小数部分,就按整数来存储;整数采用7bit的变长编码,节约空间。

具体存储格式

序列化后的格式可以表示如下:

message Metric{
    map<int32, int32> labels = 1;  //用下标来表示字典中存储的第N个字符串
    repeated float64 values = 2;
}

message Metrics{
    bytes gzip_dict = 1;   //字典表,经过GZIP压缩
    repeated Metric metrics = 2;  // 监控数据
   int64 global_timestamp = 3;  //全局的时间戳
}

运行期的字典,可以表示如下:

type LabelDictForEncode struct{
    Data []byte  //所有的label name 和 label value顺序存放在大数组中,用\0分割
    Labels map[string]int  // 每个字符串,指向大数组中的下标
}

type LabelDictForDecode struct{
    Data []byte  //所有的label name 和 label value顺序存放在大数组中,用\0分割
    Labels map[int][]byte  //下标,指向大数组中的某一段
}

因此,可以把:
foo{label1="value1",label2="value2"}
bar{label1="value1",label2="value3"}
简化为以下字典:

  • foo
  • bar
  • label1
  • label2
  • value1
  • value2
  • value3
    相同的内容越多,压缩的空间越大。
    当然,还可以排序,合并相同前缀……

最后,每个metric只要索引字典里面的值就可以了。
传输前使用ZSTD压缩,占用空间会进一步缩小。

上一篇:事件环 Event Loop


下一篇:无重复字符的最长子串-滑动窗口法