Spark Streaming + ES构建美团App异常监控平台

作者:秦思源,美团·大众点评高级数据工程师。2014年加入公司,负责美团用户增长数据相关工作,有较多Spark、BI实践经验,现专注于用户增长、数据开发效率。王彬,南京大学研究生,2015年加入美团·大众点评 用户增长团队。负责搜索引擎营销、流量数据挖掘、报表数据开发等工作,现专注于Spark、ES。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2016年《程序员》

如果在使用App时遇到闪退,你可能会选择卸载App、到应用商店怒斥开发者等方式来表达不满。但App开发者也同样感到头疼,因为App Crash(崩溃)可能意味着:用户流失、营收下滑。为了降低崩溃率,进而提升App质量,App开发团队需要实时地监控App异常。一旦发现严重问题,及时进行热修复,从而把损失降到最低。App异常监控平台,就是将这个方法服务化。

本篇以核心需求为中心,逐一展开介绍,如何使用Spark Streaming+ES构建App异常监控平台。

低成本

小型创业团队一般会选择第三方平台提供的异常监控服务。但中型以上规模的团队,往往会因为不想把核心数据共享给第三方平台,而选择独立开发。造轮子,首先要考虑的就是成本问题。我们选择了站在开源巨人的肩膀上,如图1所示。

图片描述
图1 数据流向示意图

Spark Streaming

每天来自客户端和服务器的大量异常信息,会源源不断的上报到异常平台的Kafka中,因此我们面临的是一个大规模流式数据处理问题。

美团数据平台提供了Strom和Spark Streaming两种流式计算解决方案。我们主要考虑到团队之前在Spark批处理方面有较多积累,使用Spark Streaming成本较低,就选择了Spark Streaming。

Elasticsearch

ES的全称是Elasticsearch,是一个开源搜索引擎。不过在监控平台中,我们是当做“数据库”来使用的。为了降低展示层的接入成本,我们还使用了另一个开源项目ES SQL提供类SQL查询。

ES的运维成本,相对SQL on HBase方案也要低很多。整个项目开发只用了不到700行代码,开发维护成本还是非常低的。那如此“简单”的系统,可用性可以保证吗?

高可用

Spark Streaming + Kafka的组合,提供了“Exactly Once”保证:异常数据经过流式处理后,保证结果数据中(注:并不能保证处理过程中),每条异常最多出现一次,且最少出现一次。

保证Exactly Once是实现24/7的高可用服务,最困难的地方。在实际生产中会出现很多情况,对Exactly Once的保证提出挑战:

异常重启

Spark提供了Checkpoint功能,可以让程序再次启动时,从上一次异常退出的位置,重新开始计算。这就保证了即使发生异常情况,也可以实现每条数据至少写一次HDFS。再覆写相同的HDFS文件就保证了Exactly Once(注:并不是所有业务场景都允许覆写)。写ES的结果也一样可以保证Exactly Once。你可以把ES的索引,就当成HDFS文件一样来用:新建、删除、移动、覆写。

作为一个24/7运行的程序,在实际生产中,异常是很常见的,需要有这样的容错机制。但是否遇到所有异常,都要立刻挂掉再重启呢?显然不是,甚至在一些场景下,你即使重启了,还是会继续挂掉。我们的解决思路是:尽可能的把异常包住,让异常发生时,暂时不影响服务。

图片描述
图2 异常重启架构图

如图2所示,包住异常,并不意味可以忽略它,必须把异常收集到Spark Driver端,接入监控(报警)系统,人工判断问题的严重性,确定修复的优先级。为了更好的掌控Spark Streaming服务的状态,我们还单独开发了一个作业调度(重启)工具。

美团数据平台安全认证的有效期是7天,一般离线的批处理作业很少会运行超过7天,但Spark Streaming作业就不同了,它需要一直保持运行,所以Spark Streaming作业只要超过7天就会出现异常。因为没有找到优雅的解决方案,只好粗暴的利用调度工具,每周重启刷新安全认证,来保证服务的稳定。

