Spotify如何从Apache kafka迁移到云平台的pub/sub系统


Spotify是一家提供数据信息的公司,在这样的公司中,事件传递是关键组件。包含有关用户,他们采取的操作或数百个系统中的操作日志的数据的每个事件都是有价值的信息。没有成功的事件传递系统,我们将无法深入了解用户并为他们提供他们喜欢的个性化内容。
2015年,当Spotify决定将其基础架构移至Google Cloud Platform(GCP)时,很明显,我们需要在云中重新设计Event Delivery。这篇文章是我们关于Spotify事件传递的博客系列的下一部分。前三个可以在Spotify的事件交付中找到-通往云之路(第一部分)(第二部分)(第三部分)。自从我们撰写了最初的3篇博客文章以来,我们已经在Cloud中运行了Event Delivery已有2.5年了,我们认为现在是该系列博客中另外两个部分的时候了。在本部分(IV)中,我们将讨论已实现的目标以及从原始计划中获得的收益,正在采取的后续步骤以及如何通过向上移动云中的堆栈来发展和简化。在那2.5年中,很多事情发生了变化,我们有了新的要求。现在,我们正在开发下一代事件传递,我们将在本博客系列的第V部分中进行介绍。

事件传递系统
我们花了将近一年的时间来设计,编写,部署和扩展当前的Cloud Pub / Sub的事件交付系统完全投入生产。从一开始,该系统就应该仅在云(GCP)中运行,而我们实现了这一目标。为了快速迭代,我们使生产和使用接口与旧系统兼容。此设置使我们能够并行运行两个系统,以便比较所有交付的数据是否完整。这对于我们通过严格的审核要求至关重要,这在当时是一个紧迫的问题。由于我们确保新的事件交付系统已准备就绪,可以满足严格的IT审核要求,因此首先将关键业务数据迁移到新系统是有意义的。
关键业务数据的示例包括EndSong事件(Spotify用户结束收听曲目时发出的事件),该事件用于向唱片公司和艺术家支付版权费,计算每日活动用户(DAU),每月活动用户(MAU);和UserCreate事件(指示新的Spotify用户帐户的事件)。
我们计划进行务实且分阶段的部署,但是并没有按计划进行。我们仅在一天之内就将新系统部署到了100%的流量中,并且令人惊讶地运行良好。
2017年2月,我们取消了现有的基于Kafka的系统,新系统已成功运行,此后每天交付数十亿个事件。

自从我们将基于GCP的事件交付系统投入生产以来,Spotify的用户基础有了惊人的增长。在2016年至2018年之间,我们的MAU翻了一番以上,现在为232M MAU(截至2019年8月5日)。系统全面投入生产后,我们达到了150万个事件/秒(e / s),仅比我们在设计阶段所测试的最大值少了0.5M e / s(请参阅第二部分中的更多内容)。到2019年第一季度末,我们在全球峰值产量超过了8M e / s。有500多种不同的事件类型,每天总计有超过350 TB的事件(原始数据)流经我们的系统,这些事件来自我们的客户和内部系统。

原则,策略和重大决策
这些数字令人印象深刻,那么,让我们构建如此可扩展的基础架构的原则是什么?让我们分享一些。

1.每个事件类型的隔离
事件类型由生产者定义,它具有名称,架构和元数据。从基于Kafka的系统的运维困难中,我们了解到并非所有事件都是平等的,我们可以利用这一点对我们有利。事件类型具有优先级,并且可能会根据下面列出的某些属性而有所不同:

  • 业务影响事件类型–有些用于向唱片公司和艺术家支付版权费,有些用于计算公司关键指标。这些事件类型均受外部SLA的约束,以便及时交付并保证质量
  • 批量事件类型–每小时发出几百次,有些每秒发出1M +次。
  • 大小事件类型–大小在几字节到几十千字节之间变化。

