众所周知,StatsD 负责收集并聚合测量值。之后,它会将数据传给 Graphite,后者以时间序列为依据存储数据,并绘制图表。但是,我们不知道,基于 http 访问的图表在展示时,是基于每秒钟的请求数,每次留存的平均请求数还是其它。让我们就以此为目标,来一探究竟吧!本文系 OneAPM 工程师编译整理。

StatsD

为了全面了解 StatsD 的工作原理,我阅读了它的源码。之前我就耳闻 StatsD 是一种简单的应用,但读过源码后才发现它竟如此简单!在主脚本文件只有300多行代码,而Graphite 的后端代码只有150行左右。

StatsD 中的概念

这个文档中,列出了一些需要理解的 StatsD 概念。

Buckets

当一个 Whisper 文件被创建,它会有一个不会改变的固定大小。在这个文件中可能有多个 "buckets" 对应于不同分别率的数据点,每个 bucket 也有一个保留属性指明数据点应该在 bucket 中应该被保留的时间长度,Whisper 执行一些简单的数学计算来计算出多少数据点会被实际保存在每个 bucket 中。

Values

每个 stat 都有一个 value,该值的解释方式依赖于 modifier。通常,values 应该是整数。

Flush Interval

在 flush interval (冲洗间隔,通常为10秒)超时之后,stats 会聚集起来,传送到上游的后端服务。

测量值类别

计数器

计数器很简单。它会给 bucket 加 value,并存储在内存中,直到 flush interval 超时。

让我们看一下生成计数器 stats 的源码,该 stats 会被推送到后端。

for (key in counters) {
var value = counters[key];
var valuePerSecond = value / (flushInterval / 1000); // calculate "per second" rate statString += 'stats.'+ key + ' ' + valuePerSecond + ' ' + ts + "\n";
statString += 'stats_counts.' + key + ' ' + value + ' ' + ts + "\n"; numStats += 1;
}

首先,StatsD 会迭代它收到的所有计数器,对每个计数器它都会分配两个变量。一个变量用于存储计数器的 value,另一个存储 per-second value。之后,它会将 values 加至 statString,同时增加 numStats 变量的值。

如果你使用默认的 flush interval(10秒),并在每个间隔通过某个计数器给 StatsD 传送7个增量。则计时器的 value 为 7,而 per-second value 为 0.7。

计时器

计时器用于收集数字。他们不必要包含时间值。你可以收集某个存储器中的字节数、对象数或任意数字。计时器的一大好处在于,你可以得到平均值、总值、计数值和上下限值。给 StatsD 设置一个计时器,就能在数据传送给 Graphite 之前自动计算这些量。

计时器的源码比计数器的源码要稍微复杂一些。

for (key in timers) {
if (timers[key].length > 0) {
var values = timers[key].sort(function (a,b) { return a-b; });
var count = values.length;
var min = values[0];
var max = values[count - 1]; var cumulativeValues = [min];
for (var i = 1; i < count; i++) {
cumulativeValues.push(values[i] + cumulativeValues[i-1]);
} var sum = min;
var mean = min;
var maxAtThreshold = max; var message = ""; var key2; for (key2 in pctThreshold) {
var pct = pctThreshold[key2];
if (count > 1) {
var thresholdIndex = Math.round(((100 - pct) / 100) * count);
var numInThreshold = count - thresholdIndex; maxAtThreshold = values[numInThreshold - 1];
sum = cumulativeValues[numInThreshold - 1];
mean = sum / numInThreshold;
} var clean_pct = '' + pct;
clean_pct.replace('.', '_');
message += 'stats.timers.' + key + '.mean_' + clean_pct + ' ' + mean + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.upper_' + clean_pct + ' ' + maxAtThreshold + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.sum_' + clean_pct + ' ' + sum + ' ' + ts + "\n";
} sum = cumulativeValues[count-1];
mean = sum / count; message += 'stats.timers.' + key + '.upper ' + max + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.lower ' + min + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.count ' + count + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.sum ' + sum + ' ' + ts + "\n";
message += 'stats.timers.' + key + '.mean ' + mean + ' ' + ts + "\n";
statString += message; numStats += 1;
}
}

