度量是大多数软件的一个基本部分. 度量能够窥探, 监控一个软件系统的在运行时的行为. 并在系统异常的时候进行报警.
Elixir经常被称赞为一个跑的快的语言. 特别是在分布式, 并发应用程序方面. 但是 "快" 如果没有测量指标, 重构会变得异常困难, 也难于判断是否性能得到改善.
下面我们来俺看用什么方式和工具在Elixir应用程序中搜集度量信息.
工具栈概述
我们的统计数据搜集整体架构如下:
下面更加深入的探索这个架构和其中的组件.
存储度量数据
现在我们从架构的核心开始: 用于存储度量数据的数据量. 我们使用 InfluxDB 一个开源的时序数据库, 它特别适合存储度量数据. 并且提供一堆非常有用的功能.
InfluxDB 数据库由一组被称为 measurements
的东西组成. 每个 measurements
是一个数据点的集合. 每个数据点由一个时间戳, 一组标记(tags), 和一组值(values)组成.
标签(Tags)用元数据标记measurements
. 例如一个measurement常用的标记为host
, 它保存了报告该measurement
的主机, 在一个measurement
中标记是被所有点共享的. 相反 值是和measurement
相关的, 例如标识完成一个HTTP请求的时间被称为request_time
. 一个数据点可以有多个值, 当一个点只有一个值的时候, 我们通常叫这个值只是一个value
, 因为measurement
名称足够判断他标识的是哪个值.
在本文的剩余部分, 我们把measurements
称作measurements
和points
使阅读体验更流畅.
InfluxDB 还提供了丰富的SQL语法来查询数据. 以及重采用数据已优化存储.
InfluxDB 支持一些存储数据的方法: measurements
可以通过HTTP接口或UDP即可上报, 可以编码为JSON或InfluxDB自己的协议. UDP接口天然的有更快的速度, 因为它没有想HTTP协议和TCP协议那样的开销, 但是, 它不能保证数据成功投递到目标. 选择HTTP还是UDP
我们使用 influxdb-relay, 它设置了一个类似负载均衡的架构添加一些复制到统计存储
统计报告
Telegraf
InfluxDB 的开发者为我们提供了另外一个开源的工具叫做 Telegraf. Telegraf 基本上就是一个守护进程, 它没隔N秒从各种数据源收集数据并投递到不同的输出目标. 输入和输出是通过"输入插件"
和"输出插件"
进行管理. 这里我们使用两个主要输入员, 以及一个输出目标.
输入
第一个Telegraf要收集的输入源是"系统统计", 包含CPU使用率, 内存使用率, 磁盘使用率, 网络使用率等系统级别的运行信息.Telegraf负责读取这些系统的状态值. 另外一个我们使用的输入是内置在Telegraf中的UDP监听器: 它提供了一个到InfluxDB的UDP接口的一个镜像, 并通过本地Telegraf进行把UDP端口暴露出来(作用是作为一个管道联通本机的UDP接口和InfluxDB的UDP接口)
输出
我们只用一个输出插件: InfluxDB 插件. 该插件很简单: 它把通过输入插件收集到的measurements
上报给InfluxDB. 它还提供了一些从没隔输入插件中可以应用于所有measurements
的全局标记.(我们使用其来设置host
标记给应用程序和系统统计), 以及measurement
过滤. 但是最有用的功能是该插件可以通过InfluxDB的HTTP接口通信, 而不是UDP接口: 这保证了统计包能够到达InfluxDB而不会丢失.
聚合
Telegraf 作为一个在我们的应用程序和InfluxDB之间的中间件, 这种架构有几个有点:
我们可以从应用程序中通过UDP接口发送统计数据给Telegraf, 在上报统计数据的时候有极大的速度提升, 另外 Telegraf 是在本地运行的, 并且通过HTTP协议把统计信息发送给InfluxDB, UDP丢包的风险得到极大的降低.
measurements
丢失的风险非常低.可以设置报告给InfluxDB的measurements数量阀值: Telegraf 会每隔N秒(5,或10秒是一个合理的值, 取决于应用程序)把这些值聚合后发送给InfluxDB, 这意味着, 每隔N秒, Telegraf 发送一个统计报告给InfluxDB, 减少了网络流量.
聚合的作用是可以降低报告的平率, 同时保持较高的采样频率. 同时减少因为上报统计数据给InfluxDB产生的网络流量.
Telegraf/InfluxDB 驱动
如果我们需要在Elixir应用程序中使用InfluxDB和Telegraf, 现在没有现成的Elixir的UDP接口可用(只有HTTP接口).
每个Elixir应用程序有其自己的Fluxter
模块:
defmodule MyApp.Fluxter do
use Fluxter
end
上面的代码把MyApp.Fluxter
转换为一个UDP连接的监控进程池. 要达到容错的目的, 我们要保证在一个应用程序的 supervision 树下启动这个池.
def start(_type, _args) do
children = [
Supervisor.Spec.supervisor(MyApp.Fluxter, []),
# ...
]
Supervisor.start_link(children, strategy: :one_for_one)
end
最后, 我们通过应用程序配置来配置这个池:
config :fluxter,
host: "localhost",
port: 8086
然后, 一旦Fluxter池启动, 我们可以通过下面的方式来报告统计:
def my_operation() do
MyApp.Fluxter.write("something_done", [my_tag: "foo"], 1)
end
关于 Fluxter 的详细信息, 可以参考 Fluxter文档
Erlang VM统计
对于Erlang虚拟机的统计, 我们使用 vmstats, 一个小巧的Erlang应用程序用于从Erlang虚拟机中搜集统计并报告给一个sink
: 一个sink
是一个实现了:vmstats_sink
行为的Erlang/Elixir模块. 我们使用Fluxter池作为这个sink
, 摈并且我们我们再每一个Elixir应用程序中都有一个等同的设置:
defmodule MyApp.Fluxter do
use Fluxter
@behaviour :vmstats_sink
def collect(_type, name, value) do
write(name, value: value)
end
end
查看vmstats的README文件了解用它能够做什么.
批处理
我们在短时间内会产生大量的统计报告, 因此我们需要对其进行聚合操作以减少发送给InfluxDB的数据.
{:ok, batch} = MyApp.Fluxter.start_batch("my_operation_success", [host: "eu-west"])
Enum.each(1..1_000_000, fn(_) ->
my_operation()
MyApp.Fluxter.write_to_batch(batch, 1)
end)
MyApp.Fluxter.flush_batch(batch)
统计可视化
所有我们搜集到和存储的统计数据如果没有可视化是毫无用处的. 我们使用Grafana作为我们的可视化工具, 它原生支持InfluxDB(可以直接查询InfluxDB数据库), 并且提供一打很有用的功能特性.
我们的仪表盘可视化效果如下:
上面我们使用的仪表盘用于监控应用程序的健康状态: 它显示了从系统和Erlang虚拟机搜集到的统计数据, 左上角的下拉菜单可以让我们选择要可视化的应用程序, 只需要点击选择不同的应用程序就可以查看系统和Erlang虚拟机的概述.
Grafana 提供了几个有用的功能: 它可以通过标记进行分组, 在一个图标上显示集群中不同的主机为不同的线条. 它还可以在数据上进行聚合排序操作, 而且外观也是非常容易定制的.
Grafana 是整个架构中一个最简单的部分, 但不是一个关键的部分.
报警
我们主要使用统计来测量我们的应用程序是如何运行的, 但我们还可以用它来做另一件非常有用的事情: 报警
.
比如我们可以统计Erlang的进程数量, 当其值偏离平均值太多的时候, 我们知道我们需要查看一下系统到底发生了什么异常的情况.
InfluxDB供应商的另一个产品 Kapacitor 它是一个守护进程, 可以重复的在InfluxDB运行查询, 然后在查询结果上运行一些分析.
Kapacitor 是一个开源框架, 用来处理, 监控和警告时间序列数据. Kapacitor 使用 TICKscript 脚本来定义任务.
Kapacitor 的任务是通过 Kapacitor scripts 来定义Identifier. 它是用 Kapacitor 领域语言编写的任务脚本. 用这种脚本可以做很多事,
stream
|from()
.measurement('pushboy_vm_modules')
|groupBy('host')
|deadman(15.0, 1m) //
.id('Pushboy []')
.message(' is updown')
.stateChangesOnly()
.slack()
该脚本检查 vm_modules
度量值, 如果吞吐量降低到每分钟15个点以下触发一个警告. (健康的应用程序每分钟会报告60个点), 你看到最后一行, 通过Slack发送警告给管理员(在Kapacitor的配置中指定). Kapacitor 支持多种通知方式, 包括限于Slack, PagerDuty或者电子邮件.
总结
本文显示了我们如何从Elixir应用程序中收集程序的运行数据, 以及我们如何使用它来监视应用程序的监控状态和性能, 当出现应用程序出现异常的时候我们能够获得警告, 以让我们在早期对应用程序异常情况进行处理, 避免系统发送大的故障导致的经济损失.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。