升级重导

Spark提供了2种读取Kafka的模式:“Receiver-based Approach”、“Direct Approach”。使用Receiver模式,在极端情况下会出现Receiver OOM问题。使用Direct模式可以避免这个问题。我们使用的就是这种Low-level模式,但它在一些情况下需要我们自己维护Kafka Offset:

升级代码。开启Checkpoint后,你如果想改动代码,需要清空之前的Checkpoint目录后再启动,否则改动可能不会生效。但当你这样做了之后,就会发现另一个问题:程序“忘记”上次读到了哪个位置,因为存储在Checkpoint中的Offset信息也一同被清空了。这种情况下,你就需要自己用Zookeeper维护Kafka的Offset。

重导数据。重导数据的场景也是,当你希望从之前的某一个时间点开始重新开始计算的时候,显然也需要自己维护时间和Offset的映射关系。

自己维护Offset的成本并不高,所以看起来Checkpoint功能很鸡肋。其实可以有一些特殊用法的,例如:因为Python不需要编译,所以你如果使用的是Pyspark,可以把主要业务逻辑写在提交脚本的外边,再使用Import调用。这样升级主要业务逻辑代码时,你只要重启一下程序即可。网上有不少团队分享过升级代码的“黑科技”,这里不再展开。

实现24/7监控服务,我们不仅要解决纯稳定性问题,还要解决延迟问题。

低延迟

App异常监控,需要保证数据延迟在分钟级。虽然Spark Streaming有着强大的分布式计算能力,但要满足用户角度的低延迟,可不是单纯的能计算完这么简单。

输入问题

iOS App发生Crash的时候,会生成一个Crash Log,但Crash Log是一堆十六进制的内存地址,对开发者来说就是“天书”。只有经过“符号化”的Crash Log,开发者才能看懂。我们将数据源分为符号化数据流、未符号化数据流,可以看出两个数据流的相对延迟时间。因为符号化需要在Mac环境下进行,而我们的Mac集群资源有限,不能符号化全部Crash Log。

即使我们做了去重等优化,但符号化后的数据流还是有延迟。每条异常信息中,包含N维数据,如果不做符号化只能拿到其中的M维。

图片描述
图3 双数据流融合示意图

如图3所示,我们将数据源分为符号化数据流、未符号化数据流,可以看出两个数据流的相对延迟时间T是较稳定的。如果直接使用符号化后的数据流,那么全部N维数据都会延迟时间T。

为了降低用户角度的延迟,我们根据经验T加大了时间窗口:先存储未符号化的M维数据,等到拿到对应的符号化数据后,再覆写全部N维数据,这样就只有N-M维数据延迟时间T了。

输出问题

如果你的Spark Streaming计算结果只是写入HDFS,很难遇到什么性能问题。但你如果想写入ES,问题就来了。因为ES的写入速度大概是每秒1万行,只靠增加Spark Streaming的计算能力,很难突破这个瓶颈。

异常数据源的特点是数据量的波峰波谷相差巨大。由于我们使用了Direct模式,不会因为数据量暴涨而挂掉,但这样的“稳定”从用户角度看没有任何意义:短时间内,数据延迟会越来越大,暴增后新出现的异常无法及时报出来。

为了解决这个问题,我们制定了一套服务降级方案。

图片描述
图4 服务降级方案示意图

如图4所示,我们根据写ES的实际瓶颈K,对每个周期处理的全部数据N使用水塘抽样(比例 K/N),保证始终不超过写ES的瓶颈K。并在空闲时刻使用Spark批处理,将N-K部分从HDFS补写到ES。

既然写ES这么慢,那我们为什么还要用ES呢?

高性能

App开发者需要在监控平台上分析异常。实际分析场景可以抽象描述为:“实时/秒级/明细 /聚合” 数据查询。

