关于数据库:饿了么EMonitor演进史

54次阅读

共计 17259 个字符,预计需要花费 44 分钟才能阅读完成。

简介:可观测性作为技术体系的外围环节之一,追随饿了么技术的飞速发展,一直自我变革。

序言

工夫回到 2008 年,还在上海交通大学上学的张旭豪、康嘉等人在上海开办了饿了么,从校园外卖场景登程,饿了么一步一步发展壮大,成为外卖行业的领头羊。2017 年 8 月饿了么并购百度外卖,强强合并,持续开疆扩土。2018 年饿了么退出阿里巴巴小家庭,与口碑交融成立阿里巴巴本地生存公司。“爱什么,来什么”,是饿了么对用户不变的承诺。

饿了么的技术也随同着业务的飞速增长也一直突飞猛进。据公开报道,2014 年 5 月的日订单量只有 10 万,但短短几个月之后就冲到了日订单百万,到当今日订单上千万单。在短短几年的技术倒退历程上,饿了么的技术体系、稳定性建设、技术文化建设等都有长足的倒退。各位可查看往期文章一探其中倒退历程,在此不再赘述:

•《饿了么技术往事(上)》
•《饿了么技术往事(中)》
•《饿了么技术往事(下)》

而可观测性作为技术体系的外围环节之一,也追随饿了么技术的飞速发展,一直自我变革,从“全链路可观测性 ETrace”扩大到“多活下的可观测性体系 ETrace”,倒退成目前“一站式可观测性平台 EMonitor”。

EMonitor 通过 5 年的屡次迭代,当初曾经建成了集指标数据、链路追踪、可视化面板、报警与剖析等多个可观测性畛域的平台化产品。EMonitor 每日解决约 1200T 的原始可观测性数据,笼罩饿了么绝大多数中间件,可观测超 5 万台机器实例,可观测性数据时延在 10 秒左右。面向饿了么上千研发人员,EMonitor 提供精准的报警服务和多样化的触达伎俩,同时运行约 2 万的报警规定。本文就细数饿了么可观测性的建设历程,回顾下“饿了么可观测性建设的那些年”。

1.0:混沌初开,万物衰亡

翻看代码提交记录,ETrace 我的项目的第一次提交在 2015 年 10 月 24 日。而 2015 年,正是饿了么倒退的第七个年头,也是饿了么业务、技术、人员开始蓬勃发展的年头。彼时,饿了么的可观测性零碎依赖 Zabbix、Statsd、Grafana 等传统的“轻量级”零碎。而“全链路可观测性”正是过后的微服务化技术改造、后端服务 Java 化等技术发展趋势下的必行之势。

咱们可观测性团队,在调研业界支流的全链路可观测性产品 – 包含驰名的开源全链路可观测性产品“CAT”后,汲取众家之所长,在两个多月的爆肝开发后,推出了初代 ETrace。咱们提供的 Java 版本 ETrace-Agent 随着新版的饿了么 SOA 框架“Pylon”在饿了么研发团队中的推广和遍及开来。ETrace-Agent 能主动收集利用的 SOA 调用信息、API 调用信息、慢申请、慢 SQL、异样信息、机器信息、依赖信息等。下图为 1.0 版本的 ETrace 页面截图。

在经验了半年的爆肝开发和各中间件兄弟团队的鼎力支持,咱们又开发了 Python 版本的 Agent,更能适应饿了么过后各语言百花齐放的技术体系。并且,通过和饿了么 DAL 组件、缓存组件、音讯组件的密切配合与埋点,用户的利用减少了多层次的访问信息,链路更加残缺,故障排查过程更加清晰。

整体架构体系

ETrace 整体架构如下图。通过 SDK 集成在用户利用中的 Agent 定期将 Trace 数据经 Thrift 协定发送到 Collector(Agent 本地不落日志),Collector 经初步过滤后将数据打包压缩发往 Kafka。Kafka 上游的 Consumer 生产这些 Trace 数据,一方面将数据写入 HBase+HDFS,一方面依据与各中间件约定好的埋点规定,将链路数据计算成指标存储到工夫序列数据库 – LinDB 中。在用户端,Console 服务提供 UI 及查问指标与链路数据的 API,供用户应用。

全链路可观测性的实现

所谓全链路可观测性,即每次业务申请中都有惟一的可能标记这次业务残缺的调用链路,咱们称这个 ID 为 RequestId。而每次链路上的调用关系,相似于树形构造,咱们将每个树节点上用惟一的 RpcId 标记。

如图,在入口利用 App1 上会新建一个随机 RequestId(一个相似 UUID 的 32 位字符串,再加上生成时的工夫戳)。因它为根节点,故 RpcId 为“1”。在后续的 RPC 调用中,RequestId 通过 SOA 框架的 Context 传递到下一节点中,且下一节点的层级加 1,变为形如“1.1”、“1.2”。如此重复,同一个 RequestId 的调用链就通过 RpcId 还原成一个调用树。

