• 全部文章 专栏目录
  • 互动问答
  • 栈栈作者
    2019-08-26 17:12:54
    经验教训帖:探寻Reddit广告服务系统的构建!

    Reddit如何使用Go构建其广告服务系统,以及从流程中汲取经验教训。

    概要

    Reddit工程团队近段时间将Go引入其堆栈,以编写新的广告服务系统来取代第三方系统。 Deval Shah向我们介绍了这个新服务的架构、Reddit团队次使用Go的经验,以及他们使用Go构建这个广告服务器所学到的所有课程。

    Reddit简介

    Reddit是互联网的首页,它是一个拥有数万个兴趣社区的社交网络,人们可以在那里讨论对他们来说重要的事情。

    Reddit的数字排名:

    第5/18(美国/世界)Alexa排名

    330M + MAU

    138K活跃社区

    每月1200w篇文章

    每月2B投票

    而Reddit构建的任何系统都必须能够处理此级别的流量。

    广告架构概述

    广告服务器需要处理整个广告流程。广告服务器处理从广告显示到广告之后的任何后处理的所有内容。

    广告服务@ Reddit

    Reddit广告服务器有几个要求:

    扩展:Reddit上的每个请求都会进入广告系统,所以它必须应对大规模的需求。

    速度:广告服务器必须快速。他们不希望广告成为降低用户体验的性能瓶颈。他们要求在30毫秒内回复广告。

    拍卖:确保服务器能够根据出价选择佳广告。

    步调:服务器必须能够以佳方式分配广告。

    之前的广告服务@ Reddit:

    之前,每当用户访问reddit.com时,reddit 的monolith后端都会向第三方广告服务器发送请求。第三方服务器将使用其选择的一个或多个广告进行响应,并将其返回给用户。

    过了一会儿,他们意识到继续使用第三方广告服务器对他们来说不会有用,因为它:

    可定制性较低:第三方不支持他们想要进行的许多更改。

    操作上不透明:他们无法知道某些事情是如何实施的,无法控制广告的质量等。

    我们决定建立一个广告服务器,建立一个由3人组成的团队。从infra开始,编写服务,然后将其推广到正在生产的系统。

    广告服务基础设施:

    广告服务器基础架构中使用的一些值得注意的工具:

    适用于所有RPC的Apache Thrift。 Thrift自2007年以来一直存在,Reddit从一开始就一直在使用它。

    RocksDB用于数据存储。它是由Facebook构建的OSS键值存储。它是一个可嵌入的数据存储,它避免了网络跳变,并针对高读取和写入进行了优化。

    他们还决定使用Go作为主要的后端语言。这是Reddit次将Go用于生产。在此之前,Reddit主要使用Python和Java。该团队希望确保Go成为Reddit使用的语言集中的一等公民,并支持Reddit所需的一切。

    广告服务器架构:

    这是新广告服务器的架构:

    简要概述它的工作原理:

    Reddit.com调用了一个名为广告选择器的服务。这是广告投放基础架构中的项服务。这是一种Thrift服务,并接收来自reddit.com的请求。然后,它调用一个名为getAds的函数,该函数处理获取和返回要向用户显示的广告。然后广告选择器调用充实服务。

    充实服务负责获取有关查找和选择相关广告所需的请求、用户和其他信息的更多数据和信息。它会收集所有这些信息并将其返回给广告选择器。

    收到充实服务的响应后,广告选择器会选择添加,然后将广告返回给reddit.com以显示给用户。它还将回复发送给Kafka。

    向用户展示广告后,需要进行一些后期处理。客户端向事件跟踪器服务发送事件HTTP请求。此活动可确认广告已投放。此事件通知也被带到Kafka。

    Kafka为两个Apache Spark作业提供数据:

    事件统计流作业始终在运行,它会写入增强服务以提供用于学习选择更好的广告信息。

    还有Pacing循环,它涉及Pacing Spark工作。这涉及一个流媒体工作,计算每个广告客户展示的广告数量,以及另一个确保广告佳展示的工作。

    在这种架构中,Go服务是:

    广告选择器:

    有30ms的P99要求

    涉及用于定位和选择的复杂业务规则

    进行竞价:所有的广告,业务逻辑规则,是在竞争得到广告显示,而广告选择器会处理此问题。

    事件追踪:

    1ms P99要求

    确认日志和事件

    需要高度可靠

    充实服务:

    节俭服务

    将数据返回到广告选择器

    有一个嵌入的RocksDB数据库

    4毫米P99

    对于每个请求,它在Go中进行前缀扫描,并获取一堆数据并进行计算和聚合。我们的想法是避免网络跳变获取信息,以确保我们快速提供响应。

    Reddit的其他一些Go工具和服务则不会深入探讨:

    报告服务

    Vault管理工具

    广告事件生成服务

    我们的Go经验

    这是Reddit与Go的次体验。德瓦尔表示,到目前为止,这段经验很棒。这项工作始于使用Go的两到三名工程师,现在已经发展到大约十几名在Go方面工作的工程师。

    他们在Go看到的主要优势是:

    提高开发人员的速度:新工程师可以加入并快速熟悉代码。 Go强调简单性、快速部署和编译时间意味着紧密的反馈循环,这有很大帮助。

    开箱即用的出色表现:除了遵循佳实践外,没有太多的工具或优化来快速运行。与他过去调整JVM和处理垃圾收集的经验相比,这对Deval来说是一次不错的体验。

    易于专注于业务逻辑:业务逻辑是困难的部分,Go的简单性和开箱即用的性能有助于团队专注于它。

    后,广告服务会延迟大幅下降:响应时间从90毫秒降至10毫秒以下。

    得到教训

    这是一系列面临的问题,Reddit如何处理这些问题,以及从这些挑战中学到的知识。

    问题1:如何构建生产就绪的微服务?

    Reddit以前有过为Python做过的经验,但不是Go。

    初的原型通过大量的StackOverflow读取和谷歌搜索工作,但显然不会与开发人员一起扩展。

    他们看到的一些问题是:

    记录、指标等都到处都是

    改变传输层很难

    我们需要可重复的模式

    他们意识到Go社区已经解决了这些问题,因此他们研究了解决这些问题的现有框架。他们遇到的一些选择:

    他们认为Go-Kit有意义。 Reddit选择Go-Kit的主要原因是:

    支持Thrift

    是灵活的,不是非常有描述性。如果Reddit想要转移到gRPC,他们希望能够轻松迁移。

    具有用于记录、度量、速率限制、跟踪、断路等工具,这些是在生产中运行微服务时的标准要求。

    Go-Kit @ Reddit。这是使用Go-Kit的图:

    这个架构有一些值得注意的事情。中心服务有2个实现:内存实现(这很好并可以用于原型),以及用于生产实现的RocksDB实现。本地开发仍然存在内存中实现。

    有几个中间件层:跟踪、日志记录和度量。后,Thrift运输处于顶层。这种结构使得更改变得容易。例如,如果他们想要将传输层从Thrift更改为gRPC,他们只需要更改顶层。

    使用Go-Kit是有益的,因为它为团队提供了如何构建Go代码的良好例证。他们以前没有这方面的经验,因此使用Go-Kit有助于理解Go服务的典型结构。

    教训1:使用框架/工具包。对于您使用Go的所有内容而言,并不是必需的,但对于需要度量、日志记录等的生产服务,请使用已解决问题的库而不是尝试自己完成。

    问题2:如何安全快速地推出新系统?

    终目标是推出新的广告服务器,对Reddit用户、支付广告客户、依赖广告团队的其他内部团队影响小。第三方广告服务器是一个黑盒子,Reddit需要一种快速迭代、学习和改进的方法。

    这就像在飞行途中改变飞机。他们慢慢地在他们的第三方服务周围添加了新的基础设施,当它准备就绪时,他们会把它撕掉:

    他们首先将广告选择器注入请求路径,将其纯粹作为代理。系统执行的操作与以前相同,但广告选择器就位。这使他们可以通过广告选择器扩展请求,而无需实际执行任何操作。

    然后,他们不仅仅是代理,而是在广告选择器服务中实施并推出了原生广告选择。现在,广告选择器将在内部处理请求,但仍充当代理并将请求传递给第三方,系统仍将使用第三方响应。

    然后,他们添加了Event Logger来实现本机响应的日志记录,并设置Kafka。

    他们继续构建其余的服务,从存根服务开始,并在此过程中添加逻辑。

    终,一旦一切就绪,他们就会切断第三方广告服务器。

    在这些Go特性的帮助下,Go允许他们安全轻松地迁移到新的广告服务器:

    Go编译器很快

    支持跨平台编译

    自包含二进制文件

    强并发原语

    教训2:Go使快速迭代变得简单而安全。

    问题3:如何调试延迟问题?

    部署新广告服务器后,他们确实看到了一些缓慢,网络故障,部署不良等问题。

    如果你确切知道哪个服务有问题,pprof就很棒。另一方面,分布式跟踪使您可以查看服务。他们没有支持广告方面的分布式跟踪,但他们确实在Reddit的堆栈上的其他地方支持它。

    为什么跟踪有用?

    识别导致高总体延迟的热点

    帮助发现其他错误/意外行为

    跟踪通常很容易,你有一个客户端和服务器。在客户端,您提取跟踪标识符,并将它们注入您发送的服务器的请求中。在服务器端,当您获得请求和标识符时,将它们放入上下文对象并传递它们。使用HTTP和gRPC非常简单,没有理由不这样做。

    但是,reddit正在处理Thrift,所以他们遇到了一些问题。

    他们看了一下Thrift替代品,Facebook Thrift和Apache Thrift。他们正在寻找的两个关键功能是对标题和上下文对象的支持:

    他们尝试使用FB thrift,但是存在一些问题,主要是缺少上下文对象,导致代码混乱和复杂化。在Apache thrift中,支持上下文对象,但它不支持头文件。因此,解决方案是:向Apache Thrift添加标头。这已经针对其他语言完成,但不适用于Go。因此,他们将THeader添加到Apache Thrift。这意味着现在支持上下文对象,并且头文件可以存储跟踪标识符。

    如果您想查看这些更改,可以查看https://github.com/devalshah88/thrift。 Deval希望通过贡献流程获得更改并将其合并到上游。

    这是一个跟踪代码。客户端包装器只从上下文对象中提取跟踪信息,并将其添加到headers:

    服务器包装器从头部获取信息并将其注入上下文对象,以便它可以传递:

    此代码来自https://github.com/devalshah88/thrift-tracing。

    完成所有这些工作后,分布式跟踪被证明在调试延迟问题方面非常有用。然而,我们得到的结论是第三课:使用节俭和Go进行分布式跟踪是很困难的。

    问题4:如何处理缓慢/超时?

    在Reddit,他们希望系统能够优雅地处理缓慢。他们从不希望用户受到影响,因此如果速度缓慢,Reddit宁愿不展示广告也不愿降低用户体验。

    他们的两个目标是:

    不要让用户等待太久

    不要浪费资源做不必要的工作

    使用上下文对象来强制服务中的超时:这是来自充实服务的代码,用于向上下文对象添加截止日期,传递它,并在截止日期到期时提前退出。

    这样的结果是好的,但还不够:

    张图显示了从充实服务获得响应所需的时间。这个特定的时间框架有一些缓慢,但它没有让用户等待超过25毫秒。

    第二个图表显示,在服务器端,增强服务正在处理长70毫秒的请求,因此服务器在客户端已经超时并且在不再需要响应之后,有些浪费资源。

    通常要做的是使用HTTP传播截止日期。此代码添加了一个超时,它通过上下文对象传递给服务器:

    Thrift使这很难。这里没有使用上下文对象。如果客户端超时,goroutine不知道并且不退出:

    这个方法不是特别好,但有办法来解决这个问题:

    一种选择是为请求有效负载添加截止时间。客户需要在请求中包含截止日期。服务器会将截止日期注入上下文对象,并使用它。这并不是很好,因为必须在所有端点进行此更改。

    相反,他们通过截止日期作为节俭标题。这与它们传递跟踪标识符的方式类似。在此更改之后,在服务器端,他们看到类似于客户端的延迟:

    教训4:在服务内部和服务之间使用截止日期。

    问题5:如何确保新功能不会降低性能?

    快速迭代和复杂的业务逻辑可能导致性能问题。广告服务团队需要流程和工具,以确保他们能够快速移动而不会违反延迟SLA。为此,他们使用了负载测试和基准测试。

    使用弯曲器进行负载测试:

    这就是你从Bender那里得到的回应:

    负载测试对于在重负载下测试更改非常有用,并且允许开发人员再推送到生产之前优化新功能以实现高负载。

    他们还利用所有关键系统的基准测试。此基准测试代码:

    获取此输出:

    基准测试有助于:

    通过改变减慢速度来防止降级

    让您了解事情随时间的变化情况

    告知开发人员有关不同实现存在的权衡

    教训5:基准测试和负载测试很容易。做吧!

    回顾:

    使用框架/工具包

    Go使快速迭代变得简单而安全

    使用Thrift和Go进行分布式跟踪很难

    在服务内部和跨服务使用截止日期

    使用负载测试和基准测试

    结论:

    Go帮助reddit构建和扩展新的广告服务平台 - 易于构建和快速

    我们分享了我们在此过程中学到的5个重要经验教训

    尝试在下一个Go项目中至少使用其中一个

    2
    0
    AI中国
    创建时间:2019-08-22 17:24:09
    我将分享我关注的人工智能资讯、采访报道、技术干货文章等
    展开
    订阅须知

    • 所有用户可根据关注领域订阅专区或所有专区

    • 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

    • 专区发布评论属默认订阅所评论专区(除付费小栈外)

    作者

    • 栈栈
      作者
    戳我,来吐槽~