Appodeal是一家拥有约100名员工的公司,在莫斯科,旧金山,巴瑙尔,卢茨克,基洛夫,巴塞罗那以及自2018年6月以来在明斯克工作。
我们通过向用户展示广告来通过移动应用获利。 我们从广告中介开始,但是技术堆栈在不断增长,因此广告技术行业的其他产品也已添加到中介中。

对于不熟悉Ad Tech的用户,这是从事广告领域的技术公司的工作范围。 当您告诉某人您从事移动广告领域的工作时,人们常常会持怀疑态度-显然,会想到恼人的广告“ Azino Three Axes”。 实际上,这只是冰山一角,所有这些“野生”广告都与真实的广告业务无关。
我们从事的移动细分市场早已超过了网络广告细分市场:

为什么要在应用程序中集成广告?
当然,创建应用程序需要花费大量资源-创建者/所有者希望花费时间和精力来获得回报。 将其应用程序发布到App Store / Google Play上的移动应用程序的所有者称为发布者或发布者。 发行商采用了不同的获利模型,从应用内购买到广告获利。 但是在所有这些方法中,只有后者允许用户不用为使用该应用程序付费—这可以最大程度地扩大受众范围。
是的,如果广告过多,它将使所有人烦恼,并对用户保留率产生不利影响。 当然,没有人需要。 因此,他们总是试图明智地整合广告,以在其应用程序上获得最大的收益,同时又不花一分钱。
如何运作?
一旦发布者决定通过广告获利,他就会来到公司,这对他来说可能会使这项任务尽可能容易。 Appodeal如何处理? 在网站上注册后,我们将他的应用程序与我们的服务集成在一起。 这是通过客户端SDK完成的,该客户端SDK将应用程序连接到服务器部分,并通过API与服务器部分进行通信。
如果您最小化细节,那么交互的目标将减少到两个阶段:
一个 确定现在要显示哪个广告;
b。 发送有关显示哪个广告和不显示广告的信息,并将其显示在统计信息中。
当前,Appodeal服务于数千个活动应用程序,每天提供大约400-4.5亿个广告印象,这些响应是响应广告网络(作为广告提供者)的大约10亿个请求而收到的。 为了完成这项工作,我们的服务器每秒处理约125,000个请求,即 每天大约有108亿次查询。

这一切是建立在什么基础上的?
我们使用各种技术来提供速度,可靠性以及开发和支持的灵活性。 目前,我们正在使用以下语言编写代码:
- / Ruby / Ruby on Rails + React.JS(前端)/:仍然有很大一部分API和用户以及我们的员工看到的整个Web部分
- / GoLang /:不仅处理大量的统计数据
- / Scala /:实时处理请求,以使用RTB协议进行流量交换(在本文末尾了解更多信息)
- / Elixir / Phoenix /:相反,是实验部分。 构建一些微服务以处理一些统计信息和API。

为什么最初是Ruby和Ruby on Rails?
Appodeal在其细分市场中与非常大的参与者竞争,因此您必须快速适应市场变化。 通常,这感觉就像以100 km / h的速度改变汽车的车轮。 Ruby on Rails使我们能够经受住竞争并在市场上获得立足点,足以成为该领域的领导者。 我们认为,Rails的主要优点是:
- 大量合格的开发商
- 伟大的社区。 大量现成的解决方案和库
- 引入新功能以及更改/删除旧功能的速度

明显的缺点:
- 整体表现不佳。 它还会影响(目前)缺少JIT,缺乏使代码并行化的能力(如果您不考虑JRuby)。 在某种程度上,这仍然可以接受,因为瓶颈通常是数据库和缓存。 我们在NewRelic的图片中看到的是:

