当前位置: 首页 > news >正文

statsD学习笔记

一、前言

  1. Etsy社区的信仰

如果 Etsy 社区也有信仰,那一定是图表,Ian Malpass 在 Code as Craft 发表的文章中这么描述: 只要是变化的事件,我们就追踪它。有时候,为了记录事件的变化,我们从它不变时就用图表进行记录。通常,我们会从三个层面进行测量:网络、设备以及应用。应用指标往往是这三者中最难测量却又最重要的。应用指标与业务息息相关,随着应用的变化而变化。在此,我们不会早早地规划要测量的所有指标,将它们放在经典的测量管理系统中,我们只会将工程师可能测量或计时的指标以最简便的方式做成图表。(我们可以随时随地修改代码并部署它,因此,测量“ X 的发生频率”,“ X 在过去半小时内的发生情况”,只要有需求,就能很快实现。)

  1. statsD是什么

应用程序的监控是微服务中很重要的一环,监控主要包括四个方面的内容:指标(metrics)的采集、存储、展示以及相应的报警机制。目前相关的解决方案以及工具非常多,今天就介绍一种业界使用比较广泛的方案StatsD.

StatsD最早是2008年 Flickr 公司用 Perl 写的针对 Graphite、datadog 等监控数据后端存储开发的前端网络应用,2011 年 Etsy 公司用 node.js 重构。statsD狭义来讲,包含一个监听UDP(默认)或者TCP的守护程序,以及一套简单的协议。任何udp/tcp客户端都可以根据其协议发送数据到守护进程,守护进程聚合之后定时推送给后端,如graphite和influxdb等。

  1. StatsD by Example

开始探究statsD之前,我们先来看一个简单例子:一个网站的登录过程,鉴权成功则login,鉴权失败则提示错误信息

以下是其中的鉴权函数:

def login(username,password): if password_valid(username,password): render_welcome_page() else: render_error(403)

看到这个函数你第一反应可能就会想知道这个网站的登录频率,因为:1、鉴权对任何一个网站都很重要。2、登录次数的突然改变可能会是一个异常的早期预警(例如,不正确的DNS修改,失效的TLS证书等),让我们修改一下这个函数:

Import statsd

statsd_client = statsd.StatsClient('localhost',8125)

def login(username,password):

statsd_client.incr('login.invocations') if password_valid(username,password): render_welcome_page() else: render_error(403)

以上代码定义了一个stasd client,login函数每次执行的时候,都会将login.invocations这个counter的值加1,我们将程序发布,然后我们可以看到以下的图表:

 

另外,我们对自己的函数执行耗时会有一个预期,比如,20ms,借助stasd我们可以很方便的验证并监控自己的函数运行时长,我们修改一下代码:

Import statsd

statsd_client = statsd.StatsClient('localhost',8125)

statsd_client.timer('login.time')

def login(username,password):

statsd_client.incr('login.invocations') if password_valid(username,password): render_welcome_page() else: render_error(403)

以上代码会测量login函数的执行时长,然后发送到StatsD Server。我们将程序发布,然后我们可以看到以下的图表:

 

从以上走势图,我们可以很清楚的看到login函数执行时长在20ms上下波动

二、基本架构

StatsD系统包括三部分:客户端(client)、服务器(server)和后端(backend)。客户端植入于应用代码中,将相应的metrics上报给StatsD server。statsd server聚合这些metrics之后,定时发送给backends。backends则负责存储这些时间序列数据,并通过适当的图表工具展示

三、基本概念

  1. statsd协议

statsd采用简单的行协议,如下:

<bucket>:<value>|<type>[|@sample_rate]

  • bucket: 是一个metric的标识,可以看成一个metric的变量。

  • value: metrics值,通常是数字。

  • type: metric的类型,通常有timer、counter、gauge和set四种

  • sample_rate: 采样率,降低客户端到statsd服务器的带宽。客户端减少数据上报的频率,然后在发送的数据中加入采样频率,如0.1。statsd server收到上报的数据之后,如cnt=10,得知此数据是采样的数据,然后flush的时候,按采样频率恢复数据来发送给backend,即flush的时候,数据为cnt=10/0.1=100,而不是容易误解的10*0.1=1

例子:

statsd.login:10|c

statsd.memory:20|g

statsd.cost:30|ms

statsd.user:1|s

  1. udp和tcp

statsd可配置相应的server为UDP和TCP。默认为UDP。UDP和TCP各有优劣。但 UDP确实是不错的方式。

  • UDP不需要建立连接,速度很快,不会影响应用程序的性能。

  • “fire-and-forget”机制,就算statsd server挂了,也不会造成应用程序crash。 当然,UDP更适合于上报频率比较高的场景,就算丢几个包也无所谓,对于一些一天已上报的场景,任何一个丢包都影响很大。另外,对于网络环境比较差的场景,也更适合用TCP,会有相应的重发,确保数据可靠。

  1. Metric type

