处理广告工作负载的基础架构方案(第 1 部分)

本文介绍了在不同广告技术平台之间共享的组件,包括广告服务器和出价工具。本文为您提供了实现这些组件的方案。

广告服务器和出价工具通常是具有重叠技术的互补平台。为避免内容重复,本文及其配套文章(第 2 部分)为该系列提供了相关背景信息:

如需简要了解整个系列及其中使用的术语,请参阅构建广告平台(概览)

平台注意事项

在与平台买方或卖方达成交易时,请注意以下方面:

  • 计算平台:此类程序化广告平台包含各种服务,每项服务提供一种或多种功能。请尽早确定是需要容器化部分还是全部功能,以及服务是否必须直接在虚拟机 (VM) 实例上运行。
  • 地理位置:将您的基础架构部署在靠近客户和提供方的位置,有助于减少网络延迟时间。
  • 可再现性:当您在全球不同地区重复使用系统时,始终如一地部署相同基础架构的能力可让整个平台实现一致性。
  • 负载平衡:只靠一台机器无法处理广告技术负载。 请在多台服务器之间分配内部和外部请求。
  • 自动扩缩:广告请求负载会在一天中波动。 您可以通过自动扩缩系统来降低费用并提高可用性。
  • 网络通信:分布式系统会产生通信问题。例如,假设您在俄勒冈州出价,但您的广告管理数据库位于欧洲。这样的话,即使通信由离线同步构成,您可能也不希望通过公共互联网进行通信。

计算平台

Google Cloud 提供了多种运行计算工作负载的方案。请考虑以下选择:

  • App Engine:可用于运行网页界面 (UI) 以省去大部分操作开销。
  • Compute Engine:可用于安装和管理 App Engine 不支持的某些关系型数据库或自定义代码。
  • Google Kubernetes Engine (GKE):可用于设置无状态前端或运行容器化应用。

建议使用以上这些方案,它们通常可以互换。但请记住,您的需求才是最终决定性因素,无论该因素是费用、运营开销还是性能。

Compute Engine 和 GKE 均支持抢占式虚拟机,这种虚拟机通常用于广告技术工作负载以节省费用。但是,抢占式虚拟机的抢占前警告时间只有一分钟,因此您可能希望执行以下操作:

  • 如果使用 Compute Engine,则可以在同一负载平衡器后面驻留两个不同的托管实例组(一个可抢占,另一个使用标准虚拟机)。通过确保其中一组由标准虚拟机组成,您可以确保您的前端始终能够处理传入请求。下图展示了此方法。

    同一负载平衡器中的两个不同的代管实例组

  • 如果使用 GKE,则可以通过在 GKE 集群中创建非抢占式和抢占式节点池来降低可用性费用。

地理位置

广告主可能希望定位全球所有地区的客户。 为平台的一个界面前端增加几毫秒不会影响广告主的体验,例如当其可视化性能数据时。但是请注意,如果因额外的网络距离而使出价响应增加了几毫秒,这几毫秒却可能会影响广告主的出价是否被接受,以及广告是否会投放给客户。

Google Cloud 在多个地区(包括 us-eastus-westeurope-westasia-southeastasia-east)提供服务。每个地区都包含多个区域以提供高可用性和规模。

如果延迟时间很重要,您可能希望在不同地区的各个区域中分布一些服务。您可以根据需要自定义设置。例如,您可以决定在 us-east4us-west1 中分布一些前端服务器,但是将数据存储在 us-central 的数据库中。在某些情况下,您可以在本地将一些数据库数据复制到前端服务器;或者您可以考虑使用多地区 Cloud Spanner 实例

可再现性

可再现性可提供更简单的维护和部署,而在所有相关地理位置上运行平台是在关键截止时间之前返回出价的关键因素。为确保可再现性,每个地区都必须执行类似的工作。主要区别在于工作负载,以及需要多少机器和区域才能通过扩缩满足地区需求。

使用 Compute Engine 时,实例模板是设置类似地区代管实例组的基础。这些实例组可以位于不同的地区,以便靠近 SSP,并且它们可以跨越多个区域以实现高可用性。下图展示了此过程。

使用实例模板设置地区代管实例组