- 在微服务上,铁轨整体结构的切割效果不是很好-它们受到业务逻辑和数据访问逻辑(ActiveRecord)之间高度连接的影响。
数据如何存储?
我们有很多数据。 很好 我们正在谈论数十亿/数百/数千亿的记录。 由于数据完全不同,因此我们以不同的方式存储它们。 它在架构上绝不应局限于任何一种通用的解决方案。 实践表明,首先,在Highload中几乎没有通用解决方案。 通用性是指访问/读取速度/数据存储大小的平均指标(或明显低于平均水平),这是为此多功能性支付的费用。 其次,您需要一直尝试新的东西,进行实验,并为任务寻找非平凡的解决方案。 总计:
- / PostgreSQL /:我们喜欢Postgre。 我们认为它是目前最好的OLTP存储解决方案。 有关用户,应用程序,广告活动等的数据存储在此处。 我们使用主副本复制。 我们仅在圣诞节假期进行备份,因为这是w夫(开玩笑)。
- / VerticaDB /:面向列的数据库。 我们用来存储数十亿的统计记录。 简而言之,Vertika一段时间以来一直被认为是存储分析的最佳OLAP解决方案。 主要缺点是许可证的价格很高(个人)。
- / ClickHouse /:也是面向列的数据库。 使用VerticaDB逐渐切换到它。 我们目前正在考虑最好的OLAP解决方案。 不值一毛钱。 它可以快速,可靠地工作。 主要的缺点是无法删除和更新数据(如果有人感兴趣,我们将在另一篇文章中进行讨论)。
没事! 怎么不可能删除和修改数据?
- / Aerospike /:我们认为最快的NoSQL键值存储。 有很多缺点,但总的来说,我们感到满意。 在其性能站点上甚至还提供了Aerospike与其他解决方案的比较表:[何时使用Aerospike NoSQL数据库与。 Redis](https://www.aerospike.com/when-to-use-aerospike-vs-redis/)
- / Redis /:关于“萝卜”,我认为分开讲是没有道理的。 矛盾的是,它的主要优点是易于使用和单线程,从而避免了竞争情况,例如在使用普通计数器时。
- /德鲁伊/:我们用于处理RTB交换的大型数据集。 实际上,在大多数情况下,他与ClickHouse处于同一领域,但是从历史上看,我们还不能切换到任何一种乐器。

这样的集合看起来似乎很繁重,但首先,Appodeal是一个由多个开发团队和一个项目组成的大型企业集团。 其次,这些都是广告技术的严峻现实-我们不是唯一在一家公司内使用多层堆栈的公司。
您如何看待?
由于数据流很大,因此需要对它们进行排队以进行处理。 作为队列,我们使用Kafka。 这是一个用Scala编写的出色可靠的解决方案,从未使我们失败。
在这种情况下,对用户的唯一要求是,他有时间使不断增长的队列快于增长。 一个简单明显的规则。 因此,出于这些目的,我们主要使用GoLang。 但是,这并不能否定该服务器上的RAM应该足够的事实。
要监视所有这种经济状况,您必须连续监视和委托所有内容。 为此,我们使用:
- / NewRelic /:经过时间考验的解决方案,与Ruby on Rails和GoLang微服务完美集成。 NewRelic唯一的缺点是它的价格。 因此,NewRelic并不是随处可见。 在大多数情况下,我们尝试将其替换为我们自己收集的指标-将其放在Grafana中。
- / Statsd + Grafana /:收集指标是一件好事。 您只需要做的事情就是自己配置所有内容,并立即“重复” NewRelic功能。
- / ElasticSearch + Fluentd + Kibana /:在日志中,我们将所有内容都放在一行中。 从慢速PostgreSQL查询到一些Rails系统消息。 实际上,诸如基于ElasticSearch的Kibana之类的解决方案使您可以方便地在一个地方收集所有日志,然后在其中搜索必要的消息。
- / Airbrake /:这是必填项,它是与消息stacktrace'ami一起收集错误的过程。 我们目前正在与Airbrake一起使用免费解决方案之一。 还是有原因的价格。

您需要了解,正确构建的监控是您的眼睛和耳朵。 盲目无法工作。 您需要查看特定时间在服务器上发生的情况,因此产品的稳定性和可靠性将在很大程度上取决于您构建用于收集和显示指标的系统的能力。
顺便说一句,说到可靠性,我们包含几个用于预发布和检查发布的登台服务器,我们在负载下保持稳定,从而复制了那里的一些实际流量。 每周我们都会在生产和登台之间同步数据库。 这为我们提供了一种“镜像”,使我们能够测试那些无法在本地验证的事物,并在负载测试级别识别问题。
真的那么复杂吗?
事实就是这样。 正如埃隆·马斯克(Elon Musk)在他的书中写道:“我们这一代人的头脑正忙于吸引人们点击广告,” Facebook工程师杰夫·哈默巴赫(Jeff Hammerbacher)告诉我。 “恐怖……” Appodeal的简短清单:
- 我们与两个打广告网络和代理商集成在一起。 在自动模式下,我们在这些网络中注册应用程序,并配置各种参数,以使这些网络以最佳性能运行。 并非每个网络都有相应的API,您必须在某个地方使用机器人来完成。
- 每个网络都向用户支付展示收入,该收入必须得到接收,按各种参数细分并进行处理。 这是不间断的。 再一次,是由机器人在某个地方。
- 为了向用户提供最大的收入,我们“使”网格相互竞争,通过广告优惠构建所谓的“瀑布”。 瀑布图是根据各种指标建立的,例如eCPM(每千次展示的平均价格),我们可以通过多种方式进行预测。 瀑布中的广告报价越高,我们预测的价格就越高。 设备上会根据需要经常提供此瀑布。 正如您可能已经猜到的那样,没有人点击广告,只会让所有人讨厌,这对任何人都不感兴趣。 只有所谓的例外。 来自可口可乐,百事可乐和其他企业巨头的“品牌”横幅广告,它们习惯于无处不在谈论自己。
- 这种交互的一部分建立在所谓的RTB(实时出价)协议上:
在这种情况下,所谓的竞标者在拍卖会上在线进行交易,以获得在选定设备上展示其广告的权利。 一个非常有趣的观点值得单独撰写。 许多交易所(例如Google AdExchange)为服务器响应时间(例如50ms)设置了严格的框架,这带来了性能问题。 在不服从的情况下-罚款数千美元。 这正是用Scala编写的内核与Druid一起完成的工作。
每个猎人都想知道野鸡的位置,我们的客户(像我们一样)想知道向谁展示广告,何时以及为什么。 因此,我们必须对所有拥有的数据堆进行排队(Kafka),然后逐步处理并将其添加到OLAP数据库(ClickHouse)。 许多人认为PostgreSQL在完成这项任务方面不会比任何“时髦”解决方案都要差,但事实并非如此。 PostgreSQL是好的,但是当用于过滤和排序的字段数超过10,并且存储的数据量接近10亿条记录时,用于构造数据访问速度索引的经典解决方案将停止工作。 您只是没有足够的内存来存储所有这些索引,否则将无法更新这些索引。 无论如何,您将无法获得与面向列的分析查询解决方案相同的性能。
结论
在本文中,我试图至少简要地描述我们的工作,如何存储和处理数据。 在评论中告诉我们您使用的堆栈,提出问题和请求新文章-我们将很乐意分享我们的经验。