重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
本篇文章给大家分享的是有关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项目中至少使用其中一个
以上就是Reddit广告服务系统是怎么构建的,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联行业资讯频道。