也能够看到,“全链路可观测性的实现”不仅依赖与 ETrace 零碎本身的实现,更依靠与公司整体中间件层面的反对。如在申请入口的 Gateway 层,能对每个申请生成“主动”新的 RequestId(或依据申请中特定的 Header 信息,复用 RequestId 与 RpcId);RPC 框架、Http 框架、Dal 层、Queue 层等都要反对在 Context 中传递 RequestId 与 RpcId。
ETrace Api 示例
在 Java 或 Python 中提供链路埋点的 API:
/*
记录一个调用链路
/
Transaction trasaction = Trace.newTransaction(String type, String name);
// business codes
transaction.complete();
/*
记录调用中的一个事件
/
Trace.logEvent(String type, String name, Map<String,String> tags, String status, String data)
/*
记录调用中的一个异样
/
Trace.logError(String msg, Exception e)
Consumer 的设计细节
Consumer 组件的外围工作就是将链路数据写入存储。次要思路是以 RequestId+RpcId 作为主键,对应的 Data 数据写入存储的 Payload。再思考到可观测性场景是写多读少,并且多为文本类型的 Data 数据可批量压缩打包存储,因而咱们设计了基于 HDFS+HBase 的两层索引机制。

如图,Consumer 将 Collector 已压缩好的 Trace 数据先写入 HDFS,并记录写入的文件 Path 与写入的 Offset,第二步将这些“索引信息”再写入 HBase。特地的,构建 HBase 的 Rowkey 时,基于 ReqeustId 的 Hashcode 和 HBase Table 的 Region 数量配置,来生成两个 Byte 长度的 ShardId 字段作为 Rowkey 前缀,防止了某些固定 RequestId 格局可能造成的写入热点问题。(因 RequestId 在各调用源头生成,如利用本身、Nginx、饿了么网关层等。可能某利用谬误设置成以其 AppId 为前缀 RequestId,若没有 ShardId 来打散,则它所有 RequestId 都将落到同一个 HBase Region Server 上。)

在查问时,依据 RequestId + RpcId 作为查问条件,顺次去 HBase、HDFS 查问原始数据,便能找到某次具体的调用链路数据。但有的需要场景是,只晓得源头的 RequestId 须要查看整条链路的信息,心愿只排查链路中状态异样的或某些指定 RPC 调用的数据。因而,咱们在 HBbase 的 Column Value 上还额定写了 RPCInfo 的信息,来记录单次调用的简要信息。如:调用状态、耗时、上下游利用名等。
此外,饿了么的场景下,研发团队多以订单号、运单号作为排障的输出,因而咱们和业务相干团队约定非凡的埋点规定 – 在 Transaction 上记录一个非凡的 ”orderId={理论订单号}” 的 Tag– 便会在 HBase 中新写一条“订单表”的记录。该表的设计也不简单,Rowkey 由 ShardId 与订单号组成,Columne Value 局部由对应的 RequestId+RpcId 及订单根本信息(相似上文的 RPCInfo)三局部组成。
如此,从业务链路到全链路信息到具体单个链路,造成了一个残缺的全链路排查体系。

Consumer 组件的另一个工作则是将链路数据计算成指标。实现形式是在写入链路数据的同时,在内存中将 Transaction、Event 等数据依照既定的计算逻辑,计算成 SOA、DAL、Queue 等中间件的指标,内存稍加聚合后再写入时序数据库 LinDB。

指标存储:LinDB 1.0

利用指标的存储是一个典型的工夫序列数据库的应用场景。依据咱们以前的教训,市面上支流的工夫序列数据库 – OpenTSDB、InfluxDB、Graphite– 在扩大能力、集群化、读写效率等方面各有缺憾,所以咱们选型应用 RocksDB 作为底层存储引擎,借鉴 Kafka 的集群模式,开发了饿了么的工夫序列数据库 –LinDB。
指标采纳相似 Prometheus 的“指标名 + 键值对的 Tags”的数据模型,每个指标只有一个反对 Long 或 Double 的 Field。某个典型的指标如:
COUNTER: eleme_makeorder{city=”shanghai”,channel=”app”,status=”success”} 45

咱们次要做了一些设计实现:

• 指标写入时依据“指标名 +Tags”进行 Hash 写入到 LinDB 的 Leader 上,由 Leader 负责同步给他的 Follower。

• 借鉴 OpenTSDB 的存储设计,将“指标名”、TagKey、TagValue 都转化为 Integer,放入映射表中以节俭存储资源。

• RocksDB 的存储设计为:以 ” 指标名 +TagKeyId + TagValueId+ 工夫(小时粒度)“作为 Key,以该小时工夫线内的指标数值作为 Value。

• 为实现 Counter、Timer 类型数据聚合逻辑,开发了 C ++ 版本 RocksDB 插件。

这套存储计划在初期很好的反对了 ETrace 的指标存储需要,为 ETrace 大规模接入与可观测性数据的时效性提供了坚硬的保障。有了 ETrace,饿了么的技术人终于能从全链路的角度去排查问题、治理服务,为之后的技术升级、架构演进,提供了可观测性层面的反对。

其中架构的几点阐明

1. 是否保障所有可观测性数据的可靠性?

不,咱们承诺的是“尽可能不丢”,不保障 100% 的可靠性。基于这个前提,为咱们设计架构时提供了诸多便当。如,Agent 与 Collector 若连贯失败,若干次重试后便抛弃数据,直到 Collector 复原可用;Kafka 上下游的生产和生产也不用 Ack,防止影响解决效率。

2. 为什么在 SDK 中的 Agent 将数据发给 Collector,而不是间接发送到 Kafka?

• 防止 Agent 与 Kafka 版本强绑定,并防止引入 Kafka Client 的依赖。
• 在 Collector 层能够做数据的分流、过滤等操作,减少了数据处理的灵活性。并且 Collector 会将数据压缩后再发送到 Kafka,无效缩小 Kafka 的带宽压力。
• Collector 机器会有大量 TCP 连贯,可针对性应用高性能机器。

3. SDK 中的 Agent 如何管制对业务利用的影响?

• 纯异步的 API,外部采纳队列解决,队列满了就抛弃。
• Agent 不会写本地日志,防止占用磁盘 IO、磁盘存储而影响业务利用。
• Agent 会定时从 Collector 拉取配置信息,以获取后端 Collector 具体 IP,并可实时配置来开关是否执行埋点。

4. 为什么抉择侵入性的 Agent?

抉择寄生在业务利用中的 SDK 模式,在过后看来更利于 ETrace 的遍及与降级。而从当初的眼光看来,非侵入式的 Agent 对用户的集成更加便当,并且能够通过 Kubernates 中 SideCar 的形式对用户通明部署与降级。

5. 如何实现“尽量不丢数据”?

• Agent 中依据取得的 Collector IP 周期性数据发送,若失败则重试 3 次。并定期(5 分钟)获取 Collector 集群的 IP 列表,随机选取可用的 IP 发送数据。
• Collector 中实现了基于本地磁盘的 Queue,在后端的 Kafka 不可用时,会将可观测性数据写入到本地磁盘中。待 Kafak 复原后,又会将磁盘上的数据,持续写入 Kafka。

6. 可观测性数据如何实现多语言反对?

Agent 与 Collector 之间抉择 Thrift RPC 框架,并定制整个序列化形式。Java/Python/Go/PHP 的 Agent 依数据标准开发即可。

2.0:异地多活,大势初成

2016 年底,饿了么为了迎接业务快速增长带来的调整,开始推动“异地多活”我的项目。新的多数据中心架构对既有的可观测性架构也带来了调整,ETrace 亦通过了一年的开发演进,降级到多数据中心的新架构、拆分出实时计算模块、减少报警性能等,进入 ETrace2.0 时代。

异地多活的挑战

随着饿了么的异地多活的技术改造计划确定,对可观测性平台提出了新的挑战:如何设计多活架构下的可观测性零碎?以及如何聚合多数据中心的可观测性数据?

通过一年多的推广与接入,ETrace 已笼罩了饿了么绝大多数各语言的利用,每日解决数据量已达到了数十 T 以上。在此数据规模下,决不可能将数据拉回到某个核心机房解决。因而“异地多活”架构下的可观测性设计的准则是:各机房解决各自的可观测性数据。

咱们开发一个 Gateway 模块来代理与聚合各数据中心的返回后果,它会感知各机房间内 Console 服务。图中它处于某个地方的云上区域,实际上它能够部署在各机房中,通过域名的映射机制来做切换。

如此部署的架构下,各机房中的利用由与机房相绑定的环境变量管制将可观测性数据发送到该机房内的 ETrace 集群,收集、计算、存储等过程都在同一机房内实现。用户通过前端 Portal 来拜访各机房内的数据,应用体验和之前相似。

即便思考极其状况下 – 某机房齐全不可用(如断网),“异地多活”架构可将业务流量切换到存活的机房中,让业务持续运行。而可观测性上,通过将 Portal 域名与 Gateway 域名切换到存活的机房中,ETrace 便能持续工作(尽管会缺失故障机房的数据)。在机房网络复原后,故障机房内的可观测性数据也能主动复原(因为该机房内的可观测性数据处理流程在断网时仍在失常运作)。

可观测性数据实时处理的挑战

在 1.0 版本中的 Consumer 组件,既负责将链路数据写入到 HBase/HDFS 中,又负责将链路数据计算成指标存储到 LinDB 中。两个流程可视为同步的流程,但前者可承受数分钟的提早,后者要求达到实时的时效性。过后 HBase 集群受限于机器性能与规模,常常在数据热点时会写入抖动,进而造成指标计算抖动,影响可用性。因而,咱们迫切需要拆分链路写入模块与指标计算模块。

在选型实时计算引擎时,咱们思考到需要场景是:

  1. 能灵便的配置链路数据的计算规定,最好能动静调整;
  2. 能程度扩大,以适应业务的疾速倒退;
  3. 数据输入与既有零碎(如 LinDB 与 Kafka)无缝连接;

很遗憾的是,彼时业界无现成的拿来即用的大数据流解决产品。咱们就基于简单事件处理 (CEP) 引擎 Esper 实现了一个类 SQL 的实时数据计算平台 –Shaka。

Shaka 包含“Shaka Console”和“Shaka Container”两个模块。Shaka Console 由用户在图形化界面上应用,来配置数据处理流程(Pipeline)、集群、数据源等信息。用户实现 Pipeline 配置后,Shaka Console 会将变更推送到 Zookeeper 上。无状态的 Shaka Container 会监听 Zookeeper 上的配置变更,依据本人所属的集群去更新外部运行的 Component 组件。而各 Component 实现了各种数据的解决逻辑:生产 Kafka 数据、解决 Trace/Metric 数据、Metric 聚合、运行 Esper 逻辑等。

Trace 数据和 Metric 格局转换成固定的格局后,剩下来按需编写 Esper 语句就能生成所需的指标了。如下文所示的 Esper 语句,就能将类型为 Transaction 的 Trace 数据计算成以“{appId}.transaction”的指标(若 Consumer 中以编码方式实现,约须要近百行代码)。通过这次的架构降级,Trace 数据能疾速的转化为实时的 Metric 数据,并且对于业务的可观测性需求,只用改改 SQL 语句就能疾速满足,显著升高了开发成本和晋升了开发效率。

@Name(‘transaction’)
@Metric(name = ‘{appId}.transaction’, tags = {‘type’, ‘name’, ‘status’, ‘ezone’, ‘hostName’}, fields = {‘timerCount’, ‘timerSum’, ‘timerMin’, ‘timerMax’}, sampling = ‘sampling’)
select header.ezone as ezone,

   header.appId                            as appId,
   header.hostName                         as hostName,
   type                                    as type,
   name                                    as name,
   status                                  as status,
   trunc_sec(timestamp, 10)                as timestamp,
   f_sum(sum(duration))                    as timerSum,
   f_sum(count(1))                         as timerCount,
   f_max(max(duration))                    as timerMax,
   f_min(min(duration))                    as timerMin,
   sampling('Timer', duration, header.msg) as sampling

from transaction
group by header.appId, type, name, header.hostName, header.ezone, status, trunc_sec(timestamp, 10);
新的 UI、更丰盛的中间件数据

1.0 版本的前端 UI,是集成在 Console 我的项目中基于 Angular V1 开发的。咱们迫切希望能做到前后端拆散,各司其职。于是基于 Angular V2 的若干个月开发,新的 Portal 组件退场。得益于 Angular 的数据绑定机制,新的 ETrace UI 各组件间联动更天然,排查故障更不便。

饿了么自有中间件的研发过程也在一直前行,在可观测性的买通上也一直深入。

2.0 阶段,咱们进一步集成了 –Redis、Queue、ElasticSearch 等等,笼罩了饿了么所有的中间件,让可观测性无死角。

杀手级性能:指标查看与链路查看的无缝整合

传统的可观测性零碎提供的排障形式大抵是:接管报警(Alert)– 查看指标(Metrics)– 登陆机器 – 搜寻日志(Trace/Log),而 ETrace 通过 Metric 与 Trace 的整合,能让用户间接在 UI 上通过点击就能定位绝大部分问题,显著拔高了用户的应用体验与排障速度。

某个排查场景如:用户发现总量异样忽然减少,可在界面上筛选机房、异样类型等找到理论突增的某个异样,再在曲线上间接点击数据点,就会弹出对应时间段的异样链路信息。链路上有具体的上下游信息,能帮忙用户定位故障。

它的实现原理如上图所示。具体的,前文提到的实时计算模块 Shaka 将 Trace 数据计算成 Metric 数据时,会额定以抽样的形式将 Trace 上的 RequsetId 与 RpcId 也写到 Metric 上(即上文 Esper 语句中,生成的 Metric 中的 sampling 字段)。这种 Metric 数据会被 Consumer 模块生产并写入到 HBase 一张 Sampling 表中。

用户在前端 Portal 的指标曲线上点击某个点时,会构建一个 Sampling 的查问申请。该申请会带上:该曲线的指标名、数据点的起止工夫、用户抉择过滤条件(即 Tags)。Consumer 基于这些信息构建一个 HBase 的 RegexStringComparator 的 Scan 查问。查问后果中可能会蕴含多个后果,对应着该工夫点内数据点(Metric)上产生的多个调用链路(Trace),继而拿着后果中的 RequestId+RpcId 再去查问一次 HBase/HDFS 存储就能取得链路原文。(注:理论构建 HBase Rowkey 时 Tag 局部存的是其 Hashcode 而不是原文 String。)

泛滥转岗、到职的饿了么小伙伴,最朝思暮想不完的就是这种“所见即所得”的可观测性排障体验。

报警 Watchdog 1.0

在利用可观测性根本全笼罩之后,报警的需要天然成了题中之义。技术选型上,依据咱们在实时计算模块 Shaka 上播种的教训,决定再造一个基于实时数据的报警零碎 –Watchdog。

实时计算模块 Shaka 中曾经将 Trace 数据计算成指标 Metrics,报警模块只需生产这些数据,再联合用户配置的报警规定产出报警事件即可。因而,咱们选型应用 Storm 作为流式计算平台,在 Spount 档次依据报警规定过滤和分流数据,在 Bolt 层中 Esper 引擎运行着由用户配置的报警规定转化成 Esper 语句并解决分流后的 Metric 数据。若有合乎 Esper 规定的数据,即生成一个报警事件 Alert。

Watchdog Portal 模块订阅 Kafka 中的报警事件,再依据具体报警的触达形式告诉到用户。默认 Esper 引擎中数据聚合工夫窗口为 1 分钟,所以整个数据处理流程的时延约为 1 分钟左右。

Metrics API 与 LinDB 2.0:

在 ETrace 1.0 阶段,咱们只提供了 Trace 相干的 API,LinDB 仅供外部存储应用。用户逐渐的意识到如果能将“指标”与“链路”整合起来,就能施展更大的功能。因而咱们在 ETrace-Agent 中新增了 Metrics 相干的 API:
// 计数器类型
Trace.newCounter(String metricName).addTags(Map<String, String> tags).count(int value);
// 耗时与次数
Trace.newTimer(String metricName).addTags(Map<String, String> tags).value(int value);
// 负载大小与次数
Trace.newPayload(String metricName).addTags(Map<String, String> tags).value(int value);
// 单值类型
Trace.newGauge(String metricName).addTags(Map<String, String> tags).value(int value);
基于这些 API,用户能够在代码中针对他的业务逻辑进行指标埋点,为起初可观测性大一统提供了实现条件。在其余组件同步开发时,咱们也针对 LinDB 做了若干优化,晋升了写入性能与易用性:

  1. 减少 Histogram、Gauge、Payload、Ratio 多种指标数据类型;
  2. 从 1.0 版本的每条指标数据都调用一次 RocksDB 的 API 进行写入,改成先在内存中聚合一段时间,再通过 RocksDB 的 API 进行批量写入文件。

3.0:新陈代谢,死记硬背

可观测性零碎大一统

在 2017 年的饿了么,除了 ETrace 外还有多套可观测性零碎:基于 Statsd/Graphite 的业务可观测性零碎、基于 InfluxDB 的基础设施可观测性零碎。后两者都集成 Grafana 上,用户能够去查看他的业务或者机器的具体指标。但理论排障场景中,用户还是须要在多套零碎间来回切换:依据 Grafana 上的业务指标感知业务故障,到 ETrace 上查看具体的 SOA/DB 故障,再到 Grafana 下来查看具体机器的网络或磁盘 IO 指标。尽管,咱们也开发了 Grafana 的插件来集成 LinDB 的数据源,但因实质上差别微小的零碎架构,还是让用户“疲于奔命”式的来回切换零碎,用户难以有对立的可观测性体验。因而 2018 年初,咱们下定决心:将多套可观测性零碎合而为一,买通“业务可观测性 + 利用可观测性 + 基础设施可观测性”,让 ETrace 真正成为饿了么的一站式可观测性平台。

LinDB 3.0:

所谓“革新”未动,“存储”后行。想要整合 InfluxDB 与 Statsd,先要钻研他们与 LinDB 的异同。咱们发现,InfluxDB 是反对一个指标名(Measurement)上有多个 Field Key 的。如,InfluxDB 可能有以下指标:

measurement=census, fields={butterfiles=12, honeybees=23}, tags={location=SH, scientist=jack}, timestamp=2015-08-18T00:06:00Z
若是 LinDB 2.0 的模式,则须要将上述指标转换成两个指标:
measurement=census, field={butterfiles=12}, tags={location=SH, scientist=jack}, timestamp=2015-08-18T00:06:00Z
measurement=census, field={honeybees=23}, tags={location=SH, scientist=jack}, timestamp=2015-08-18T00:06:00Z
能够想见在数据存储与计算效率上,单 Field 模式有着极大的节约。但更改指标存储的 Schema,意味着整个数据处理链路都须要做适配和调整,工作量和改变极大。然而不改就意味着“将就”,咱们不能忍耐对本人要求的升高。因而又通过了几个月的爆肝研发,LinDB 3.0 开发实现。

这次改变,除了降级到指标多 Fields 模式外,还有以下优化点:

• 集群方面引入 Kafka 的 ISR 设计,解决了之前机器故障时查问数据缺失的问题。
• 存储层面反对更加通用的多 Field 模式,并且反对对多 Field 之间的表达式运算。
• 引入了倒排索引,显著进步了对于任意 Tag 组合的过滤查问的性能。
• 反对了自动化的 Rollup 操作,对于任意工夫范畴的查问主动选取适合的粒度来聚合。

通过这次大规模优化后,从最后的每日 5T 指标数据涨到现在的每日 200T 数据,LinDB 3.0 都禁受住了考验。指标查问的响应工夫的 99 分位线为 200ms。具体设计细节可参看文末的分布式时序数据库 – LinDB。

将 Statsd 指标转成 LinDB 指标

Statsd 是饿了么宽泛应用的业务指标埋点计划,各机房有一个数十台机器规模的 Graphite 集群。思考到业务的外围指标都在 Statsd 上,并且各个 AppId 以 ETrace Metrics API 替换 Statsd 是一个漫长的过程(也的确是,前前后后替换实现就花了将近一年工夫)。为了缩小对用户与 NOC 团队的影响,咱们决定:用户更新代码的同时,由 ETrace 同时“兼容”Statsd 的数据。

得益于饿了么弱小的中间件体系,业务在用 Statsd API 埋点的同时会“主动”记一条非凡的 Trace 数据,携带上 Statsd 的 Metric 数据。那么只有解决 Trace 数据中的 Statsd 埋点,咱们就能将大多数 Statsd 指标转化为 LinDB 指标。如下图:多个 Statsd 指标会转为同一个 LinDB 指标。

// statsd:
stats.app.myAppName.order.from_ios.success 32
stats.app.myAppName.order.from_android.success 29
stats.app.myAppName.order.from_pc.failure 10
stats.app.myAppName.order.from_openapi.failure 5
// lindb:
MetricName: myAppName.order
Tags:

"tag1"=[from_ios, from_android,from_pc, from_openapi]

“tag2″=[success, failure]

之前咱们的实时计算模块 Shaka 就在这里派上了大用场:只有再新增一路数据处理流程即可。如下图,新增一条 Statsd 数据的解决 Pipeline,并输入后果到 LinDB。在用户的代码全副从 Statsd API 迁徙到 ETrace API 后,这一路解决逻辑即可移除。

将 InfluxDB 指标转成 LinDB 指标

InfluxDB 次要用于机器、网络设备等基础设施的可观测性数据。饿了么每台机器上,都部署了一个 ESM-Agent。它负责采集机器的物理指标(CPU、网络协议、磁盘、过程等),并在特定设施上进行网络嗅探(Smoke Ping)等。这个数据采集 Agent 原由 Python 开发,在一直需要重叠之后,已宏大到难以保护;并且每次更新可观测逻辑,都须要全量公布每台机器上的 Agent,导致每次 Agent 的公布都令人大惊失色。

咱们从 0 开始,以 Golang 从新开发了一套 ESM-Agent,做了以下改良:

• 可观测性逻辑以插件的模式,推送到各宿主机上。不同的设施、不同利用的机器,其上运行的插件能够定制化部署。
• 制订插件的交互接口,让中间件团队可定制本人的数据采集实现,解放了生产力。
• 移除了 etcd,应用 MySql 做配置数据存储,加重了零碎的复杂度。
• 开发了便当的公布界面,可灰度、全量的推送与公布 Agent,运维工作变得轻松。
• 最重要的,收集到的数据以 LinDB 多 Fields 的格局发送到 Collector 组件,由其发送到后续的解决与存储流程上。

从 ETrace 到 EMonitor,一直降级的可观测性体验

2017 年底,咱们团队终于迎来了一名正式的前端开发工程师,可观测性团队正式从后端开发写前端的状态中脱离进去。在之前的 Angular 的开发体验中,咱们深感“状态转换”的管制流程甚为繁琐,并且开发的组件难以复用(尽管其后版本的 Angular 有了很大的改善)。在调用过后风行的前端框架后,咱们在 Vue 与 React 之中抉择了后者,辅以 Ant Design 框架,开发出了媲美 Grafana 的指标看版与便当的链路看板,并且在 PC 版本之外还开发了挪动端的定制版本。咱们亦更名了整个可观测性产品,从“ETrace”更新为“EMonitor”:不仅仅是链路可观测性零碎,更是饿了么的一站式可观测性平台。

可观测性数据的整合:业务指标 + 利用链路 + 基础设施指标 + 中间件指标
在指标零碎都迁徙到 LinDB 后,咱们在 EMonitor 上集成了“业务指标 + 利用链路 + 基础设施指标 + 中间件指标”的多层次的可观测性数据,让用户能在一处观测它的业务数据、排查业务故障、深挖底层基础设施的数据。

可观测性场景的整合:指标 + 链路 + 报警

在可观测性场景上,“指标看板”用于日常业务盯屏与宏观业务可观测性,“链路”作为利用排障与宏观业务逻辑透出,“报警”则实现可观测性自动化,进步应急响应效率。

灵便的看板配置与业务大盘

在指标配置上,咱们提供了多种图表类型 – 线图、面积图、散点图、柱状图、饼图、表格、文本等,以及丰盛的自定义图表配置项,能满足用户不同数据展现需要。

在实现单个指标配置后,用户须要将若干个指标组合成所需的指标看板。用户在配置页面中,先抉择待用的指标,再通过拖拽的形式,配置指标的布局便可实时预览布局成果。一个指标可被多个看板援用,指标的更新也会主动同步到所有看板上。为防止指标配置谬误而引起歧义,咱们也开发了“配置历史”的性能,指标、看板等配置都能回滚到任意历史版本上。

看板配置是动态图表组合,而业务大盘提供了活泼的业务逻辑视图。用户能够依据他的业务场景,将指标配置整合成一张宏观的业务图。

第三方零碎整合:变更零碎 + SLS 日志

因每条报警信息和指标配置信息都与 AppId 关联,那么在指标看板上可同步标记出报警的触发工夫。同理,咱们拉取了饿了么变更零碎的利用变更数据,将其标注到对应 AppId 相干的指标上。在故障产生时,用户查看指标数据时,能依据有无变更记录、报警记录来初步判断故障起因。

饿了么的日志中间件能主动在记录日志时加上对应的 ETrace 的 RequestId 等链路信息。如此,用户查看 SLS 日志服务时,能反查到整条链路的 RequestId;而 EMonitor 也在链路查看页面,拼接好了该利用所属的 SLS 链接信息,用户点击后能中转对应的 SLS 查看日志上下文。

应用场景的整合:桌面版 + 挪动版

除提供桌面版的 EMonitor 外,咱们还开发了挪动版的 EMonitor,它也提供了大部分可观测性零碎的外围性能 – 业务指标、利用指标、报警信息等。挪动版 EMonitor 能内嵌于钉钉之中,买通了用户认证机制,帮忙用户随时随地把握所有的可观测性信息。

为了极致的体验,精益求精

为了用户的极致应用体验,咱们在 EMonitor 上各性能应用上细细打磨,这里仅举几个小例子:

  1. 咱们为极客开发者实现了若干键盘快捷键。例如,“V”键就能开展查看指标大图。
  2. 图上多条曲线时,点击图例是默认单选,目标是让用户只看他关怀的曲线。此外,若是“Ctrl+ 鼠标点击”则是将其加抉择的曲线中。这个性能在一张图几十条曲线时,比照几个要害曲线时尤为有用。
  3. 为了让色弱开发者更容易辨别胜利或失败的状态,咱们针对性的调整了对应色彩的对比度。

成为饿了么一站式可观测性平台

EMonitor 开发实现后,凭借优异的用户体验与产品集成度,很快在用户中遍及开来。然而,EMonitor 要成为饿了么的一站式可观测性平台,还剩下最初一战 –NOC 可观测性大屏。

NOC 可观测性大屏替换

饿了么有一套欠缺的应急解决与保障团队,包含 7 *24 值班的 NOC(Network Operation Center)团队。在 NOC 的办公区域,有一整面墙上都是可观测性大屏,下面显示着饿了么的实时的各种业务曲线。下图为网上找的一张示例图,理论饿了么的 NOC 大屏比它更大、数据更多。

过后这个可观测大屏是将 Grafana 的指标看版投影下来。咱们心愿将 NOC 大屏也替换成 EMonitor 的看版。如前文所说,咱们逐渐将用户的 Statsd 指标数据转换成了 LinDB 指标,在 NOC 团队的帮助下,一个一个将 Grafana 的可观测性指标“搬”到 EMonitor 上。此外,在原来红色主题的 EMonitor 之上,咱们开发了彩色主题以适配投屏上墙的成果(

终于赶在 2018 年的双十一之前,EMonitor 正式入驻 NOC 可观测大屏。在双十一当天,泛滥研发挤在 NOC 室看着墙上的 EMonitor 看版上的业务曲线一直飞涨,作为可观测性团队的一员,这份骄傲之情由衷而生。经此一役,EMonitor 真正成为了饿了么的“一站式可观测性平台”,Grafana、Statsd、InfluxDB 等都成了过来时。
报警 Watchdog 2.0

同样在 EMonitor 之前,亦有 Statsd 与 InfluxDB 对应的多套报警零碎。用户若想要配置业务报警、链路报警、机器报警,须要辗转多个报警零碎之间。各零碎的报警的配置规定、触达体验亦是千差万别。Watchdog 报警零碎也面临着对立交融的挑战。

  1. 在调研其余零碎的报警规定实现后,Watchdog 中仍以 LinDB 的指标作为元数据实现。
  2. 针对其余报警零碎的有显著区别的订阅模式,咱们提出了 ” 报警规定 + 一个规定多个订阅标签 + 一个用户订阅多个标签 ” 的形式,完满迁徙了简直其余零碎所有的报警规定与订阅关系。
  3. 其余各零碎在报警触达与触达内容上也略有不同。咱们对立整合成“邮件 + 短信 + 钉钉 + 语音外呼”四种告诉形式,并且提供可参数化的自定义 Markdown 模板,让用户可本人定时报警信息。
  4. 通过一番艰辛的报警配置与逻辑整合后,咱们为用户“主动”迁徙了上千个报警规定,并最终为他们提供了一个对立的报警平台。

报警,更精准的报警

外卖行业的业务个性是业务的午顶峰与晚顶峰,在业务曲线上便是两个波峰的形态。这样的可观测数据,天然难以简略应用阈值或比率来做判断。即便是依据历史同环比、3-Sigma、挪动均匀等规定,也难以适应饿了么的可观测性场景。因为,饿了么的业务曲线并非变化无穷,它受促销、天气因素、区域、压测等因素影响。开发出一个自适应业务曲线变动的报警算法,势在必行。

咱们通过调研既有规定,与饿了么的业务场景,推出了全新的“趋势”报警。简要算法如下:

  1. 计算历史 10 天的指标数据中值作为基线。其中这 10 天都取工作日或非工作日。不取 10 天的均值而取中值是为了缩小压测或机房流量切换造成的影响。
  2. 依据二阶滑动均匀算法,失去滑动平均值与以后理论值的差值。
  3. 将基线与差值相加作为预测值。
  4. 依据预测值的数量级,计算出稳定的幅度(如上界与下界的数值)。
  5. 若以后值不在预测值与稳定幅度确定的上下界之中,则触发报警。

如上图所示,22 点 01 分的理论值因不在上下界所限定的区域之中,会触发报警。但从后续趋势来看,该降落趋势合乎预期,因而理论中还会辅以“偏离继续 X 分钟”来修改误报。(如该例中,可减少“继续 3 分钟才报警”的规定,该点的数据便不会报警)算法中局部参数为经验值,而其中稳定的阈值参数用户可依照本人业务调整。用户针对具备业务特色的曲线,再也不必费神的去调整参数,配置上默认的“趋势”规定就能够笼罩大多数的可观测性场景,目前“趋势”报警在饿了么宽泛使用。

智能可观测性:根因剖析,大显神威

作为 AIOPS 中重要的一环,根因剖析能帮忙用户疾速定位故障,缩短故障响应工夫,缩小故障造成的损失。2020 年初,咱们联合饿了么场景,攻坚克难,攻破“指标下钻”、“根因剖析”两大难关,在 EMonitor 上胜利落地。

根因剖析最大的难点在于:蕴含简单维度的指标数据难以找到真正影响数据稳定的具体维度;孤立的指标数据也难以剖析出利用上下游依赖引起的故障根因。例如,某个利用的异样指标突增,以后咱们只能晓得突增的异样名、机房维度的异样散布、机器维度的异样散布等,只有用户手工去点击异样指标看来链路之后,能力大抵判断是哪个 SOA 办法 /DB 申请中的异样。继而用户依据异样链路的环节,去追溯上游或上游的利用,反复相似的排查过程,最初以人工教训判断出故障点。
因而,在“指标下钻”上,咱们针对指标指标的曲线,细分成最精密的每个维度数据(指标 group by 待剖析的 tag 维度),应用 KMeans 聚类找出故障数据的各维度的最大公共特色,顺次计算找到最优的公共特色,如此便能找到曲线稳定对应的维度信息。

其次,在链路数据计算时,咱们就能将额定的上下游附加信息附加到对应的指标之中。如,可在异样指标中追加一个维度来记录产生异样的 SOA 办法名。这样在依据异样指标剖析时,能间接定位到是这个利用的那个 SOA 办法抛出的异样,接下来“主动”剖析是 SOA 上游故障还是本身故障(DB、Cache、GC 等)。

在 2020.3 月在饿了么落地以来,在剖析的上百例故障中,根因剖析的准确率达到 90% 以上,显著缩短的故障排查的工夫,帮忙各业务向稳定性建设指标向前跨进了一大步。

4.0:继往开来,乘势而上

通过 4、5 年的倒退,风云变幻但团队初心不改,为了让用户用好可观测性零碎,EMonitor 没有停下脚步,自我变革,心愿让“天下没有难用的可观测性零碎”。咱们向团体的可观测性团队求教学习,联合本地生存本人的技术体系建设,力争百尺竿头更进一步,布局了以下的 EMonitor 4.0 的设计指标。

一、进行多租户化革新,保障外围数据的时延和可靠性

在本地生存的技术体系与阿里巴巴团体技术体系的不断深入的交融之中,单元化的部署环境以及对可观测性数据不同水平的可靠性要求,催生了“多租户化”的设计理念。咱们能够依据利用类型、数据类型、起源等,将可观测性数据分流到不同的租户中,再针对性配置数据处理流程及调配解决能力,实现差异化的可靠性保障能力。

初步咱们能够划分为两个集群 – 外围利用集群与非核心利用汇合,依据在利用上标记的“利用等级”将其数据主动发送到对应集群中。两套集群在资源配置上优先偏重外围集群,并且齐全物理隔离。此外通过配置开关可动态控制某个利用归属的租户,实现业务的柔性降级,防止当下偶然因个别利用的不正确埋点形式会影响整体可观测可用性的问题。

将来可依据业务倒退进一步倒退出业务相干的租户,如到家业务集群、到店业务集群等。或者依照区域的划分,如弹内集群、弹外集群等。

二、买通团体弹内、弹外的可观测性数据,成为本地生存的一站式可观测性平台

目前本地生存很多业务畛域曾经迁入团体,在 Trace 链路可观测方面,尽管在本地生存上云的我的项目中,EMonitor 曾经通过中间件改

造实现鹰眼 TraceId 在链路上的传递,并记录了 EMonitor RequestId 与鹰眼 TraceId 的映射关系。但 EMonitor 与鹰眼在协定上的人造隔膜仍使得用户须要在两个平台间跳转查看同一条 Trace 链路。因而,咱们接下来的指标是与鹰眼团队单干,将鹰眼的 Trace 数据集成到 EMonitor 上,让用户能一站式的排查问题。

其次,本地生存上云后,泛滥中间件已迁徙到云上中间件,如云 Redis、云 Kafka、云 Zookeeper 等。对应的可观测性数据也须要额定登陆到阿里云控制台去查看。云上两头的可观测性数据大多已存储到 Prometheus 之中,因而咱们打算在实现 Prometheus 协定兼容后,就与云上中间件团队单干,将本地生存的云上可观测性数据集成到 EMonitor 上。

三、拥抱云原生,兼容 Prometheus、OpenTelemetry 等开源协定。

云原生带来的技术革新势不可挡,本地生存的绝大多数利用已迁徙到团体的容器化平台 –ASI 上,对应带来的新的可观测环节也亟需补全。如,ASI 上 Prometheus 协定的容器可观测性数据、Envoy 等本地生存 PaaS 平台透出的可观测性数据与 Trace 数据等。

因而,咱们打算在原先仅反对 LinDB 数据源的根底上,减少对 Prometheus 数据源的反对;扩大 OpenTelemetry 的 otel-collector exporter 实现,将 Open Telemetry 协定的 Trace 数据转换成 EMonitor 的 Trace 格局。如此便可补全云原生技术升级引起的可观测性数据缺失,并提供高度的适配性,满足本地生存的可观测性建设。

结语

纵观各大互联网公司的产品演进,技术产品的走向与命运都离不开公司业务的倒退轨迹。咱们饿了么的技术人是侥幸的,能赶上这一波技术改革的大潮,可能施展聪明才智,打磨出一些为用户津津有味的技术产品。咱们 EMonitor 可观测性团队也为能参加到这次技术变更中深感骄傲,EMonitor 能被大家认可,离不开每位参加到饿了么可观测性体系建设的伙伴,也感激各位对可观测性零碎提供帮忙、反对、倡议的搭档!

作者简介:
柯圣,花名“炸天”,饿了么监控技术组负责人。自 2016 年退出饿了么,长期深耕于可观测性畛域,全程参加了 ETrace 到 EMonitor 的饿了么可观测性零碎的倒退历程。
原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0