容器提供比机器虚拟化更高级别的抽象。 Kubernetes 使用了能够在不同集群中一致定义 pod服务部署的 YAML 配置文件,从而能原生提高应用的可再现性。

负载平衡

以下是需要负载平衡的两大场景:

如果您决定将 Kubernetes 用于基础架构的某些部分,我们建议您使用 GKE。如果您的提供商本身不支持某些 Kubernetes 功能,则其可能需要一些额外的实现。借助 GKE,Kubernetes 可以使用原生 Google Cloud 功能:

GKE 还支持容器本机负载平衡,以最大限度地减少网络时间和可能的其他网络跃点。在较高级别,负载平衡器会阻止请求被路由到不托管所请求服务的 pod 的实例。

扩缩

由于您的平台必须能够每天解析和计算数十亿的广告请求,因此必须实现负载平衡。此外,一台机器不足以完成这项任务。但是,请求的数量往往会在一天内发生变化,这意味着您的基础架构必须能够按需扩缩。

如果您决定使用 Compute Engine,则可以从实例模板创建自动扩缩代管实例组。然后,您可以针对不同指标扩缩这些组,例如 HTTP 负载、CPU 和 Cloud Monitoring 自定义指标,如应用延迟时间。您还可以针对这些指标的组合扩缩这些组。

自动扩缩决策以过去十分钟内的指标平均值为基准,并使用滑动窗口每分钟制定一次。每个实例组都可以拥有一组自己的扩缩规则。

如果您决定使用 GKE(Kubernetes 的 Cluster Autoscaler),则可以使用 GKE Cluster Autoscaler 原生实现。GKE Cluster Autoscaler 的行为与 Compute Engine Autoscaler 不同,并且当底层节点上的 CPU 或内存不足而无法再在现有节点上调度新的 pod 时,它会启动新节点。当 CPU 或内存再次释放时,缩减功能会自动生效。

网络通信

虚拟私有云 (VPC) 可跨越多个地区。换句话说,如果您在 us-east 中拥有数据库读取副本,在 asia-southeast 中拥有位于同一个 VPC 的主数据库,则它们可以使用其专用 IP 或主机名进行安全通信,而无需离开 Google 网络。

在下图中,所有实例都在同一个 VPC 中,无需 VPN 即可直接通信,即使它们位于不同的地区。

不同地区中的所有实例都处于同一 VPC

GKE 集群在创建时会分配有一个 VPC,并且可以使用许多现有网络功能。

Google 还提供两种类型的网络基础架构:

  • 优质:使用 Google 全球专用网络。为关键工作负载(例如跨地区数据库复制)优先采用此方案。
  • 标准:如果您比较在意价格并且可以使用公共互联网,请优先采用此方案。

当您使用 Cloud Bigtable、Cloud Storage 或 BigQuery 等代管产品时,Google Cloud 通过 VPC 提供对这些产品的专用访问权限

用户前端

您的用户前端非常重要,但由于其处理的工作负载小得多,因此它需要的技术开销最小。用户前端为平台用户提供管理广告资源(如广告系列、广告素材、帐单或出价)的功能。前端还提供与报告工具交互的功能,例如监控广告系列或广告效果。

这两个功能都需要 Web 服务为平台用户提供界面,以及用于存储交易或分析数据的数据存储区。

Web 服务

您的广告前端可能需要:

  • 提供高可用性。
  • 每秒处理数百个请求。
  • 在全球范围内实现可接受的延迟时间,以提供良好的用户体验。
  • 提供界面。

您的界面应提供一个信息中心,以及用于设置广告主、广告系列及其相关组件的页面。界面设计本身是一门独立的学科,不在本文的讨论范围内。

为了最大限度地减少技术开销,我们建议将 App Engine 用作前端平台。这有助于最大限度地缩短您管理网站基础架构的时间。如果您需要自定义运行时,请考虑使用自定义运行时。或者,如果您的首选应用堆栈有其他要求,则可以使用 GKE 或 Compute Engine。

数据存储区

用户前端有两种数据存储区类型:

处理请求

前端

