当前位置: 首页 > 科技观察

Instagram基于Python的Web服务效率提升

时间:2023-03-13 04:25:55 科技观察

Instagram目前部署了全球最大的DjangoWeb框架(完全用Python编写)。我们最初选择Python是因为它以简单易用着称,这非常符合我们“先做容易的事”的理念。但简单性也伴随着效率的折衷。Instagram的规模在过去两年翻了一番,最近用户超过5亿,因此迫切需要最大限度地提高我们网络服务的效率,以便我们的平台能够继续顺利扩展。在过去的一年里,我们将我们的效率计划提上了日程,在过去的六个月里,我们能够在不向Django层添加新容量的情况下维持我们的用户增长。在本文中,我们将分享我们构建的一些工具以及我们如何使用它们来优化我们的日常部署过程。为什么需要效率?与所有软件一样,Instagram受到服务器和数据中心能源等物理约束的限制。鉴于这些限制,我们希望在我们的效率计划中实现两个主要目标:Instagram应该能够通过持续的代码发布正常提供通信服务,以防由于自然灾害、区域网络问题等导致数据中心区域丢失..Instagram应该能够自由推出新产品和功能,而不会受到容量的阻碍。为了实现这些目标,我们意识到我们需要不断监控我们的系统并防止回归。定义效率Web服务的瓶颈通常是每台服务器上可用的CPU时间。在这种环境下,效率意味着使用相同的CPU资源来完成更多的任务,即每秒处理更多的用户请求,RPS。当我们寻找优化方法时,我们面临的第一个也是最大的挑战是尝试量化我们当前的效率。到目前为止,我们一直使用“每个请求的平均CPU时间”来评估效率,但是使用这个指标有其固有的局限性:设备多样性。使用CPU时间来衡量CPU资源并不理想,因为它同时受到CPU模型和CPU负载的影响。请求影响数据。测量每个请求的CPU资源并不理想,因为在使用每个请求测量方案时,添加或删除轻量级或重量级请求也会影响效率指标。CPUInstructions是比CPUTime更好的指标,因为无论CPU型号和CPU负载如何,它都会为相同的请求报告相同的数量。我们选择使用称为“每个活跃用户”的指标,而不是将我们的所有数据与每个用户请求相关联。我们最终使用“高峰时段每个活跃用户的CPU指令”来衡量效率。现在我们已经建立了新指标,下一步是通过使用Django分析它来了解更多关于回归的信息。Djangoweb服务分析通过分析我们的Djangoweb服务,我们希望回答两个主要问题:是否会发生CPU回归?是什么导致CPU性能下降,我们该如何解决?要回答第一个问题,我们需要跟踪“每个活动用户的CPU指令”指标。如果此指标增加,我们就知道发生了CPU回归。我们为此构建的工具称为Dynostats。Dynostats使用Django中间件以一定的速率对用户请求进行采样,并记录关键效率和性能指标,例如CPU指令总数、端到端请求延迟、访问内存缓存(memcache)和数据库服务所花费的时间,等。另一方面,每个请求都有很多可以聚合的元数据,例如端点名称、HTTP请求返回代码、为请求提供服务的服务器名称以及最新提交的哈希值要求。对于单个请求记录,有两个方面非常强大,因为我们可以切分不同的维度,这将帮助我们减少任何导致CPU回归的原因。例如,我们可以根据端点名称聚合所有请求,如下面的时间序列图所示,从中可以清楚地看出特定端点是否发生了回归。CPU指令对于衡量效率很重要——当然它们也很难获得。Python没有支持直接访问CPU硬件计数器的公共库(CPU硬件计数器是可编程的CPU寄存器,用于测量CPU指令等性能指标)。另一方面,Linux内核提供了perf_event_open系统调用。Python的ctypes桥接技术允许我们调用标准C库的系统调用函数syscall,它也为我们提供了C兼容的数据类型,这样我们就可以编写硬件计数器并从中读取数据。使用Dynostats,我们已经能够找到CPU回归并探索CPU回归发生的原因,例如哪些端点受影响最大,谁提交了实际导致CPU回归的更改等。但是,当开发人员被告知他们的更改导致了发生CPU回归时,他们通常很难找出问题出在哪里。如果问题很明显,那么回归可能一开始就没有归档!这就是为什么我们需要一个Python分析器(一旦Dynostats找到它),以便开发人员可以使用它来找到回归的根本原因。我们没有从头开始,而是决定对一个名为cProfile的现成Python分析器进行适当的修改。cProfile模块通常提供一组统计信息,描述程序不同部分的执行时间和频率。我们没有测量时间,而是用从硬件计数器读取的CPU指令计数器替换了cProfile的定时器计时器。我们在采样请求后生成数据并将数据发送到数据管道。我们还发送了一些类似于Dynostats的元数据,例如服务器名称、集群、区域、端点名称等。在数据管道的另一端,我们创建了一个消费数据的tailer。trailer的主要功能是解析cProfile统计信息并创建能够在Python函数级别表示CPU指令的实体。通过这种方式,我们可以通过Python函数聚合CPU指令,从而更容易找出导致CPU回归的函数。监控和警报机制在Instagram,我们每天部署后端服务30-50次。任何这些部署都可能发生CPU回归。由于每次出现通常至少包含一个差异,因此很容易发现任何回归。我们的效率监控机制包括在每次发布前后扫描Dynostats中的CPU指令,并在变化超过一定阈值时发出警告。对于长期的CPU回归,我们还有一个探测器,可以为负载最重的端点提供每日和每周的更改扫描。部署新更改并不是触发CPU回归的唯一情况。在许多情况下,新功能和新代码路径由全局环境变量GEV控制。按计划的时间表向一部分用户发布新功能是很常见的。我们将此信息作为Dynostats和cProfile统计信息中的附加元数据字段添加到每个请求中。按这些字段对请求进行分组可以识别由全局环境变量(GEV)更改引起的可能的CPU回归。这使我们能够在CPU回归影响性能之前捕获它们。下一步是什么?Dynostats和我们的自定义cProfile,以及我们为支持它们而构建的监控和警报机制,可以有效地查明大多数CPU回归的罪魁祸首。这些进步帮助我们恢复了超过50%的不必要的CPU回归,否则我们永远不会知道这些回归。仍有一些方面我们可以改进并轻松纳入Instagram的日常部署过程:CPU指令指标应该比CPU时间等其他指标更稳定,但我们仍然观察到让我们头疼的差异。将“信噪比”保持在合理的低水平非常重要,这样开发人员才能专注于现实回归。这可以通过引入置信区间的概念并在信噪比过高时发出警报来改善。对于不同的端点,可变阈值也可以设置为不同的值。通过更改GEV检测CPU回归的一个限制是我们必须在Dynostats中手动启用这些比较的日志记录。当GEV的数量逐渐增多,开发的功能越来越多的时候,扩展起来就不容易了。相反,我们能够利用自动化框架来安排这些比较的日志输出并遍历所有GEV,然后在检测到回归时发出警报。cProfile需要一些增强功能以??更好地处理包装函数及其子函数。鉴于我们为Instagram的网络服务构建生产力框架所做的工作,我们有信心在未来继续使用Python扩展我们的服务。我们也开始对Python语言本身进行更多投资,并开始探索从Python2迁移到Python3的方法。我们将继续探索并做更多实验,以继续改善基础架构和开发人员效率,我们期待尽快分享更多经验.本文作者MinNi是Instagram的一名软件工程师。