SpringBoot 2.0 + InfluxDB+ Sentinel 实时监控数据存储
前言
阿里巴巴提供的控制台只是用于演示 sentinel 的基本能力和工作流程,并没有依赖生产环境中所必需的组件,比如持久化的后端数据库、可靠的配置中心等。目前 sentinel 采用内存态的方式存储监控和规则数据,监控最长存储时间为 5 分钟,控制台重启后数据丢失。
企业版
这里推荐一下阿里云的官方版,ahas sentinel 控制台 是 sentinel 控制台的阿里云上版本,提供企业级的控制台服务,包括:
- 实时请求链路查看
- 还有各种酷炫的监控图表
- 可靠的实时监控和历史监控数据查询,无需自行存储、拉取
- 动态规则管理/推送,无需自行配置外部数据源
免费版,可以提供 5 个节点的免费额度。开通专业版即可享受不限量节点额度。
专业版没有实例连接限制,开通后每天前5个限流降级节点不计费,超出部分按3元/天/实例收取相应的费用。
思路
官方文档也提供了思路,若需要监控数据持久化的功能,可以自行扩展实现 metricsrepository 接口(0.2.0 版本),然后注册成 spring bean 并在相应位置通过 @qualifier 注解指定对应的 bean name 即可。metricsrepository 接口定义了以下功能:
save 与 saveall:存储对应的监控数据
querybyappandresourcebetween:查询某段时间内的某个应用的某个资源的监控数据
listresourcesofapp:查询某个应用下的所有资源
其中默认的监控数据类型为 metricentity,包含应用名称、时间戳、资源名称、异常数、请求通过数、请求拒绝数、平均响应时间等信息。
对于监控数据的存储,用户需要根据自己的存储精度,来考虑如何存储这些监控数据。显然我们要使用目前最流行的时序数据库influxdb
解决方案,不要问什么?闭眼享受就可以了。
选型
influxdb
是一个开源分布式时序、事件和指标数据库。使用 go 语言编写,无需外部依赖。
应用:性能监控,应用程序指标,物联网传感器数据和实时分析等的后端存储。
强大的类sql语法
内置http支持,使用http读写
基于事件:它支持任意的事件数据
无结构(无模式):可以是任意数量的列
可度量性:你可以实时对大量数据进行计算
持续高并发写入、无更新、数据压缩存储、低查询延时
支持min, max, sum, count, mean, median 等一系列函数
基于时间序列,支持与时间有关的相关函数(如最大,最小,求和等)
改造
influxdb 安装
首先你得先有个 influxdb 数据库,建议使用 docker 方式安装,更多可以参考文末链接。
需要注意的是,从1.1.0版开始不推荐使用管理员界面,并将在1.3.0版中删除。默认情况下禁用。如果需要,仍可以通过设置如下环境变量来启用它。
以下端口很重要,并由influxdb
使用。
- 8086 http api端口
- 8083 管理员界面端口(如果已启用,1.7.8貌似启用也不好使),官方推荐使用
chronograf
通过该命令, 生成默认配置文件:
docker run --rm influxdb influxd config > influxdb.conf
创建并运行容器:
docker run -d \ -p 8086:8086 \ -p 8083:8083 \ -e influxdb_admin_enabled=true \ -v $pwd/data:/var/lib/influxdb/ \ -v $pwd/config/influxdb.conf:/etc/influxdb/influxdb.conf:ro \ --name influx \ influxdb -config /etc/influxdb/influxdb.conf
生产环境一定要开启权限验证,修改 influxdb.conf 配置:
[http] enabled = true bind-address = ":8086" auth-enabled = true # 鉴权
创建用户:
# 进入容器 docker exec -it influx /bin/sh # 连接 influx # 创建用户 create user admin with password 'admin' with all privileges
退出重新登录:
# 用户密码登录 influx -username admin -password admin # 创建数据库 create database sentinel_log
sentinel 控制台改造
pom.xml引入 influxdb 官方开源工具包:
<dependency> <groupid>org.influxdb</groupid> <artifactid>influxdb-java</artifactid> <version>2.15</version> </dependency>
配置文件引入:
# 自行替换 api 地址:端口 spring.influx.url=http://127.0.0.1:8086 spring.influx.user=admin spring.influx.password=admin spring.influx.database=sentinel_log
配置数据源:
/** * influxdb 配置 * 创建者 爪哇笔记 * 网址 https://blog.52itstyle.vip */ @configuration public class influxdbconfig { @value("${spring.influx.url:''}") private string influxdburl; @value("${spring.influx.user:''}") private string username; @value("${spring.influx.password:''}") private string password; @value("${spring.influx.database:''}") private string database; @bean public influxdb influxdb(){ influxdb influxdb = influxdbfactory.connect(influxdburl, username, password); try { /** * 异步插入: * enablebatch这里第一个是point的个数,第二个是时间,单位毫秒 * point的个数和时间是联合使用的,如果满100条或者2000毫秒 * 满足任何一个条件就会发送一次写的请求。 */ influxdb.setdatabase(database) .enablebatch(100,2000, timeunit.milliseconds); } catch (exception e) { e.printstacktrace(); } finally { influxdb.setretentionpolicy("autogen"); } influxdb.setloglevel(influxdb.loglevel.basic); return influxdb; } }
实现 metricsrepository 接口,重写实现:
/** * 数据curd * 创建者 爪哇笔记 * 网址 https://blog.52itstyle.vip */ @component("ininfluxdbmetricsrepository") public class ininfluxdbmetricsrepository implements metricsrepository<metricentity> { @autowired public influxdb influxdb; @override public synchronized void save(metricentity metric) { //省略代码,太长了,参考内存写法,参考 saveall 这里是单条插入 } @override public synchronized void saveall(iterable<metricentity> metrics) { if (metrics == null) { return; } batchpoints batchpoints = batchpoints.builder() .tag("async", "true") .consistency(influxdb.consistencylevel.all) .build(); metrics.foreach(metric->{ point point = point .measurement("sentinelinfo") //这里使用微妙、如果还有覆盖数据就使用纳秒,保证 time 和 tag 唯一就可以 .time(system.currenttimemillis(), timeunit.microseconds) .tag("app",metric.getapp())//tag 数据走索引 .addfield("gmtcreate", metric.getgmtcreate().gettime()) .addfield("gmtmodified", metric.getgmtmodified().gettime()) .addfield("timestamp", metric.gettimestamp().gettime()) .addfield("resource", metric.getresource()) .addfield("passqps", metric.getpassqps()) .addfield("successqps", metric.getsuccessqps()) .addfield("blockqps", metric.getblockqps()) .addfield("exceptionqps", metric.getexceptionqps()) .addfield("rt", metric.getrt()) .addfield("count", metric.getcount()) .addfield("resourcecode", metric.getresourcecode()) .build(); batchpoints.point(point); }); //批量插入 influxdb.write(batchpoints); } @override public synchronized list<metricentity> querybyappandresourcebetween(string app, string resource, long starttime, long endtime) { //省略代码,太长了,参考内存写法 } @override public synchronized list<string> listresourcesofapp(string app) { //省略代码,太长了,参考内存写法 } }
分别修改 metricfetcher
和 metriccontroller
中 metricstore
的注入方式,使用 influxdb
实现:
/** * 注入 * 创建者 爪哇笔记 * 网址 https://blog.52itstyle.vip */ @autowired @qualifier("ininfluxdbmetricsrepository") private metricsrepository<metricentity> metricstore;
配置完成后,我们重启控制台,然后访问客户端项目,如果控制台打印以下数据,说明配置成功:
2019-09-21 19:47:25 [sentinel-dashboard-metrics-fetchworker-thread-2] info okhttp3.okhttpclient - --> post http://118.190.247.102:8086/write?db=sentinel_log&precision=n&consistency=all (486-byte body) 2019-09-21 19:47:25 [sentinel-dashboard-metrics-fetchworker-thread-2] info okhttp3.okhttpclient - <-- 204 no content http://118.190.247.102:8086/write?db=sentinel_log&precision=n&consistency=all (46ms, 0-byte body)
多访问几次客户端项目,然后登陆控制台查看,出现以下效果,说明改造成功:
注意事项:
官方前端并没有实现按照时间范围的查询搜索,需要自行实现
官方控制台实时监控默认查询的是最近一分钟的热点资源排行,见方法
listresourcesofapp
官方控制台实时监控右侧 table 默认查询的是最近五分钟的热点访问详情,见方法
querytopresourcemetric
小结
对于官方五分钟的阉割版,时序数据库实现的流控数据存储,对于生产环境还是很有帮助的,比如实时数据分析,热点资源、监控预警等等。小伙伴们还可以根据实际生产需求结合chronograf
、grafana
做出更炫酷的大屏监控。
源码
参考
https://hub.docker.com/_/chronograf
https://github.com/influxdata/influxdb-java
https://github.com/influxdata/influxdb-python
https://help.aliyun.com/document_detail/97578.htm