counter: 计数器

counter类型的指标,用来计数。在一个flush区间,把上报的值累加。值可以是正数或者负数

user.logins:10|c // user.logins + 10

user.logins:-1|c // user.logins - 1

user.logins:10|c|@0.1 // user.logins + 100

// users.logins = 10-1+100=109

gauge:标量(提到counter后面)

gauge是任意的一维标量值(如:内存、身高等值)。gague值不会像其它类型会在flush的时候清零,而是保持原有值。statsd只会将flush区间内最后一个值发到后端。另外,如果数值前加符号,会与前一个值累加。

age:10|g // age 为 10

age:+1|g // age 为 10 + 1 = 11

age:-1|g // age为 11 - 1 = 10

age:5|g // age为5,替代前一个值

注:gauge通常是在client进行统计好在发给StatsD的,如capacity:100|g 这样的gauge,即使我们发送多次,在StatsD里面,也只会保存100

Timer: 计时器

  timers用来记录一个操作的耗时,单位ms。statsd会记录平均值(mean)、最大值(upper)、最小值(lower)、累加值(sum)、平方和(sum_squares)、个数(count)以及部分百分值

rpt:100|ms

如下是在一个flush期间,发送了一个rpt的timer值100。以下是记录的值

count_80: 1,

mean_80: 100,

upper_80: 100,

sum_80: 100,

sum_squares_80: 10000,

std: 0,

upper: 100,

lower: 100,

count: 1,

count_ps: 0.1,

sum: 100,

sum_squares: 10000,

mean: 100,

median: 100

percentThreshold

对于timer数据,除了正常的统计之外还会计算一个百分比的数据(过滤掉峰值数据),默认是90%。可以通过percentThreshold修改这个值或配置多个值。例如在config.js中配置: percentThreshold: [50, 80]

以90为例,statsd会把一个flush期间上报的数据,去掉10%的峰值,即按大小取cnt*90%(四舍五入)个值来计算百分值。假如10s内上报以下10个值:

1,3,5,7,13,9,11,2,4,8

则只取10*90%=9个值,则去掉13。百分值即按剩下的9个值来计算

$KEY.mean_90 // (1+3+5+7+9+2+11+4+8)/9

$KEY.upper_90 // 11

$KEY.lower_90 // 1

histogram

 

有时仅记录操作的耗时并不能让我们很好的知道当前系统的情况,通常,timing都是跟histogram一起来使用的。在config.js配置文件中设置:

histogram: [ { metric: '', bins: [10, 100, 1000, 'inf']} ]

这样就开启了histogram,这个histogram的bin的间隔是[0, 10ms),[10ms - 100ms), [100ms - 1000ms), 以及[1000ms, +inf),如果一个timing落在了某个bin里面,相应的bin的计数就加1,譬如:

foo:1|ms

foo:100|ms

foo:1|ms

foo:1000|ms

那么statsd最终flush输出时:

histogram: { bin_10: 2, bin_100: 0, bin_1000: 1, bin_inf: 1 } } }

sets

记录flush期间,不重复的值。可以用来计算某个metric unique事件的个数,譬如对于一个接口,可能我们想知道有多少个user访问了,我们可以这样

request:1|s // user 1

request:2|s // user1 user2

request:1|s // user1 user2

StatsD就会展示这个request metric只有1,2两个用户访问了。

Multi-Metric Packets

statsD支持在一个packet里存放多个metric,以换行符分隔

gorets:1|c\nglork:320|ms\ngaugor:333|g\nuniques:765|s

Note:Be careful to keep the total length of the payload within your network's MTU. There is no single good value to use, but here are some guidelines for common network scenarios:

  • Fast Ethernet (1432) - This is most likely for Intranets.

  • Gigabit Ethernet (8932) - Jumbo frames can make use of this feature much more efficient.

  • Commodity Internet (512) - If you are routing over the internet a value in this range will be reasonable. You might be able to go higher, but you are at the mercy of all the hops in your route.

(These payload numbers take into account the maximum IP + UDP header sizes)

  1. flush interval

statsd 默认是10s执行一次flush。可通过flushInterval设置,单位ms

flushInterval: 20000. //设为20s

  1. reload 配置

设置automaticConfigReload,watch配置文件,如果修改,即reload配置文件。默认为true

  1. delete系列配置

metric上报时,每次flush之后,就会重置为0(gauge是保持原有值)。如果不上报这些空闲值,可以通过delete*来设置。

deleteGuages: true,

deleteTimers: true,

deleteSets: true,

deleteCounters: true

  1. StatsD Cluster Proxy

