声纳系统是一个集数据采集、存储、展示、报警为一体的解决方案,本文将从实际案例切入,重点介绍声纳系统在springboot项目中的应用、我们的设计方案和系统架构、声纳系统未来的规划。内容涵盖时序数据库及相关概念的说明、时序数据库选型(重点介绍选型过程中对graphite和influxdb的综合对比)、部署架构如何实现可扩展和高可用、采集客户端设计中如何兼顾性能及稳定性并对springboot 1.x及2.x版本兼容、时序数据未来深度挖掘价值的设想等。
进入微服务时代后,单体应用被切分成很多微服务,应用程序的监控是微服务中很重要的一环,实时掌握应用程序运行情况也成为每个开发人员迫切的需求。我们使用springcloud作为微服务框架,Spring Boot Actuator提供了一套完备的监控方案用来监控Spring Boot应用,包括堆栈信息,系统信息、线程信息、GC信息等,但actuator并没有持久化机制,无法对比历史数据并图像化展示,因此我们引入时序数据库,存储actuator采集的监控指标数据。
随着鹰眼系统(日志采集、报警、分析系统)大量接入,鹰眼采集了大量event类型日志,例如web日志、程序日志、慢日志等。记录某个时刻发生了什么事情是鹰眼系统擅长的,比如凌晨1点xx系统发生空指针错误,但是统计xx系统每小时发生多少次空指针错误,这种metric类型日志更适合使用时序数据库,即记录某个时刻数据的测量值,例如pv/uv,代码质量数据等。时序数据库相比鹰眼通过明细统计数据,时序数据查询更简单,数据存储成本更低,可以保留更长时间,因此引入时序数据作为鹰眼系统的有益补充。
业务类数据存储,例如订单量统计,用于分析订单量的变化,同比环比异常等,帮助我们掌握订单量等业务数据的变化规律和趋势,这是鹰眼系统event类型日志无法替代的。
声纳系统目前有许多的应用场景,下图Dashboard为声纳系统采集的springboot项目系统运行指标,截止目前已发现多次数据库连接不释放、数据访问热点等问题。
首屏默认展开的为日常关注度较高的数据,以帮助我们快速的了解应用基本运行情况,例如系统负载、线程数、QPS、GC等,更详细的信息可展开对应子面板深入分析。
堆内存详细信息如下图:
针对声纳系统数据的同比环比报警,帮助业务方发现访问热点、业务数据(订单数等)趋势异常等:
为满足数据同比环比展示需求,开发并开源了grafana同环比插件)。
为更好的理解声纳系统,下面针对声纳系统用到的时序数据库相关概念、采集协议相关概念进行简单的介绍。
时序数据(Time Series Data)
基于时间序列的数据,用于描述一类数据在历史的时间维度上的变化信息。基本数据结构为:度量、标签、指标值、时间戳。例如:北京市每分钟温度数据,股票价格曲线数据等。
度量(Metric)
监测数据的指标,例如温度。
标签(Tag)
度量(Metric)的标签,数据的维度,代表数据的分类,一般为索引列,通常做为数据检索的条件,例如“城市(TagKey)= 北京(TagValue)”
指标值(Field)
采集数据的值,例如“温度(FieldKey)= 21℃(FieldValue)”
时间戳(Timestamp)
度量数据产生的时间点。
statsd
一个守护程序,根据指定的协议收集客户端发送来的数据,固定时间间隔内数据聚合之后,定时推送后端,
statsd四种数据类型:gauge、counter、timing、set。
gauge
记录统计时间维度内最新值,如果数值前加+/-符号,会与前一个值累加或扣减,若无符号则覆盖前一个值,每个时间维度数值会被重置,以最后一个值作为该时间维度的最终值。
counter
计数功能。把统计时间维度内上报的值累加,值可以是正数或者负数,每个时间维度数值会被重置。
timing
记录统计时间维度内数据平均值(mean)、最大值(upper)、最小值(lower)、累加值(sum)、平方和(sum_squares)、个数(count)以及部分TP值。
set
记录统计时间维度内不重复值的数量。
声纳系统在技术选型过程中基本要求:
数据写入性能
时序数据应用场景绝大部分是写入,目前每天写入的数据大约2亿条。随着时间的推移,接入的指标会越来越多,写入量也会越来越大,这就要求我们选型能够支撑高并发、高吞吐写入以及数据写入的可靠性。
高可扩展能力
一方面随着数据的不断增长,当达到单机瓶颈时,存储要支持纵向或横向扩展能力,另一方面随着接入方的不断增加,代理层要支持扩展以满足更高的并发量和吞吐量。
高可用
数据采集高可用,尤其是业务数据,需要支持断线重连、缓存队列、失败重发、同步异步处理等机制。
多采集协议支持
目前statsd是事实上的标准,很多项目都提供了statsd协议的支持,如consul等。除此外,最新的业内倾向是将时序数据中的tag等作为标配,业务上也有此类需求,所以纯时序数据的协议也是一个趋势,如influxdb协议、graphite协议等。
多分发协议支持
公司目前lvs负载暂时只支持tcp,采集客户端需要支持有tcp协议、http协议等。
结合上面对声纳系统的基本要求,我们技术选型主要分为数据库选型、部署架构选型和采集客户端选型。下图为声纳系统的部署架构,statsRelay负责statsd协议数据转发,将相同指标发送到同一telegraf节点,telegraf负责协议转化,influx-proxy负责数据的分片和双写,influxDB负责数据存储,接下来会针对每个组件选型进行详细介绍。
首先了解下最新的时序数据库排名,(https://db-engines.com/en/ranking/time+series+dbms )下图为2019年1月数据:
时序数据具有以下特点:
写多读少。
写入为主,几乎无更新。
并发量大,数据写入量平稳,大部分情况下以固定时间频率写入。
近期数据查询频率高,时间精度要求较高,时间范围跨度越大要求的时间精度越低。
数据量大。
针对时序数据特点,我们在调研时序数据库的过程中重点关注写性能、扩展性、可靠性,其次是读性能、生态丰富度、学习曲线、开闭源、DB排名等。前期部门内graphite小范围在用,主要采集consul相关指标。结合DB排名,influxDB在时序数据库排名中长期位列第一,本次调研主要针对graphite和influxDB,下表为直观的对比。
influxdb中数据查询及数据结构:
通过对比测试,虽然influxdb没有很好的水平扩展机制,但是时序数据本身不要求存储时间太长,且相同数量指标测试存储成本约占graphite的1/11。当graphite需要扩展的时候,influxdb依然可以运行的很好。influxdb多tag多field的特性、类SQL的语法让它更灵活且更容易上手,因此我们最后决定选择influxdb作为数据库。
statsd主要作用是汇总和聚合应用指标,我们的服务组件中consul、consul-template、springboot等都使用Statsd作为指标统计汇总服务端。statsd使用场景中主要跟graphite结合,statsd输出端只有console、graphite和repeator,因此我们的部署架构中需要寻找statsd替代方案支持将数据写入influxdb。除此之外,部署结构需要支持复制和分片,以便后期能够方便的进行扩容。
问题1:statsd作为重要采集数据的工具,如何兼容?
statsd在我们的应用中也必不可少,但其输出端并不能直接接入influxdb。针对这一问题我们对statsd替代方案进行调研,主要针对telegraf、statsite。通过对比我们发现telegraf更加灵活,statsite虽然set类型数据统计(近似计算)更高效,资源占用更少,但是兼容influxdb还需要对statsite进行不少的改造。综合考虑选定telegraf作为statsd的替换方案。telegraf作为influxdb生态中重要的一员,后期的发展值得期待。
问题2:确定telegraf作为statsd的替代方案后,作为服务端聚合工具,在多telegraf节点的情况下,如何对指标数据进行分发?
statsd自带statsd Cluster Proxy组件,支持分发,但是仅支持udp方式,由于公司暂不支持udp负载均衡,statsd Cluster Proxy被放弃。Uber开源的statsrelay解决了这一问题,statsrelay支持udp和tcp,使用一致性哈希将同一指标发送到相同的telegraf实例,确保聚合的正确性。
缺陷 无法自动增减负载
statsrelay每个输出后端都有自己的发送队列,支持失败重试,如果发送队列的大小达到最大值,则会删除新传入的数据,直到后端连接成功并开始消费队列。若输出后端telegraf长时间宕机,发送到该节点的数据将一直被丢失。这一问题通过服务注册发现工具consul解决,telegraf启动后注册到consul,consul定时检查telegraf健康状态,当连续30s(可配置)检查telegraf状态为宕机后注销该telegraf节点服务,statsrelay通过与Consul-Template动态生成负载配置,摘除负载,同理当发现telegraf服务上线后增加负载。
问题3:如何实现复制、分片,后期如何实现扩容
influxdb官方提供了influx-relay解决方案,但influx-relay仅支持udp和http,且仅支持写入功能,对查询无法支持,这无疑增大了使用和后期维护的难度。
influxdb-proxy提供了解决方案,influxdb-proxy同时支持写和查询代理功能,支持多写及表级别的分片,支持失败重试、批量提交,写入失败时写入文件,后端恢复时重新写入,同时限制部分查询命令和全部删除操作,避免了用户误操作。
关于扩容的问题,前期可配置influxdb-proxy写入代理同时写入新旧分片,influxdb-proxy查询代理按照扩容前映射规则查询数据,在新分片数据达到业务留存时间后不再写入原分片,influxdb-proxy查询代理更新至最新映射规则。虽然扩容过程中会增加一定的存储成本,但扩容对用户来说平滑无感知,且操作简单。
为方便开发人员接入声纳系统,更简单快捷的采集springboot应用监控类数据、自定义的指标,例如订单量数据等,且受限于公司暂时不支持udp方式负载均衡,需要自研或调研一款支持tcp的采集客户端。
困难1:采集客户端性能、稳定性、高可用如何保证?
因公司暂未支持udp的负载均衡,调研市面上的采集客户端仅支持udp方式,如何实现tcp发送?自研采集客户端能否保证性能和稳定性?是否能够复用现有成熟的插件?我们发现无论statsd、graphite、influxdb他们的协议都是纯文本,可以复用log4j(.net项目使用nlog)的Socket Appender实现tcp发送,只需要像写日志一样调用info方法即可实现指标的tcp发送。log4j作为最常见的日志工具,它的性能、稳定性毋庸置疑,省掉了自研需要考虑的同步异步处理机制、消息缓冲队列、失败重发、断线重连等等一系列问题。tcp发送问题解决了,自研客户端只需要关注协议层,这让工作变的简单多了。目前支持graphite、statsd、influxdb三种协议,当然我们也对采集客户端进行了性能损耗测试和稳定性测试。
困难2:兼容springboot1.x和springboot2.x
在最初阶段使用springboot1.x原生采集器发送,在试运行阶段发现采集时钟不稳定。设置的10秒间隔,经常出现间隔11秒、12秒甚至更长间隔才发送一次数据,导致监控数据点缺失。springboot1.x本身的缺陷让我们决定找一款更稳定的采集器,springboot2中对actuator进行了重点改造,并将micrometer作为springboot项目新的指标采集器。micrometer除了支持springboot2外,对springboot1也提供了支持,经过测试原actutor中存在的问题在micrometer中是正常的。考虑到后续springboot版本升级,我们最终选择了micrometer,而没有用原生的actutor实现。
目前部门内springboot项目运行信息、solr监控信息、consul集群监控、声纳系统集群监控、业务订单量等数据已接入声纳系统。
目前数据应用场景:
基于同环比报警,例如pv同昨日比较变化率超过20%报警。
基于阈值的报警,例如堆栈使用率大于80%报警。
基于数据值报警,例如进程状态为0时(1为正常)报警。
如何深度挖掘数据价值?
目前基于声纳系统的报警规则还比较简单,节假日、线上活动、业务周期性等影响因素并没有考虑进去,复杂调用链路中报警连锁反应,未精准的发送报警人,后期希望能够引入AI,构建数据驱动闭环,在以下几点进行改进:
报警
基于统计学的基线设置,借助AI实现自我学习,多维度关联分析,阈值的动态调整。
根因分析及报警收敛
根据监控指标快速确认影响范围、异常根源,此外可与运维系统打通,获取近期相关项目、服务器等变更事件,例如刚刚上线代码变更、运营活动、服务器宕机等等,缩小故障判定范围,报警精准推送并为报警人根源定位提供依据。
智能决策
机器智能化分析决策,自动化执行脚本,实现故障自愈。
预测
构建丰富的预测模型,根据历史趋势、周期规律等,对未来做出预测分析,为决策提供依据。
参考文章:
https://gitbook.cn/books/59395d3d5863cf478e6b50ba/index.html )
graphite集群扩容方案探究(http://www.opscoder.info/graphite_cluster_scale.html )
Clustering Graphite(http://bitprophet.org/blog/2013/03/07/graphite )
uber statsrelay+statsite(https://github.com/uber/statsrelay/issues/56 )
Micrometer Application Monitoring For Springboot 1.x(http://micrometer.io/docs/ref/spring/1.5 )
Telegraf is the Agent for Collecting and Reporting Metrics & Data(https://www.influxdata.com/time-series-platform/telegraf )