本文根据演讲视频以及PPT整理而成。
本文将主要围绕以下三个方面进行分享:
- 构建背景
- APM的构建过程
- 未来展望
一、构建背景
二维火公司的整体架构体系分为三个阶段,即从单机到面向服务化,最后到面向微服务的架构。因此,监控平台所需要监控的也是上文所提及的这三个阶段,即从单机到分布式的指标日志,最后到APM。在单机时平台往往是靠用户对故障进行反馈的,在接到反馈后相关技术人员手动登陆服务器,人工输入指令,对问题进行定位,不但会导致故障的时延非常长,而且对开发人员的技术要求也很高。但随着公司的业务发展,公司的整体架构进行升级后,对故障的容忍度进一步的降低,此时,便需要一些集中化的方法去管理相关指标和日志。二维火公司分布式指标的实现方法如下,首先会在每台机器上安装falcon-agent,来对所需要的指标,如网络指标等进行采集,然后将这些指标传输到transfer,transfer把这些指标发送到自带时序数据库的同时,会转发一份到InfluxDB,最后通过InfluxDB在Grafana里进行视图配置,以及一些相关操作如同比、环比操作。对于日志,则使用ELK架构,通过把相关的日志信息如应用日志,nginx日志,Mysql日志等保存在指定的文件中,根据系统环境的不同,在VM下通过rsyslog,在Docker下通过log-pilot把日志传输到Kafka,然后通过logstash对不同的日志进行解析处理,并发送到不同的ES集群,最后通过kibana进行视图的相应配置。在二维火,ES的作用不仅仅是全文搜索,还有数据分析,比如得到nginx的访问趋势,还有一些核心应用的指标趋势等。
但随着公司的发展,这一套架构已经不能够完全满足公司的业务需求了。事实上,大家经常会遇到这样的问题,底层服务挂掉后,往往需要很长的时间来进行问题的定位。随着微服务化的发展,一些架构也需要随之升级,但在升级时会遇到很多问题需要解决,例如如何确认优化之后的架构是不是合理的,调用是不是合理的,如何提供一个有效的衡量指标,如何梳理复杂的依赖,来快速的进行问题定位,又比如作为提供方,哪些服务调用了我们的应用,调用的情况是什么样的,以此方便进行熔断,限流等操作,这些都是在架构升级时需要考虑的问题。
二、APM的构建过程
二维火公司是从2017年开始研发APM平台的,当时业界有一些开源的产品,比如Zipkin,Pinpoint,Skywalking等。虽然使用这些开源软件可以很快的进行平台的搭建,但是经过调研后发现这些开源软件的功能比较单一,并不能完全满足相应的业务需求,同时扩展性较低,所以二维火公司选择了对APM平台进行自研搭建。进行自研平台的搭建虽然需要投入专员进行开发维护,但对于APM平台是可以完全掌控的,能够很好的和现有的技术体系进行集成,并且可以很好的支持自定义扩展。其实上述所说的自研并不是从零开始,也有很多现成的方案进行参考,包括谷歌,阿里等都有相应的案例分享。对于APM来说,主要分为以下几个步骤,第一个是数据的埋点,第二个是数据的采集,第三个是数据的处理,第四个是数据的存储,第五个是数据的展现,关于数据展现需要根据不同公司实际的业务需求进行处理,同时数据埋点目前有许多案例可以进行参考,便不过多叙述。接下来将主要围绕数据的采集,处理和存储进行更加细致的介绍。
(1)数据采集
首先介绍的是数据采集,我们会在各个中间件的拦截层将链路的信息进行采集,如果一个组件没有拦截层的话,则会通过静态代理的方式对埋点进行注入。当采集到链路信息后,会进行采样和下层的RingBuffer未满的判断,如果都满足,此时会在业务线程里使用Protostuff进行序列化操作并发送到RingBuffer,最后通过异步线程批量的将数据发送到Kafka中。在数据采集过程中,需要注意的是采集数据要尽可能的减少应用的性能损耗,降低主机的资源消耗,因此使用ProtoStuff进行序列化操作,ProtoStuff相比于其他方法,序列化后的数据量更小,同时在业务线程里进行序列化后,可以进一步降低之后异步线程的CPU占用时间。但是这种方案也会存在一些问题,比如,采样或者RingBuffer满时,数据会丢失,同时指标计算不够准确,并且缺少应用指标,如连接池、线程池等,因此需要对数据的采集过程进行相应的改进。改进后的数据采集过程增加了jvm,redis、Mysql连接池,dubbo线程池等指标信息的收集并发送到Kafka,同时在客户端进行预计算,把每个接口的统计信息,比如请求数,平均响应时间等先行进行计算,因此在采样或者RingBuffer满时,指标数据也能保证不会丢失。对于数据采集来说,最为关键的是要尽可能的对重要数据进行完整的采集,同时降低对应用的损耗,在APM中最重要的两种数据分别是链路数据和指标数据,因此下文主要介绍这两块数据的处理和存储。
(2)数据处理
在数据获取后,便要进行数据的分析和处理。目前业界常用的方法是流计算,而在二维火平台中则是使用flink进行处理的,一方面,flink的核心代码很多是Java,能比较快速的掌握,另一方面,flink有以下几个优点,一是优秀的窗口机制,能够允许一定时间的乱序,二是api非常简洁,三是具有完善的监控,可以很快的对性能瓶颈进行定位,四是提交方式简便,flink提供web界面进行任务提交,同时flink还具有容错性强,社区活跃性好,及时性高等优点。
首先是对于链路数据的处理流程,从Kafka获取到数据后,如果数据量不大,则直接通过flink把数据写入存储中,但数据量很大的话,如部分行业的高峰期,便会进行采样控制,通过对traceId进行哈希取模后再写入存储中,但高优先级或异常数据一定会写入存储中。
其次是统计数据的计算,首先启动一个job,消费原始的链路数据,因为数据量会非常大,如果直接将数据发送到下一个任务进行窗口计算,这时任务间的网络传输量会非常大,因为flink中用于network的内存是有限的,大数据量的传输就会演变成为瓶颈。事实上,对于原始的链路数据来说,每一批数据大部分都是对同一个接口的调用,所以在数据转发到窗口之前,首先要进行预计算,通过把属于同一个接口的统计信息提前计算出来,以此来减少任务之间的网络数据传输,之后进行细粒度化的的keyby操作,来减少热点问题。在这之后以一分钟的窗口计算低维的统计数据,如某个应用其中一台机器下dubbo提供者的接口,计算出接口的请求总数、失败数、平均响应时间、tps、在各个响应时间区间的数量等,将低维的统计数据写入TSDB的同时,同时发送一份数据到Kafka,这样便可以通过新的job进行更大窗口的计算或者高维的聚合。在二维火,目前flink每天处理几十TB的数据,主要用于数据转发,依赖分析,实时指标计算,预聚合,SQL统计,组件异常警告等方面,可以平稳无中断运行数月。
(3)数据存储
对于数据存储,主要分为链路数据的存储和指标数据的存储,对于链路数据存储,我们存储在hbase,需要通过hbase还原整个调用链路,通用的做法是将同一次调用的所有数据存储到hbase的同一行,然后使用traceId作为rowKey,通过traceId还原整个调用过程。但在spanId是随机数的情况下,通过traceId查询出来的数据,调用顺序是无法保证的,所以增加了一个parentId,parentId是上层链路的spanId。通过使用traceId+parentId作为rowKey的方法,便可以一层层还原整个链路的调用过程,但是这种方法只能通过RowKey进行链路的查询,因此需要对存储方式进行改进,即将查询条件放入elasticsearch中,并关联rowKey。所以在通过flink进行写入数据时,首先把链路数据写入Hbase中,并把一些查询条件写入ES中,此时在前端进行查询时,既可以通过RowKey查询,也可以通过条件进行查询。
接下来是指标数据的存储,考虑到随着数据量的增大,单机无法满足全部的业务需求,因此选择分布式时序数据库Aliyun TSDB。Aliyun TSDB具有很多优点,以二维火公司为例,监控团队规模不大,没有太多人力来做时序数据库的运维,而Aliyun TSDB可以做到零运维,同时Aliyun TSDB有着极佳的写入性能,可以支持秒级的数据写入。Aliyun TSDB还具有永久储存,时间线无限制,多值写入等其他优点,可以满足二维火平台绝大部分的业务需求。目前二维火监控平台有近400个应用的链路统计信息以及部分的主机信息,在写入Aliyun TSDB时没有产生过反压问题,对于低维查询可以做到毫秒级返回,但和其他时序数据库一样,仍然会有一些小问题产生,比如在高维查询或者时间线过多的时候响应不够理想,不支持topN等,这时便需要结合使用一些其他方法,例如对数据进行预聚合,使用redis进行处理等。
4)整体架构
目前,二维火监控平台整体的前期架构是在应用层采集到链路数据和指标数据,将数据发送到Kafka,通过flink对数据进行分析,分析后对数据进行存储,最后在前端通过UI视图对数据进行展现。经过一段时间的功能迭代后,目前的总体架构中,一方面增加了移动端的相关数据信息,会把移动端的监控数据进行采集后一并发送到Kafka,同时也增加了拨测功能,另一方面将日志处理模块迁移到flink中。
三、未来展望
二维火监控平台对于未来的功能升级主要集中在以下几个方面,首先是进一步完善移动端监控以及拨测信息,其次是实时告警,目前告警的时延超过一分钟,希望未来能够在一分钟内进行告警,同时会进行AiOps的开发,如自动化的故障检测和实时的告警收敛,最后将会探索平台与Service Mesh的结合。