为了防止大量或嘈杂的事件破坏业务关键数据,我们选择尽快隔离事件流。事件类型紧接在事件服务之后,事件服务是我们基础结构的入口点。它负责解析和识别事件类型,并将其发布到Cloud Pub / Sub(Pub / Sub)。
格式错误或无法识别的事件将被拒绝。最后,每个事件类型都有其自己的发布/订阅主题,提取,转换,加载(ETL)过程以及最终存储位置。
此设置使我们可以灵活地单独交付每个事件类型。出于运维目的,事件类型按重要性进行区分:高,中和低,并且每个重要性级别都有单独的优先级和服务级别目标(SLO)。
这使我们能够在事件发生期间确定工作和资源的优先级,以便首先交付最关键的事件。我们还错开警报,以免在不太重要的事件类型出现问题时在深夜醒来。我们最严格的SLO如下:
  • 高优先级–几个小时的SLO
  • 正常优先级– 24小时SLO,为第二天会出现事件的选择
  • 低优先级– 72小时SLO,主要在内部使用,用于下一个工作日所需的事件

您可以在《网站可靠性工作手册》(在线提供,第13章–数据处理管道,案例研究:Spotify)中与我们共同撰写的一章中,从操作角度阅读更多关于我们如何维护事件交付管道的信息。

2.活着超过延迟
传递的事件按小时划分;这意味着每个事件类型都有一个存储事件的不可变的每小时时段。请参见图3,每小时的时段由正方形表示,每种颜色表示给定的事件类型,例如,带有T14标签的深红色正方形表示一个不可变的时段),其中包含即抵达到我们的系统13:00至14:00之间UTC(红暗事件类型)的所有事件,由于每个事件类型前面提到的分离,我们选择优先活着超过延迟。  换句话说,嘈杂的,破碎或受阻止的事件类型不会都塞系统的其余部分。
您可以在可靠地将Cloud Pub / Sub流导出到Cloud Storage中了解有关此概念的更多信息,该博客文章解释了我们ETL的所有技术。

3.关注点分离和自动缩放
我们的系统包含将近15种不同的微服务,这些微服务部署在大约2500个VM上。这使我们能够分别处理它们中的每一个,并在需要时进行替换。每个组件都被单独监视,这使得调试和发现瓶颈变得容易。当然,将变更引入如此具有业务关键意义的大型系统存在风险。为了使我们能够自信地进行迭代,我们提供了端到端集成测试,暂存环境和Canary机器。这些服务中的一些是自动缩放的,这就是为什么前面提到的VM数量有所不同的原因。具有数百台机器的自动伸缩基础架构的最大挑战是其状态始终在变化。这成为我们的痛点之一。有了我们现在已经达到的规模,使用Spotify的部署工具进行自动扩展会导致整个团队的部署最多需要三个小时!这意味着尽管系统设计用于安全且快速的迭代,但是由于部署工具的局限性,现在不幸的是我们的迭代周期很长。
您可以在下面阅读有关我们配置的更多信息自动扩展Pub / Sub使用者

4.管理服务
作为Spotify迁移到云的一部分,该策略一直是将耗时的问题外包给Google和GCP,这些问题对于我们的业务而言并非核心。特别是,我们利用了托管消息队列,数据处理和存储的优势。我们系统的骨干是Cloud Pub / Sub云存储(GCS)是最终数据集和中间数据的主要存储。ETL建立在Compute Engine(GCE)实例(每个事件类型为一个群集,使用区域托管实例组),CloudSQL(用于元数据)和Dataproc(用于重复数据删除)的基础上。我们使用Dataflow作业对事件中的敏感数据进行加密。我们使用BigQuery 用于数据仓库,此工具已成为数据工程师,数据科学家,分析师,产品经理以及大多数希望与事件数据进行交互的人的最爱。