如果在默认的 flush interval 内,你将下列计数器 values 传给 StatsD:

  • 450
  • 120
  • 553
  • 994
  • 334
  • 844
  • 675
  • 496

StatsD 将会计数下面的 values:

  • mean_90 496
  • upper_90 844
  • sum_90 3472
  • upper 994
  • lower 120
  • count 8
  • sum 4466
  • mean 558.25

Gauges

一个 guage 代表着时间段内某点的任意 vaule,是 StatsD 中最简单的类型。你可以给它传任意值,它会传给后端。

Gauge stats 的源码只有短短四行。

for (key in gauges) {
statString += 'stats.gauges.' + key + ' ' + gauges[key] + ' ' + ts + "\n";
numStats += 1;
}

给 StatsD 传一个数字,它会不经处理地将该数字传到后端。值得注意的是,在一个 flush interval 内,只有 gauge 最后的值会传送到后端。因此,如果你在一个 flush interval 内,将下面的 gauge 值传给 StatsD:

  • 643
  • 754
  • 583

会传到后端的值只有583而已。该 gauge 的值会一直存储在内存中,直到 flush interval 结束才传值。

Graphite

现在,我们已经了解数据是怎样从 StatsD 传出来的,让我们看看它在 Graphite 里是如何存储并处理的。

总览

在 Graphite 文档里,我们可以找到 Graphite 概览,此概览总结了 Graphite 的两个要点:

  • Graphite 存储数值型带有时间序列的数据。
  • Graphite 按需绘制图表。

Graphite 由三部分组成:

  • carbon :监听时间序列的数据的后台程序。
  • whisper:一个简单的数据库库,用来存储时间序列数据。
  • webapp: Django webapp,使用 Cairo 来根据需要呈现图形。

Graphite 当做时间序列数据的格式如下:

<key> <numeric value> <timestamp>

存储方案

Graphite 采用可配置的存储方案用以定义所存数据的留存率。它会给数据路径匹配特定的模式,从而决定所存数据的频率和来历。

以下配置示例截取自 StatsD 文档。

[stats]
pattern = ^stats\..*
retentions = 10:2160,60:10080,600:262974

该示例表明,匹配上述样式的数据都会套用这些留存。留存的格式为 frequency: history。所以,该配置允许我们将10秒钟的数据存储6个小时,1分钟的数据存储1周,10分钟的数据存储5年。

在 Graphite 显示计时器

了解了这么多,我们来看看一个简单的 ruby 脚本,该脚本能收集 HTTP 请求的时间。

#!/usr/bin/env ruby

require 'rubygems' if RUBY_VERSION < '1.9.0'
require './statsdclient.rb'
require 'typhoeus' Statsd.host = 'localhost'
Statsd.port = 8125 def to_ms time
(1000 * time).to_i
end while true
start_time = Time.now.to_f resp = Typhoeus::Request.get 'http://www.example.org/system/information' end_time = Time.now.to_f elapsed_time = (1000 * end_time) - (to_ms start_time)
response_time = to_ms resp.time
start_transfer_time = to_ms resp.start_transfer_time
app_connect_time = to_ms resp.app_connect_time
pretransfer_time = to_ms resp.pretransfer_time
connect_time = to_ms resp.connect_time
name_lookup_time = to_ms resp.name_lookup_time Statsd.timing('http_request.elapsed_time', elapsed_time)
Statsd.timing('http_request.response_time', response_time)
Statsd.timing('http_request.start_transfer_time', start_transfer_time)
Statsd.timing('http_request.app_connect_time', app_connect_time)
Statsd.timing('http_request.pretransfer_time', pretransfer_time)
Statsd.timing('http_request.connect_time', connect_time)
Statsd.timing('http_request.name_lookup_time', name_lookup_time) sleep 10
end

