饿了么看重阿里巴巴的什么,阿里巴巴收购饿了么有什么影响

首页 > 经验 > 作者:YD1662022-10-29 08:54:49

全链路可观测性的实现

所谓全链路可观测性,即每次业务请求中都有唯一的能够标记这次业务完整的调用链路,我们称这个ID为RequestId。而每次链路上的调用关系,类似于树形结构,我们将每个树节点上用唯一的RpcId标记。

饿了么看重阿里巴巴的什么,阿里巴巴收购饿了么有什么影响(5)

如图,在入口应用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的两层索引机制

饿了么看重阿里巴巴的什么,阿里巴巴收购饿了么有什么影响(6)

如图,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)三部分组成。

如此,从业务链路到全链路信息到详细单个链路,形成了一个完整的全链路排查体系。

饿了么看重阿里巴巴的什么,阿里巴巴收购饿了么有什么影响(7)

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

我们主要做了一些设计实现:

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

其中架构的几点说明

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

不,我们承诺的是“尽可能不丢”,不保证100%的可靠性。基于这个前提,为我们设计架构时提供了诸多便利。如,Agent与Collector若连接失败,若干次重试后便丢弃数据,直到Collector恢复可用;Kafka上下游的生产和消费也不必Ack,避免影响处理效率。

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

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

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

选择寄生在业务应用中的SDK模式,在当时看来更利于ETrace的普及与升级。而从现在的眼光看来,非侵入式的Agent对用户的集成更加便利,并且可以通过Kubernates中SideCar的方式对用户透明部署与升级。

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

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

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

2.0:异地多活,大势初成

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

饿了么看重阿里巴巴的什么,阿里巴巴收购饿了么有什么影响(8)

上一页12345下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.