我们团队在使用的OLAP解决方案可以分为4种,它们各有各的优势:

  • SQL on HBase方案,例如:Phoenix、Kylin。我们团队从15年Q1开始,陆续在 SEM、SEO生产环境中使用Phoenix、Kylin至今。Phoenix算是一个“全能选手”,但更适合业务模式较固定的场景;Kylin是一个很不错的OLAP产品,但它的问题是不能很好的支持:实时查询、明细查询,因为它需要离线预聚合。另外,基于其他Nosql的方案,基本大同小异,如果选择HBase,建议团队在HBase运维方面有一定积累。
  • SQL on HDFS方案,例如:Presto、Spark SQL。这两个产品,因为只能做到亚秒级查询,我们平时多用在数据挖掘的场景中。
  • 时序数据库方案,例如:Druid、OpenTSDB。OpenTSDB是我们旧版App异常监控系统使用过的方案,时序数据库更适合做系统指标监控。
  • 搜索引擎方案,代表项目有:Elasticsearch。相对上面的3种方案,基于倒排索引的ES非常适合异常分析的场景,可以满足:实时、秒级、明细、聚合,全部4种需求。

ES在实际使用中的表现如何呢?

明细查询

支持明显查询,算是ES的主要特色,但因为是基于倒排索引的,明细查询的结果最多只能取到10000条。在异常分析中,使用明细查询的场景,其实就是追查异常Case,根据条件返回前100条就能满足需求了。例如:已知某设备出现了Crash,直接搜索这个设备的DeviceId就可以看到这个设备最近的异常数据。

我们在生产环境中做到了95%的明细查询场景1秒内返回。

聚合查询

面对爆炸的异常信息,一味追求全是不现实,也是没必要的。App开发者,需要能快速发现关键问题。因此平台需要支持多维度聚合查询,例如:按模块、版本、机型、城市等分类聚合,如图5所示。

图片描述
图5 聚合查询页面截图

不用做优化,ES聚合查询的性能就已经可以满足需求。因此,我们只做了一些小的使用改进,例如:很多异常数据在各个维度的值都是相同的,做预聚合可以提高一些场景下的查询速度。App开发者更关心最近48小时发生的异常,分离冷热数据,自动清理历史数据也有助于提升性能。最终在生产环境中,做到了90%的聚合查询场景1秒内返回。

可扩展

异常平台不止要监控App Crash,还要监控服务端的异常、性能等。不同业务的数据维度是不同的,相同业务的数据维度也会不断的变化,如果每次新增业务或维度都需要修改代码,那整套系统的升级维护成本就会很高。

维度

为了增强平台的可扩展性,我们做了全平台联动的动态维度扩展:如果App开发人员在日志中新增了一个“城市”维度,那么他不需要联系监控平台做项目排期,立刻就可以在平台中查询“城市”维度的聚合数据。

只需要制定好数据收集、数据处理、数据展示之间的交互协议,做到动态维度扩展就很轻松了。需要注意的是,ES中需要聚合的维度,Index要设置为“not_analyzed”。想要支持动态字段扩展,还要使用动态模板,样例如代码1所示:

{
    "mappings": {
        "es_type_name": {
            "dynamic_templates": [
                {
                    "template_1": {
                        "match": "*log*",
                        "match_mapping_type": "string",
                        "mapping": { "type": "string" } }
                },
                {
                    "template_2": {
                        "match": "*",
                        "match_mapping_type": "string",
                        "mapping": { "type": "string", "index": "not_analyzed" } }
                }
            ]
        }
    }
}

代码1 ES索引配置

资源

美团的数据平台提供了Kafka、Spark、ES的集群,整套技术栈在资源上也是分布式可扩展的。线上集群使用的版本:

  • kafka-0.8.2.0
  • spark-1.5.2
  • elasticsearch-2.1.1

图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值