让我们看看该数据生成的 Graphite 图。该数据来自 2 分钟前,而 elapsed_time 则来自前面的脚本。

图像生成

Render URL

下面图片的 Render URL

/render/?width=586&height=308&from=-2minutes&target=stats.timers.http_request.elapsed_time.sum

Graphite 生成的图片

该图片简单地描绘了 http 请求在一段时间内的 elapsed_time 值。

JSON-data

Render URL

下面 JSON-data 的 Render URL

/render/?width=586&height=308&from=-2minutes&target=stats.timers.http_request.elapsed_time.sum&format=json

来自 Graphite 的 JSON-output

在下面的结果中,我们可以查看来自 Graphite 的源数据。这些数据来自12个不同的数据点,也即 StatsD 10 秒 flush internal 的两分钟。Graphite 绘制数据就是如此简单。

此外,借助 JSONLint,JSON-data 的数据显示更加美观。

[
{
"target": "stats.timers.http_request.elapsed_time.sum",
"datapoints": [
[
53.449951171875,
1343038130
],
[
50.3916015625,
1343038140
],
[
50.1357421875,
1343038150
],
[
39.601806640625,
1343038160
],
[
41.5263671875,
1343038170
],
[
34.3974609375,
1343038180
],
[
36.3818359375,
1343038190
],
[
35.009033203125,
1343038200
],
[
37.0087890625,
1343038210
],
[
38.486572265625,
1343038220
],
[
45.66064453125,
1343038230
],
[
null,
1343038240
]
]
}
]

在 Graphite 绘制 gauge 图像

下面的简单脚本能将 gauge 传送给 StatsD,模拟用户注册的过程。

#!/usr/bin/env ruby

require './statsdclient.rb'

Statsd.host = 'localhost'
Statsd.port = 8125 user_registrations = 1 while true
user_registrations += Random.rand 128 Statsd.gauge('user_registrations', user_registrations) sleep 10
end

图像显示——用户注册数量

Render URL

下面图片的 Render URL

/render/?width=586&height=308&from=-20minutes&target=stats.gauges.user_registrations

来自 Graphite 的图片

另一个简单的图片,展示总的注册数。

图片显示——每分钟的用户注册数

使用 Graphite 的衍生函数,可以获得每分钟的用户注册数量。

Render URL

下面图片的 Render URL

/render/?width=586&height=308&from=-20minutes&target=derivative(stats.gauges.user_registrations)

来自 Graphite 的图片

该图片所用的数据跟之前的图片一致,但是使用了衍生函数从而显示每分钟的注册率。

结论

深入了解 StatsD 与 Graphite 的工作原理,能让我们更加明白 StatsD 所传送的数据种类,如何传送,以及怎样更有效地根据 Graphite 读取数据。

原文地址:https://blog.pkhamre.com/understanding-statsd-and-graphite/

OneAPM 是应用性能管理领域的新兴领军企业,Cloud Insight 能帮助企业用户和开发者轻松实现:监控各项基础组件以及对数据进行聚合、过滤和筛选的功能,致力于打造一个更为强大的数据管理平台。想阅读更多技术文章,请访问 OneAPM 官方博客