请求被发送到您的平台提供的 HTTP(S) 端点以进行处理。关键组件如下所示:

  • 能够处理几十万 QPS 的负载平衡器。
  • 能够根据各种 KPI 快速扩缩的实例池。
  • 能够对端点进行限制和/或身份验证的 API。

Compute Engine 和 GKE 都是用作计算平台的优秀方案:

  • Compute Engine 使用扩缩部分提到的 Cloud Load Balancing 和代管实例组。
  • GKE 使用 Cloud Load Balancing 和 Ingress(或 Istio Ingress Gateway)、Horizontal Pod Autoscaler 和 Cluster Autoscaler。

由于 pod 扩缩比节点扩缩更快,因此 GKE 可以在服务层级上提供更快的自动扩缩。GKE 还支持容器本机负载平衡以优化请求,使其直接路由到托管相关服务的 pod 的实例。

您可以使用 ApigeeService Infrastructure 等技术管理限制和身份验证。

解析

广告请求通常采用 JSON 或 protobuf 格式,其中包含 IP 地址、用户代理或网站分类等信息。提取此数据至关重要,因为这些数据可能还包含(唯一身份)用户的详细信息,可用于检索要选择和过滤广告的细分。

静态过滤

买方收到的一些请求通常可以使用静态规则来舍弃。这种早期过滤可以减少数据量和在下游需要进行的复杂处理。

规则可能是发布商的黑名单或是排除某些内容类型。在初始化阶段,工作器可以从 Cloud Storage 上托管的文件中拉取和加载这些规则。

广告选择

广告选择可以在各种服务或平台中执行,包括:发布商广告服务器、广告主广告服务器或 DSP。选择广告时有不同程度的复杂性:

  • 有些选择可能像为发布商的网站或页面的特定类别选择广告一样简单。在这种情况下,每个广告的价格没有区别。
  • 更高级的选择包含用户属性和细分,并可能涉及基于机器学习的广告推荐系统。
  • 实时出价系统通常会做出最复杂的决策。广告根据(唯一)用户细分和以往出价等属性进行选择。 选择还包括出价计算,以根据请求优化出价。

选择浏览者感兴趣的广告是系统的核心功能。这要求您考虑许多因素,包括基于规则的高级算法或机器学习选择算法。但是,本文将继续重点关注高级流程以及不同数据存储区的交互。

广告选择流程包括以下步骤:

  1. (唯一)用户个人资料存储空间中检索与目标用户关联的细分。
  2. 选择与用户细分匹配的广告系列或广告。此选择需要从元数据管理存储空间中读取元数据,这就是此存储空间要求您实现读取密集型存储模式之一的原因。
  3. 根据指标过滤所选的广告系列或广告,例如存储在其中一个上下文存储空间的剩余预算。
  4. 选择广告。

出价工具涉及到更多与出价和拍卖相关的步骤,并且它们有更苛刻的延迟时间要求。如需详细了解广告选择期间出价工具要求,请参阅实时出价工具的基础架构方案(第 4 部分)

读取密集型存储模式

选择广告时做出的大多数决策都需要智能数据做到以下几点:

  • 以毫秒为单位,甚至是以亚毫秒为单位读取。
  • 尽快写入,特别是对时间敏感的计数器。
  • 经常作为使用后台分析的离线流程或机器学习任务的一部分写入。

如何选择数据存储区取决于如何确定以下要求的优先级:

  • 最大限度地减少读写延迟时间:如果延迟时间很关键,则您需要一个靠近服务器,并且可以处理大规模快速读写的存储空间。
  • 最大限度地减少操作开销:如果您拥有一个小型工程团队,则可能需要一个全托管式数据库。
  • 扩缩:为了支持每天数百万目标用户或数十亿事件,数据存储区必须能够横向扩缩。
  • 调整查询样式:某些查询可以使用特定键,其他查询可能需要检索满足不同条件的记录。在某些情况下,查询数据可以编码到键中。在其他情况下,查询需要类似 SQL 的功能。
  • 最大限度地提高数据新鲜度:必须尽快更新某些计数器,例如预算。其他数据(如细分受众群)或计数器(如每日上限)可以稍后再更新。
  • 最大限度地降低费用:通过单个数据库每天处理数十亿次读写操作,同时保持全局高度一致性,以求最大限度地减少 DevOps 成本,并不总是经济的或者可能的。