我们通过所有这些托管服务实现稳定的途径已经成功,但并非一帆风顺。我们与Google建立了牢固的合作关系,并且了解了很多有关在云中构建基础架构的知识。使用托管服务时,需要考虑以下几点:

  • Google支持工程师全天候提供服务,这非常有价值。
  • 工程师只需单击几下,便可以快速将其想法转化为基础架构项目。尽早构建概念可以减少以前尝试新事物所需的时间,并且可以大大降低与失败相关的风险。
  • 使用alpha或beta产品的价值。云提供商需要新产品的早期采用者,并且通过及早提供反馈,您就可以吸引组织可能会从中受益甚至可以决定产品未来的功能请求。
  • 有时,事情开箱即用。由主要的云提供商构建的解决方案是通用的,以便满足各种不同的需求。您所做的工作很可能在某种程度上很特别。就我们而言,通常是我们的规模。我们成功地为许多托管服务构建了自己的扩展和库。随着时间的推移,这些扩展已经过时了,但是我们接受限制并创建扩展的意愿是交付项目的主要部分。如果您遇到限制并为您的用例构建扩展,那么它很有可能在整个社区中都很有价值,这是开源贡献的绝佳机会(请参阅我们的GitHub个人资料)。

5. 测试新想法,快速失败,更快恢复
在迁移到云服务期间,由于可用的选项并非最合适,因此我们需要创建自定义解决方案。在尝试根据我们的需求量身定制云服务时,我将提供三个试验和错误经验的示例。
数据流是一个出色的数据处理框架,可以根据类型和处理时间实时对数据进行分区。这听起来像是我们的ETL流程的理想选择-这个想法失败了。当我们试图生产我们的概念证明时,我们遇到了问题。我们撰写了有关此初始计划的详细信息以及将Cloud Pub / Sub流可靠导出到Cloud Storage中失败的内容。
另一个例子是我们试用Pub / Sub的经验。当我们开始采用该服务的早期采用者时,我们的计划是使用官方Java客户端库-这也失败了。他们要么还没有准备好,要么就不能满足我们的用例和规模要求。但是,我们仍然在Pub / Sub上出售,因此决定编写自己的库供发布和使用。在改用Google的图书馆之前,我们已经在生产中成功使用了这些库一年以上。
建立和维护定制解决方案可能很困难,并且软件很快就会过时。使用推荐的客户端或库时不是这种情况,因为只要产品可用,云提供商通常就必须提供更新。此外,自定义解决方案降低了客户支持专家提供的价值,因为他们很难确定问题是在云供应商的基础架构内还是在您的磁带库内。

事件传递即服务
我们有数百个团队拥有500多种不同的事件类型,这些团队很可能是给定事件类型的生产者。但是,某些事件类型是通用的,并且在许多团队之间共享,例如PageView。所有者可以完全控制其事件类型,但最重要的是,他们所需的更改不需要我们的参与。他们可以随时定义并开始发出新的事件类型。所有者可以将不同类型的存储(GCS或BQ)和分区策略(每小时或每天)应用于其事件类型定义。我们允许所有者以向后兼容的方式来发展事件类型的模式(我们使用Apache Avro)。受此限制,消费者可以读取长达10年的历史记录,并使用相同的代码来开发数据,而无需回填数据集。
所有这些自动化基础架构均由集中式协调器管理,以获取所需的云资源。对于每种事件类型,它都会创建一个单独的发布/订阅主题和订阅,ETL集群(并计划ETL作业),分发其架构,向适当的SLO注册它,并在所有不同的微服务和管道上配置监视。在正式弃用“事件类型”之后,该作曲者还负责所有清理工作。这种集中式服务是运行时组件,但是我们希望下一次迭代成为声明性和静态资源,例如配置文件。

得到教训
设计按比例缩放的系统时,一个好的方法是为系统提供容量模型。这将帮助您计算每种资源所需的配额,并随着规模的增加而平稳运行。有趣的是,我们观察到数据增长比服务流量快一个数量级。

大系统要花大钱
我们开始开发该系统的目的是减少摩擦,以便我们可以收集尽可能多的数据。两年,研发翻了一番org以后,我们收集的数据超出了我们的使用范围。花钱没事,但浪费钱不对。具有数百万用户和数千个VM的系统将花费大量资金,无论它是在您自己的数据中心中运行还是外包给云提供商。最初,有意识地决定从数据社区中提取数据生产成本,以不妨碍学习和创新。不利的一面是,它变得太容易浪费,有时浪费大量金钱。当前,我们正在努力在不受限制的创新和成本意识之间寻求平衡。间歇性和短期节省成本的冲刺一直是减少浪费的好方法,同时允许在新项目上不受限制地支出。