如何深入理解 StatsD 与 Graphite ?的更多相关文章

  1. #研发解决方案介绍#基于StatsD+Graphite的智能监控解决方案

    郑昀 基于李丹和刘奎的文档 创建于2014/12/5 关键词:监控.dashboard.PHP.graphite.statsd.whisper.carbon.grafana.influxdb.Pyth ...

  2. StatsD!次世代系统监控的核心

    在互联网业务蒸蒸日上的今时今日,系统架构日渐复杂,随着软件产品和工程团队的变革,许多开源的监控工具应运而生,其中有一些相当出名,比如 Zabbix.Nagios 还有 StatsD.也有一些问题被大家 ...

  3. Pyhton开源框架(加强版)

    info:Djangourl:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC)风格的 ...

  4. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  5. Prometheus入门

    什么是TSDB? TSDB(Time Series Database)时序列数据库,我们可以简单的理解为一个优化后用来处理时间序列数据的软件,并且数据中的数组是由时间进行索引的. 时间序列数据库的特点 ...

  6. Kubernetes容器集群管理环境 - Prometheus监控篇

    一.Prometheus介绍之前已经详细介绍了Kubernetes集群部署篇,今天这里重点说下Kubernetes监控方案-Prometheus+Grafana.Prometheus(普罗米修斯)是一 ...

  7. 实战 Prometheus 搭建监控系统

    实战 Prometheus 搭建监控系统 Prometheus 是一款基于时序数据库的开源监控告警系统,说起 Prometheus 则不得不提 SoundCloud,这是一个在线音乐分享的平台,类似于 ...

  8. HBase 监控 | HBase Metrics 初探(一)

    前言:对于任意一个系统而言,做好监控都是非常重要的,HBase也不例外.经常,我们会从JMX中获取相关指标来做展示.对HBase进行监控,那这些指标是怎么生成的呢?如果你想自定义自己的监控指标又该怎么 ...

  9. 《Python Web开发实战》|百度网盘免费下载|Python Web开发

    <Python Web开发实战>|百度网盘免费下载|Python Web开发 提取码:rnz4 内容简介 这本书涵盖了Web开发的方方面面,可以分为如下部分: 1. 使用最新的Flask ...

随机推荐

  1. Oracle静态数据字典

    select * from user_tab_comments a where a.comments like '%操作%'  数据字典 寻找数据库中注释带有“操作”二字的所有表  静态数据字典 这类 ...

  2. Microsoft Office Access 不能在追加查询中追加所有记录

    有客户反映,我们的软件ACCESS数据库版本在使用时会出现"Microsoft Office Access 不能在追加查询中追加所有记录"的错误.使用客户的数据库调试软件发现错误出 ...

  3. Allegro设置十字大光标

    使用大十字光标,在摆放元器件时,容易对齐.在allegro中,可以通过设置实现大十字光标,其具体方法如下: 1.选择Setup->User Perferences,即可出现如下图所示界面: 2. ...

  4. 而在Jquery中则使用$.map()、$.each()来操作数组

    首先是普通的数组(索引为整数的数组): //$.map(arr,fn); //对数组中的每个元素调用fn函数逐个进行处理,fn函数将处理返回最后得到的一个新的数组 var arr = [9, 8, 7 ...

  5. 【转】Class.forName()用法详解

    ref: http://blog.csdn.net/kaiwii/article/details/7405761 主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class. ...

  6. Android开发之计算器(一)界面设计

    计算器开发主要涉及到LinearLayout布局.EditText.Button的使用.为android入门基础内容. 打开android studio选择创建一个新的工程,应用程序的名称为Calcu ...

  7. Color About——First

    最近在Android开发学习中遇到TextView背景色以及文字颜色着色问题,在此做个记录. 首先对于颜色的选择,我推荐W3C School,上面有对网页颜色的详尽说明以及实例,还提供了对于,同一种颜 ...

  8. CLR via C# 线程基础知识读书笔记

    1.线程的开销 a.线程内核对象 b.线程环境块 c.用户模式栈(1MB) d.内核模式栈 f.DLL线程连接和线程分离通知 2.线程的优先级由进程优先级和线程优先级共同组成 3.进程中所有的前台线程 ...

  9. C#不同页面之间通信的方法

    以前做项目的时候经常头疼两个页面之间的交互(汗),这几天看的MVVM项目,忽然感觉好简单的!我自己写了个简单的demo 可以简单实现2个页面之间的交互,新人第一次发博客,不喜勿喷 代码很简单,注释我就 ...

  10. hdu 5755 2016 Multi-University Training Contest 3 Gambler Bo 高斯消元模3同余方程

    http://acm.hdu.edu.cn/showproblem.php?pid=5755 题意:一个N*M的矩阵,改变一个格子,本身+2,四周+1.同时mod 3;问操作多少次,矩阵变为全0.输出 ...