本文主要介绍腾讯SNG在开发全链路日志监控平台过程中遇到的挑战和解决方案。背景全链路日志监控,可以有效提高当前微服务和分布式环境下问题定位和分析的效率,成为开发和运维的利器。目前已有开源方案和成熟厂商,如Twitter的zipkin,基于Google的Dapper论文设计开发了分布式跟踪系统,用于收集处理节点间的日志和耗时信息,帮助用户排查请求链路问题异常链接。统一的RPC中间件框架,业务部门接入zipkin更容易,但智云全链路日志监控平台(后来变成全链路)实际面临的业务场景更复杂。全链路日志监控的实现遇到了更多的挑战,全链路技术的选型也经历了从开源组件到自研组件的转变。目前智云全链路日志监控平台接入空间和视频云业务日志数据,日数据存储量10TB,压缩比1/10,峰值流量30GB/s。先分享一个案例场景:2017年8月31日21:40~21:50,X业务模块各项指标异常,成功率从99.988%下降到97.325%,如下图:接到成功率异常告警后,通过多维监控系统画像下钻发现,空间点播服务iPhone客户端成功率下降,返回码为-310110004。如下图所示:通过市场的多维度数据分析发现异常原因后,由于涉及到APP问题,还需要进一步分析用户异常的上下文。因此需要在异常时间点查看异常用户的全链路日志数据。在全链路视图下,可以展示满足异常条件的用户日志和操作流程。以上就是一个从面到点的异常分析案例。使用场景全链路日志监控的使用场景主要分为三类:案例分析,主要处理用户投诉和点对点的异常分析。开发调试,主要用于在开发过程中查看关联模块的日志,作为测试提单线索。监控告警主要是从日志数据中提取维度,统计为多维数据,用于异常检测和原因分析。遇到的挑战在打造智云全链路日志监控平台的过程中,智云的监控模块经历了从传统监控、质量统计到大数据多维度监控平台的转变。我们走过了大数据套件的陷阱,遇到了业务场景的挑战。业务多样性挑战QQ系统中业务丰富多样,如:手Q、空间、直播、视频点播、会员等。这些业务产生不同风格的日志格式,没有统一的RPC中间件框架。这种背景决定了系统需要支持灵活的日志格式和多种采集方式。海量数据挑战2亿多在线用户同时上报的状态数据,日存储容量超过10T,带宽超过30GB/s。需要稳定高效的数据处理,高性能低成本的数据存储服务。在使用开源组件完成原型开发后,我们逐渐遇到了性能瓶颈和稳定性挑战,促使我们通过自研逐步替代开源组件。应对挑战多样化日志的价值不仅提供查询检索,还可以用于统计分析和异常检测报警。为此,我们将日志数据规范化,分发到多维监控平台,复用监控平台已有能力。基于以往积累的监控平台开发经验,我们在设计智云全链路日志监控平台时取长补短,通过自研日志解决了开源存储组件遇到的成本、性能和稳定性瓶颈存储平台。智云全链路日志监控平台提供了4种数据格式的支持,分别是:delimiterregularparsingjsonformatapireportdelimiter,regularparsingandjsonformatfornon-intrusivedatacollection,灵活性好。但是服务器的日志解析性能较低,分隔符的数据解析只能达到4W/s的处理性能。api方式可以达到10W/s的处理性能。对于内部业务,我们推荐使用统一的日志组件,并嵌入api上报数据。系统的自动容灾和扩缩容对于海量日志监控系统的设计,首先要做到模块无状态化。例如系统的接入模块、分析模块、处理模块等不需要状态同步的模块,可以独立部署提供服务。但是对于这种无状态的业务模块,需要增加异常链接去除机制。即如果数据处理链路中间的一个节点出现异常,则该节点之后的其他节点提供的服务将失效,需要中断当前链路。去除异常链接的机制有很多,比如zkremove的心跳机制。为了避免依赖过多的组件,我们做了有状态的心跳机制。上游节点A每隔6s定时向下游节点B发送心跳检测请求。B在回复心跳请求时,携带了自己的服务可用性状态和链路状态。上游节点A从B的心跳带收到不可用状态后,如果A下游没有其他可用节点,则A的下游链路状态也设置为不可用状态,依次发送心跳状态,最后整个链接被自动禁用。有状态服务通常是存储服务,通过主备机制实现容灾。如果同时只允许一个master提供服务,可以利用zk的选举机制实现主备切换。实现系统自动容灾和扩缩容的第二步是通过路由机制实现名称服务和负载均衡。使用开源组件zookeeper可以快速实现名称服务功能,需要在服务端实现注册逻辑,在客户端实现路由重载逻辑。数据通道容灾我们采用两种机制:双写方式,对于数据质量要求高的监控数据,采用双写方式实现。这种方式要求后端有足够的资源来处理峰值请求,并提供低延迟和高效的数据处理能力。消息队列是针对日志数据使用具有数据容灾能力的消息队列来实现的。使用的选型方案有kafka和rabbitmq+mongodb。使用消息队列可以应对高吞吐量的日志数据,具有调峰作用。副作用是高峰期数据延迟大,不能满足实时监控告警的需要。消息队列的使用也需要注意避免消息积压导致的队列异常。比如在使用Kafka集群时,如果消息累计量超过磁盘容量,整个队列的吞吐量就会下降,影响数据质量。我们后来采用了rabbitmq+mongodb的方案:数据在接入层以10000条或累计30秒组成一个数据块,数据库随机写入多个mongodb实例组成的集群。然后将mongodb的ip和key写入rabbitmq。后端处理集群从rabbitmq获取到要消费的信息后,从对应的mongodb节点读取并删除数据。通过定时统计rabbitmq和mongodb的消息积压,如果超过阈值,会执行自动清理策略。查询日志存储方案采用自研方式实现,以应对高效率、低成本的查询。整个链路上报的数据按照用户ID或者请求ID为主键进行哈希分片。分片数据在缓存模块中累积1min或1M大小,然后写入文件服务器集群;文件写入集群后,将哈希值与文件路径的映射关系写入ElasticSearch。查询数据提供两种类型的能力:按主键查询。查询方式是计算querykey的hash值,从ES中获取文件路径,发送给query模块进行过滤搜索。非主键的关键字查找。根据业务场景,提供的查询策略是查询包含关键字的日志。该策略的出发点是平衡查询性能并避免检索全文。即第一次查询1000个文件,有查询结果则停止后续查询;如果没有返回查询结果,则递增搜索2,000个文件,直到查询到100,000个文件为止。为了满足各种业务场景,我们在数据处理模块中抽象出ETL能力,实现插件化扩展和可配置实现,提供统一的任务管理和集群管理能力。总结全链路日志监控的发展历程。以下经验可供借鉴:使用成熟的开源组件构建主要业务功能。在业务运行过程中,通过修改开源组件或自研,提高系统处理能力和稳定性,降低运营成本,提高运维效率。标准化无状态和路由负载平衡功能。抽象提炼功能模型,建立满足多样化业务需求的平台能力。