有很多不同的方案可以解决读取密集型要求,包括读取副本、缓存系统、内存 NoSQL 数据库和托管宽列 NoSQL 数据库。

RDBMS 读取副本

使用 Cloud SQL(或在 Compute Engine 上安装和管理的等效 RDBMS)时,可以从主实例分流读取。许多数据库本身都支持此功能。工作器可通过以下方式查询所需信息:

  • 使用与工作器数量匹配的只读副本。
  • 使用池代理。

下图展示了此过程。

从主实例分流读取所在的数据库

读取副本旨在提供读取密集型流量,但可扩缩性不是线性的,且性能可能会受到大量副本的影响。如果您需要实现可扩缩、具有全局一致性和最小操作开销的读取或写入操作,请考虑使用 Cloud Spanner

本地缓存层

您可以在 Compute Engine 上使用 Redis 等缓存层,并在工作器上使用可选的本地副本。这一层可以极大地减少读取和联网的延迟时间。下图展示了该层。

利用缓存层最大限度地减少延迟时间

如果您决定在这种情况下使用 Kubernetes,请查看 DaemonSet 和相似性以确保:

  • 复制数据的数量存在限制。
  • 数据仍然靠近服务 pod。

内存键值 NoSQL

部署内存数据库(如 Aerospike 或 Redis)以实现大规模快速读取操作。此解决方案对于地区性数据和计数器很有用。如果您担心存储的数据结构的大小,您还可以利用可写入 SSD 磁盘的内存中数据库。下图展示了此解决方案。

可写入 SSD 磁盘的内存数据库

代管式宽列 NoSQL

宽列数据存储区是可以提供大规模快速读写操作的键值存储空间。您可以安装常见的开源数据库,如 Cassandra 或 HBase。

如果您决定使用此类存储空间,我们建议您使用 Cloud Bigtable 来最大限度地减少操作开销。这些存储空间支持您根据节点数量线性调整输入/输出操作 (IOP)。通过正确的键设计,广告选择器和数据流水线能以个位数毫秒级的速度分别读取和写入 PB 级数据的第一个字节。下图展示了此过程。

宽列数据存储空间

静态对象存储

对于能够以 protobuf、AVRO 或 JSON 格式保存的静态数据,工作器可以在初始化阶段从 Cloud Storage 加载这些数据,并将内容保留在 RAM 中。下图展示了此过程。

从 Cloud Storage 加载数据

没有一种解决方案能够适用于所有情况。请根据您的优先考虑因素选择解决方案,并平衡延迟时间、费用、操作、读/写性能和数据大小。

解决方案 延迟时间 费用 运营开销 读/写性能 数据大小
RDBMS 读取副本 毫秒 基于服务或计算 受限 受限
Cloud Spanner 毫秒 基于服务 根据节点数线性调整 PB 级
内存存储空间 亚毫秒 基于计算 根据节点数调整 根据节点数调整
Cloud Bigtable 个位数毫秒 基于服务 根据节点数线性调整 PB 级

广告数据存储区

本部分介绍了适用于三种不同场景的数据存储空间方案:

  • 广告投放存储空间适用于与广告选择相关的服务。 处理工作负载需要低延迟时间和每天处理数十亿次读取的能力。数据大小取决于数据类型。
  • 分析型存储空间适合临时查询或批量数据流水线离线使用。这种存储空间支持每天存储数百 TB 的数据。
  • 报告/信息中心存储空间可用于预制信息中心、时间序列或自定义报告。这些方案让您的前端与众不同,您的平台用户能够快速获得数据洞见并将业务表现可视化。

广告投放存储空间可以进一步细分,具体如下:

元数据管理存储空间

元数据管理存储空间包含在进行广告选择时应用规则的引用数据。这里存储的某些资源为特定于平台的资源,但其他资源可能会重叠:

  • 对于卖方广告服务器,发布商管理有关广告系列、广告素材、广告主、广告位和价格的数据。一些前端也可能为其买方授予访问权限。
  • 对于买方广告服务器,买方管理有关广告系列、广告素材、广告主和价格的数据。广告主通常可以通过界面自行更新此数据。
  • 对于 DSP,买方管理有关广告系列、广告素材、广告主和出价的数据。广告主通常可以通过界面自行更新数据。