StatsD Cluster Proxy是个基于udp的proxy,它放置于多个statsD实例之前,它通过一致性哈希算法将一个metric发送到同一个StatsD实例,以便正确的聚合。同时它也完成简单的健康检测,判断某个实例是否掉线。

cp exampleProxyConfig.js proxyConfig.js

node proxy.js proxyConfig.js

四、安装&使用

statsd的安装非常简单。可选择两种方式:克隆源码和docker。

  1. 安装

  1. 克隆源码

首先需要安装node环境,然后到github克隆代码,修改相关配置启动即可。

cd /usr/local

git clone https://github.com/statsd/statsd.git

cd statsd

cp exampleConfig.js config.js

node /usr/local/statsd/stats.js /usr/local/statsd/config.js

  1. Docker

statsd支持以下两种方式

  • The official docker image on docker hub

  • Building the image from the bundled Dockerfile

  1. 配置

statsd提供默认的配置文件exampleConfig.js。可以参考相应的注释按需配置,接下来将简单介绍一些配置项。

{ port:8125,//statsd 服务端口

backends:["./backends/console"],

console:{ prettyprint:true },

flushInterval:30000,//statsd flush时间

percentThreshold: [80,90]

}

{ port: 8125,

graphitePort: 2003,

graphiteHost: "graphite.example.com",

backends: [ "./backends/graphite" ]

}

注:在statsd目录下,backends中包含了默认的后端:graphite和console。

  1. 使用

配置statsd的backends为console进行调试,启动statsd。然后使用netcat发送数据进行测试

echo "test:9|ms" | nc -w 1 -u 127.0.0.1 8125

注意:netcat -w参数表示超时时间,单位是秒

根据配置的flushinterval,statsd服务端会定时的将聚合好的数据发送到backends,如果配置的后端是console,则会输出:

Flushing stats at Fri Aug 26 2022 14:12:34 GMT+0800 (中国标准时间)

{

counters: {

'statsd.bad_lines_seen': 0,

'statsd.packets_received': 1,

'statsd.metrics_received': 1

},

timers: { test: [ 9 ] },

gauges: { 'statsd.timestamp_lag': 0 },

timer_data: {

test: {

count_80: 1,

mean_80: 9,

upper_80: 9,

sum_80: 9,

sum_squares_80: 81,

count_90: 1,

mean_90: 9,

upper_90: 9,

sum_90: 9,

sum_squares_90: 81,

std: 0,

upper: 9,

lower: 9,

count: 1,

count_ps: 0.1,

sum: 9,

sum_squares: 81,

mean: 9,

median: 9

}

},

counter_rates: {

'statsd.bad_lines_seen': 0,

'statsd.packets_received': 0.1,

'statsd.metrics_received': 0.1

},

sets: {},

pctThreshold: [ 80, 90 ]

}

Flushing stats at Fri Aug 26 2022 14:12:44 GMT+0800 (中国标准时间)

{

counters: {

'statsd.bad_lines_seen': 0,

'statsd.packets_received': 0,

'statsd.metrics_received': 0

},

timers: { test: [] },

gauges: { 'statsd.timestamp_lag': 0 },

timer_data: { test: { count_ps: 0, count: 0 } },

counter_rates: {

'statsd.bad_lines_seen': 0,

'statsd.packets_received': 0,

'statsd.metrics_received': 0

},

sets: {},

pctThreshold: [ 80, 90 ]

}

五、扩展

  1. backends

StatsD支持可插拔backends,安装包中默认带有graphite后端。可以将其他后端作为简单的npm软件包进行分发和安装:

amqp-backend ,

ganglia-backend ,

librato-backend ,

socket.io-backend ,

statsd-backend ,

mongo-backend ,

mysql-backend ,

datadog-backend ,

monitis backend,

instrumental backend ,

hosted graphite backend ,

statsd aggregation backend ,

zabbix-backend ,

opentsdb backend ,

influxdb backend ,

stackdriver backend ,

couchdb-backend ,

elasticsearch backend ,

Google BigQuery backend,

  1. 服务端实现

StatsD最初由Etsy的Erik Kastner编写,它基于Flickr的想法以及Cal Henderson的这篇文章:Counting and Timing。2011年该服务器用Nodejs编写重写,但是从那时起已经有其他语言的实现:

brubeck - Server in C ,

clj-statsd-svr — Clojure server ,

gographite — Server in Go ,

gostatsd — Server in Go ,

netdata - Embedded statsd server in the netdata server, in C, with visualization, Net::Statsd::Server — Perl server, also available on CPAN ,

Py-Statsd — Server and Client ,

Ruby-Statsdserver — Ruby server ,

statsd-c — Server in C ,

statsdaemon (bitly) — Server in Go ,

statsdaemon (vimeo) — Server in Go,

statsdcc - Server in C++ ,

