在Duolingo要求追踪

2021-01-21 00:53:54

Duolingo致力于为所有学习者改善我们应用程序的性能。随着Duolingo随着新课程,功能和数百万其他用户的不断发展,一些团队正在幕后进行工作,以确保事情顺利进行。这篇文章将重点介绍称为“请求跟踪”的后端功能,该功能已导致性能显着提高。

Duolingo不断地从单片架构(一些大型的,紧密耦合的服务)迁移到微服务架构(许多小型的,松散耦合的服务)。这有其优点,但也有一些折衷,包括操作复杂性和可观察性。

例如,使用隔离的服务,可以很容易地记录其请求并进行一些简单的分析,以了解调用在做什么。但是,当许多服务相互作用时(如微服务体系结构那样),您如何获得这种可观察性?为此,我们最近在Duolingo中添加了请求跟踪,这使我们可以观察到并深入了解我们的服务。

Jaeger是一个很棒的开源请求跟踪工具,我们于2019年开始使用。有大量的请求跟踪器,但是我们选择Jaeger是因为它对堆栈具有最佳的兼容性和支持(主要是Python和JVM服务)。

通常将Jaeger之类的工具配置为跟踪少量请求。这避免了使用繁重的基础架构来分析,报告和存储所有跟踪的需求,并且避免了对所跟踪服务的潜在性能冲击。

在早期测试中,我们发现最大的旧版服务效率低下。一种非常常见的内部方法是多次查询Dynamo以获取基本相同的数据,而不是缓存。尽管最初添加时是合理的,但多年来这种模式的效率下降了:查询更多数据,并且被越来越频繁地调用。

缓存可以使Duolingo的整体延迟减少10%,同时也可以大幅度降低成本。这是一个很好的迹象,表明请求跟踪将很有用!

抽样跟踪对于某些问题(但不是全部问题)非常有用。再现罕见的错误或特定用户的问题仍然是困难的任务,因为通常无法获得跟踪。理想情况下,我们希望跟踪每个请求,而又不需要昂贵的基础架构或导致应用程序运行缓慢。

Amazon Web Services(AWS)具有由" X-Amzn-Trace-Id"支持的请求跟踪功能。标头字段;这通常仅由AWS X-Ray(另一个采样的跟踪器)使用。通过打开访问日志记录,我们可以访问该功能,但并未使用它。

但是,此功能支持任意字段。如果我们可以传播标题并添加" Caller"字段(请参见下文),我们可以为任何请求派生一个调用堆栈!

调用5种服务的堆栈,其中svc-A调用svc-B和svc-E。通过我们的微服务传播此自定义标头相对简单。通过AWS Athena和Python将各种应用程序日志条目解析和缝合在一起。这项工作的大部分工作是由刘瑞东作为Duolingo实习项目完成的。

处理每个请求上的所有链接数据将不必要地昂贵,但是如果明智地使用(按需),则将非常有价值。这是我们发现的一些按需应用程序:

跟踪特定请求。原始请求ID可以通过AWS ELB日志找到,但对于开发人员而言,通常很难找到。但是,由于它是通过带有自定义字段的标头生成的,因此它也可以输出到异常数据中(从错误警报服务中报告),或者与元数据(例如用户ID)进行交叉引用。在我们的例子中,使用Jaeger UI,可以轻松地可视化生成的数据:

从AWS ELB日志和自定义标头生成的Jaeger跟踪。延迟和错误调查,例如"什么服务导致了从1:13 pm开始的速度降低?"通过解析高延迟或失败的呼叫,并与常规行为的快照进行比较,我们可以找到可疑服务。然后,我们使用排名算法(例如PageRank)来查找最可能的服务:

服务,按造成中断的可能性排序。 可视化服务依赖关系,并按流量,请求和响应大小或其他元数据进行过滤。 这可能会导致令人惊讶的见解,例如发现未压缩的响应,重复的错误或微服务之间的延迟。 带有过滤选项的服务依赖关系的可视化。 在“可观察性和追踪”空间中正在进行令人激动的工作! 特别是,我们计划采用即将推出的OpenTelemetry标准,包括AWS在内的许多供应商也支持该标准。 Grafana Tempo类似于Always-On跟踪(同时开发); 它不仅是对我们方法的验证,而且可能是一种统一和标准化该功能基础架构的方法。 对Duolingo的软件工程感兴趣? 我们正在招聘全职和实习职位!