元数据存储空间包含关系型或分层半静态数据:

  • 写入是平台用户通过前端修改的结果,不经常发生。
  • 广告选择服务器每天读取数十亿次数据。

对于用户前端,广告系列元数据数据库必须能够管理资源关系和层次结构,并存储 MB 至 GB 级的数据。该数据库还必须提供数百 QPS 范围内的读写操作。为满足这些要求,Google Cloud 提供了多个数据库方案(包括代管方案和非代管方案):

  • Cloud SQL:可以运行 MySQL 或 PostgreSQL 的全代管式数据库服务。
  • Datastore:高度可扩缩的代管和分布式 NoSQL 数据库服务。它支持 ACID 事务,提供类似于 SQL 的查询语言,并具有高度一致性和最终一致性级别
  • Spanner:可横向扩缩的关系型数据库,提供高度一致的读取、全局事务和跨地区复制。它可以处理读写密集型操作。
  • 自定义:您还可以在 Compute Engine 或 GKE 上安装和管理许多开源或专有数据库(如 MySQL、PostgreSQL、MongoDB 或 Couchbase)

您的具体要求有助于缩小方案范围,但概括来讲,您可以使用 Cloud SQL,因为它支持关系型数据。Cloud SQL 为托管式服务,并提供读取副本方案。

如前所述,元数据存储空间不仅可供平台用户进行报告或管理,还可供服务器选择广告。这些读取操作每天执行数十亿次。有两种主要方法可以满足读取密集型要求:

  • 使用可以处理全局一致写入、每天执行数十亿次读取的数据库,如 Spanner。
  • 分离读取和写入。这种方法是可行的,因为元数据不会经常更改。如需详细了解此方法,请参阅导出(在第 2 部分中)。

(唯一)用户个人资料存储空间

此存储空间包含(唯一)用户及其关联信息,可提供根据要求选择广告系列或广告的关键数据分析。该信息可以包括(唯一)用户的属性、您自己的细分或从第三方导入的细分。在实时出价中,导入的细分通常包含出价建议。

此数据存储区必须能够存储数百 GB、甚至 TB 的数据。此数据存储区还必须能够以最多个位数毫秒的速度检索单个记录。您存储的数据量取决于您的(唯一)用户信息的详细程度。您至少应该能够检索目标用户细分列表。

存储空间会根据(唯一)用户与广告、访问过的网站或所采取的操作的互动而频繁更新。信息越多,定位效果就越好。您可能还希望使用第三方数据管理平台 (DMP) 来丰富您的第一方数据。

Cloud Bigtable 或 Datastore 是用于(唯一)用户数据的常用数据库。这两个数据库都非常适合单个记录的随机读写。仅当您至少拥有数百 GB 的数据时,才建议使用 Cloud Bigtable。

其他常见的数据库,如 MongoDB、Couchbase、Cassandra 或 Aerospike 也可以安装在 Compute Engine 或 GKE 上。虽然这些数据库通常需要更多管理,但有些可以提供更好的灵活性并可能缩短延迟时间,在某些情况下还可以提供跨区域复制。

如需了解详情,请参阅用户匹配(在第 4 部分中)。

上下文存储空间

上下文存储空间通常用于存储计数器,例如频次上限和剩余预算。在上下文存储空间中,数据刷新的频率各不相同。例如,每日上限可以每日传播,而其他广告系列预算则需要尽快重新计算和传播。

根据您选择的存储模式、更新的计数器以及所选数据库的功能,您可以直接写入数据库。或者,建议在消息传递系统(例如 Pub/Sub)中使用发布-订阅模式来分离实现,以在计算后更新存储空间。

上下文存储空间适用于以下情况:

  • Cloud Bigtable
  • 区域型内存键值 NoSQL 模式
  • 地区型缓存模式

通过使用横向扩缩,这些存储空间可以处理大规模写入和读取。 广告服务器的基础架构方案(第 3 部分)实时出价工具的基础架构方案(第 4 部分)将更详细地讨论其中一些方案。

如何在分布式环境中管理预算计数器的示例