statsdpy — Python/eventlet ,

Server Statsify - Server in C# ,

statsite — Server in C ,

bioyino — High performance multithreaded server written in Rust

  1. 客户端实现

客户端主要是根据statsd协议,通过UDP/TCP向守护进程通信。常见的实现有:

Node

lynx — Node.js client used by Mozilla, Nodejitsu, etc.

Node-Statsd — Node.js client

node-statsd-client — Node.js client

node-statsd-instrument — Node.js client

statistik - Node.js client with timers & CLI

statsy - clean idiomatic statsd client

Java

java-statsd-client — Lightweight (zero deps) Java client

Statsd over SLF4J — Java client with SLF4J logging tie-in

play-statsd — Play Framework 2.0 client for Java and Scala

statsd-netty — Netty-based Java 8 client

Python

Py-Statsd — Server and Client

Python-Statsd — Python client

pystatsd — Python client

Django-Statsd — Django client

Ruby

statsd-instrument — Ruby client

statsd — Ruby client (needs new maintainer)

Statsd-Client — Ruby client (not maintained)

C

C client — A trivial C client

Cpp

statsd-client-cpp — StatsD Client in CPP

cpp-statsd-client — A header-only StatsD client implemented in C++

.net

NStatsD.Client — .NET 4.0 client

graphite-client — .NET client library for StatsD and Graphite

StatsC — An asynchronous client with built-in support for batching

JustEat.StatsD — A .NET library for publishing metrics to statsd. Targets both .NET full framework and .NET Standard 2.0.

Statsify - .NET client

六、总结

  • statsD接收client发送过来的metrics,进行聚合之后,发送到后端

  • statsD可以进行程序性能指标和业务指标的收集,监控

  • statsD默认支持graphite作为backend,但同时支持很多第三方backends

  • 基于statsD可以方便的做一些适合自己业务场景的定制化开发,正所谓源码在手,天下我

  • 七、例子

  • 开发一个sdk获取操作系统的一些性能指标,包括cpu,free memory,procs,uptime等参数,以下是一些采集代码:

  •  

  • app采集操作系统的一些参数指标,通过statsD client发送给statsD,statsD经过聚合之后,发送到console,打印出来:

  •  

相关文章:

  • 坠落的蚂蚁(暑假每日一题 40)
  • TV蓝牙无法被搜索问题解决记录:REQUEST_DISCOVERABLE ActivityNotFoundException
  • 【JavaScript 逆向】猿人学 web 第六题:回溯
  • 最牛逼的 Java 日志框架,性能无敌,横扫所有对手
  • CREO:CREO软件之装配设计界面的简介、装配图设计流程、案例应用(图文教程)之详细攻略
  • 【赛码网刷题】动态规划之上台阶
  • Java 的开发效率究竟比 C++ 高在哪里?
  • python random应用实例 从可选池随机选取指定个数的元素并随机排序
  • 【Java成王之路】EE初阶第二十二篇 博客系统(页面设计)
  • 编译mtd-utils(使用uclibc编译)
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • springboot网络安全考核平台设计毕业设计源码042335
  • 神经网络常用的训练方式,人工神经网络训练过程
  • WebSocket快速入门及基本使用
  • 牛视源码定制,抖音矩阵系统,别和谐啊、、、
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 2017年终总结、随想
  • Android开源项目规范总结
  • Codepen 每日精选(2018-3-25)
  • ES6之路之模块详解
  • gf框架之分页模块(五) - 自定义分页
  • java中的hashCode
  • node 版本过低
  • VuePress 静态网站生成
  • WinRAR存在严重的安全漏洞影响5亿用户
  • 汉诺塔算法
  • 马上搞懂 GeoJSON
  • 设计模式走一遍---观察者模式
  • 想晋级高级工程师只知道表面是不够的!Git内部原理介绍
  • 项目实战-Api的解决方案
  • 学习Vue.js的五个小例子
  • 延迟脚本的方式
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • 积累各种好的链接
  • ​总结MySQL 的一些知识点:MySQL 选择数据库​
  • #LLM入门|Prompt#3.3_存储_Memory
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • $.each()与$(selector).each()
  • (1) caustics\
  • (AngularJS)Angular 控制器之间通信初探
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (原創) 物件導向與老子思想 (OO)
  • .equals()到底是什么意思?
  • .net 微服务 服务保护 自动重试 Polly
  • .net(C#)中String.Format如何使用
  • .NET4.0并行计算技术基础(1)
  • .net连接MySQL的方法
  • .net下简单快捷的数值高低位切换
  • .net专家(张羿专栏)
  • /usr/local/nginx/logs/nginx.pid failed (2: No such file or directory)
  • @ 代码随想录算法训练营第8周(C语言)|Day57(动态规划)