使用广告系列管理工具可以设置预算。您不会希望广告系列超支,因为在大多数情况下,广告主不会为这些额外展示付费。但是,在分布式环境中合并计数器(例如剩余预算)可能很有挑战性,尤其是当系统每秒可以接收数十万个广告请求时。如果全局剩余预算未快速合并,那么广告系列可能会在几秒钟内迅速超支。

默认情况下,工作器在使用部分预算时并不了解其他工作器的开销。缺乏通信可能导致工作器支出时预算已耗尽。

有很多不同方法可以解决这个问题。以下两个方案都实现了全局预算管理器,但它们的行为方式不同。

  1. 通知工作器预算耗尽:预算管理器跟踪支出并在广告系列预算耗尽时向每个工作器推送通知。由于存在 QPS 水平较高的可能性,通知应在一秒内推送,以便快速限制超支。下图展示了此过程的工作原理。

    预算耗尽通知

  2. 为每个工作器定期分配预算额度:预算管理器将剩余的总预算分成较小的额度,并分别分配给每个工作器。工作器花费自己的本地预算,当预算耗尽时,它们可以申请增加预算。此方案具有以下优点:

    1. 在能够再次支出前,工作器需要等待预算管理器为其分配新的额度。即使一些工作器会闲置一段时间,这种方法也可以避免超支。
    2. 预算管理器可以根据工作器在每个周期内的支出行为调整发送给每个工作器的分配额度。下图展示了此过程。

      预算管理器为每个节点分配预算

无论您选择哪种方案,预算计数器均根据发布商和广告主商定的价格模型进行计算。例如采用以下模型:

  • 基于 CPM,一次可计费展示会向系统发送一个事件,该事件会根据每千次展示费用减少剩余预算。
  • 基于 CPC,一次用户点击会向系统发送一个事件,该事件会根据每次点击费用减少剩余预算。
  • 基于 CPA,广告主媒体资源上的一个跟踪像素会向系统发送一个事件,该事件会根据每次操作费用减少剩余预算。

展示次数通常比点击次数高几个数量级, 而点击次数通常比转化次数高几个数量级。提取这些事件需要一个可扩缩的事件处理架构。数据流水线一文更详细地讨论了这种方法。

分析型存储空间

分析型存储空间是一个用于离线存储和处理 TB 级每日提取数据的数据库;换句话说,其存储和处理过程不在任何实时处理过程中。例如:

  • 离线处理(唯一)用户数据以确定相关联的细分,这些细分又被复制到更快的数据库(例如用户个人资料数据库)以提供服务。导出部分介绍了这个过程。
  • 使用展示和(唯一)用户操作联接请求,以便合并上下文存储空间中使用的离线计数器。

报告/信息中心存储空间

报告/信息中心存储空间用于用户前端,以提供有关广告系列或广告资源效果的数据分析。报告有不同的类型。您可能希望提供部分或全部内容,包括自定义分析功能和每隔几秒或实时更新的半静态信息中心。

您可以使用 BigQuery 的分析功能。如果您利用视图来限制数据访问并与客户进行相应的共享,则可以通过自己的界面或平台用户的可视化工具为其提供临时分析功能。并非每家公司都提供此方案,但它是能够为您的客户提供的一个很好的补充方案。针对这种使用场景,请考虑使用固定价格

如果您希望以毫秒至秒的延迟时间向客户提供 OLAP 功能,请考虑在 BigQuery 之前使用数据库。您可以合并数据以生成报告,并将其从 BigQuery 导出到您选择的数据库。关系型数据库(例如 Cloud SQL 支持的数据库)通常用于此目的。

由于 App Engine 或任何其他前端使用服务帐号代表用户执行操作,因此 BigQuery 会将查询视为来自单个用户。结果是 BigQuery 可以缓存一些查询并更快地返回之前计算的结果。

半静态信息中心也很常用。这些信息中心使用由数据流水线流程编写的预合并 KPI。存储空间很可能是基于 NoSQL 的,例如可以更轻松进行实时更新Firestore,或 Memorystore 等缓存层。数据新鲜度通常取决于更新的频率和用于合并数据的窗口的持续时间。

后续步骤