广告业务云原生改造:解决百万 QPS 下稳定性和效益问题
本文将基于火山引擎客户服务实践和字节跳动内部技术实践,介绍利用云原生技术稳定支撑百万 QPS 广告业务、在云资源消耗上实现极致性价比。
广告投放是线上营销的重要路径,近年来,随着线上消费稳步增长,数字营销快速发展。根据《2023 中国互联网广告数据报告》,2023 年,中国互联网广告市场规模已经达到了 5732 亿元,大数据、人工智能、云计算、物联网等技术在广告领域的应用不断加强,革新着传统广告生产、投放、互动、监管模式。
目前,国内互联网营销行业针对每个细分的领域都有不同主体/平台在运营,这些细分环节包括广告创意、广告代理及投放、广告交易平台、舆情监测等。每个环节都需要高度协同与精细化管理,以确保广告活动的成功实施。下图是一个典型的广告业务流程:
SSP(Sell-Side Platform,供应方平台):媒体方可以在 SSP 上管理自己的广告位,控制流量分配、价格、筛选等;
ADX(Ad Exchange,广告交易平台):将媒体方流量以竞价的方式卖给 DSP,从而让广告主在对的时间、对的地点接触到对的用户;
DSP(Demand Side Platform,需求方平台):广告主服务平台,广告主可以在平台上设置广告的目标受众、投放地域、广告出价等;
DMP(Data-Management Platform,数据管理平台):整合各方数据并提供数据分析、数据管理、数据调用等能力,用于指导广告主进行广告优化和投放决策。
从广告主提交广告创意和预算,到根据需求筛选目标受众,再到竞价和广告投放,在整个链路中,广告业务需要处理海量的数据、进行复杂的算法计算和实时的数据分析,这对底层技术架构和计算能力提出了巨大挑战。此外,广告业务的季节性和周期性波动也需要灵活的资源调配和快速的业务响应能力。
同时,近年来随着广告市场的竞争加剧,广告主对于广告投放的精准度、效果评估和实时优化的要求越来越高,程序化广告(Programmatic Advertising)的出现也带来了全新的场景。所谓程序化广告,即广告主基于数字平台提供的数字算法和数据技术,从受众匹配的角度由程序自动化完成展示类广告的采买和投放,并实时反馈投放分析的一种自动化广告投放方式。它能使广告投放更有效率和更有针对性,从而提高广告效果和回报率,但会在实时竞价、配对交易环节算法等环节产生大量的云计算资源消耗。
程序化广告业务盈利的核心目标,在于最大限度承接媒体流量的同时,尽可能多地促成广告创意实际投放。一个标准的广告业务,通常具备以下几个特点:
媒体和流量方繁杂多样,QPS 通常在几万到几十万之间,对请求时延较为敏感,时常会出现一些突发性的短时流量。部分大型媒体方在检测到时延未达预期时,会即刻切断流量,从而导致流量损失;
业务流量整体呈现出显著的周期性特征,往往与人们的作息时间相关。一般而言,晚饭后流量达到高峰,凌晨则回落到谷底;
在广告业务链路中,上下游服务相互依存,整个流程需在 100 毫秒内完成。在一些突发流量的场景下,部分服务的业务表现会对上下游服务的性能产生影响。
在广告业务场景中,请求总量一般可达几万甚至百万 QPS,高性能入口网关对于业务是至关重要的,同时广告业务还对流量入口提出了如下挑战:
如何动态管理大量路由规则:广告业务对接来自不同 APP 渠道的请求,需要根据业务诉求进行动态流量切分,要求入口网关能够方便快捷地管理流量规则;
如何确保入口流量时延和稳定性:在业务容器化部署的场景下,需要简化访问链路、降低时延,并且能够对入口的稳定性提供有效保障;
如何应对潮汐流量变化:在流量存在潮汐峰谷的情况下,需要入口网关既能应对流量突增,同时也能与后端容器侧的动态扩缩容有机结合。
广告业务场景通常会为来自不同媒体方的流量分配不同域名,方便进行流量的管理和观测。如果我们希望为这些域名配置更为精细化的流量规则,不可避免地会产生大量路由配置。因此广告业务的入口网关需要在结构设计上就从域名维度对不同入口加以区分,但传统的七层负载均衡方案,如 Nginx,通常将规则写入到大量的配置文件中,无法在域名维度进行精细化管理。
针对上述问题,火山引擎 API 网关 APIG(API Gateway)在管理维度上支持从域名维度对不同入口进行区分,实现业务的逻辑隔离,清晰定义不同域名的路由信息,同时在每个域名下的不同路由做到完全隔离,便于用户基于不同媒体方的需求进行路由规则配置。
管理大量路由带来的另一个问题是如何解决不同路由中规则互相包含造成的路由优先级冲突问题,例如除了针对单一域名的路由规则外,用户还希望能有全局生效的兜底路由,这时就需要能够将兜底路由设置为较低的优先级。APIG 能够提供根据域名和路径最长匹配优先、根据 header 或 query string 的匹配数量优先和自定义路由优先级等精细化路由排序的策略,通过自定义路由优先级+精准匹配,在大量路由的场景下保证路由生效规则符合用户业务要求。
同时 APIG 还支持路由配置的按需加载机制,即只更新发生变化的路由配置,而无需推送全部路由配置。在配置了大量路由的环境下能够显著提升路由的生效时间。
广告业务对接口返回的时延有严苛的要求,在实时竞价场景中对接口的超时时间可能只有几十毫秒。因此就要求尽可能在流量路径中减少不必要的延时。在传统的 API 网关产品中,由于没有和 Kubernetes 服务发现打通,需要将请求转发到容器集群的 Ingress,再由 Ingress 转到工作负载中。
APIG 作为新一代云原生网关,能够自动同步 Kubernetes 容器服务的地址信息,直接将请求转发工作负载中,不需要经过额外的网络路径。同时 APIG 在请求转发时能够考虑网关实例和后端工作负载的亲和性,网关实例会将请求优先转发到自身所在的 AZ 工作负载中,从而减少跨 AZ 带来的延时。
对于包括广告业务在内的在线业务,确保业务的稳定性是所有工作的基石。在入口网关的场景下除了依靠网关服务本身提供的多 AZ 高可用能力,还可以通过网关和应用服务的多活部署架构提升整体服务的稳定性和可用性。
由于广告业务的流量和用户的使用频次高度相关,具有周期性波动以及突增流量的特点,其流量表现在单日周期内具有明显的波峰波谷,在流量快速增加时,需要入口网关也具备快速扩容的能力,避免网关成为性能瓶颈。
Kubernetes 默认的 HPA 策略会根据当前指标和期望指标来计算扩缩比例,当网关负载达到了 HPA 水位后,会秒级快速进行扩容动作。但默认的 HPA 策略在单次扩容上并不足够积极,直接导致流量集中涌入新扩容的 Pod,引起 session limit 的问题导致连接失败,进而引起流量波动。针对这个问题,网关基于智能 HPA 方案提前预测流量趋势并实现资源扩容,可以帮助广告系统在特定时间段的流量高峰(如节假日、促销活动或重大事件)场景,确保用户体验不受影响。
由于流量存在周期性变化,后端服务也会进行相应的弹性伸缩,在一些广告业务的工程实践中,服务端会采用懒加载、异步线程池等方式优化响应速度,但这种优化会导致 Java 进程初始化开销较大、处理能力小于已经完全启动的 Pod。如果直接使用经典的轮询负载均衡算法,会将大量的请求转发到新启动的 Pod 上,可能导致新启动的 Pod 负载过高、延时增大、业务后端负载长期不均衡等其他问题。
由于广告业务具有周期性特点,其后端服务天然存在随着业务潮汐规律进行弹性伸缩的需求。在传统基于 ECS 的弹性伸缩架构下,会碰到几个较大的难题:
在自运维场景中,频繁的弹性伸缩无法解决节点之间的负载均衡问题,最终致使节点装箱率偏低,造成大量资源浪费;
标准的 CA、HPA 方案均为被动触发,扩缩过程耗时较长,而且容易引发业务抖动,导致流量损失;
业务运行过程中,面对业务压力的正常变化,在运维人力有限的情况下,如何保障实例的常态化稳定运行。
在传统的 Kubernetes on ECS 方案中,服务工作负载的均衡调度与弹性伸缩极度依赖系统运维人员的经验和专业能力,通常难以在保障系统稳定性和提高资源利用率之间达成平衡,最终只能依靠堆砌资源来确保系统稳定运行。在广告场景中,资源需求的波峰波谷差距能达到数倍,这极易导致大量资源浪费。
Serverless 容器方案的架构设计从本质上就能解决这个问题,用户无需在意单节点的资源利用率,可按需使用容器资源。Serverless 容器具备弹性计算和 Kubernetes 编排能力,能通过秒级启动、高并发创建的能力满足广告业务的动态弹性伸缩需求。同时,用户仅需为业务实际运行所需的资源付费,大幅降低了资源的浪费。
以火山引擎 Serverless 容器 VCI(Volcengine Container Instance)为例,VCI 的弹性能力能够满足分钟级数万核 CPU 计算资源的需求,确保在业务需要时,快速弹出充足的计算资源。当流量洪峰结束,业务工作负载降低时,也能迅速释放弹性计算资源。此外,VCI 在设计之初就围绕企业“云成本控制”的需求,着重按照实际使用资源量进行精细化计费。一方面,按照计算资源实际使用情况进行秒级按量计费;另一方面,相较于传统计算资源,VCI 能够减少闲置资源、提高装箱率,进而降低用户的计算资源使用成本。
受限于传统 HPA 指标弹性伸缩的局限性,业务的弹性伸缩往往不够及时,在业务压力突增的场景下,容易出现后端实例因压力过大被打爆,导致流量损失的情况。从火山引擎云原生团队的实践经验来看,目前通过 Serverless 容器和智能水平自动扩展 iHPA(Intelligent Horizontal Pod Autoscaling)能力有机结合,可以较好地应对这一场景。
基于资源画像的预测能力,结合工作负载历史数据和实际资源消耗情况,为部署至 VCI 的工作负载提供前置扩缩容的能力,满足精细化扩缩容的业务诉求。对于广告业务而言,VCI + iHPA 方案能够帮助广告系统在特定时间段的流量高峰(如节假日、促销活动或重大事件)场景下,提前预测流量趋势并实现资源扩容,确保用户体验不受影响。同时,通过 iHPA 精确化分析实际资源需求并动态调整 VCI 资源分配策略,也能够避免在流量低峰期过度消耗资源。
iHPA 方案的核心能力是将预测数据转化为 External Metrics 触发 HPA 伸缩 。下图为其典型的工作流程设计:
iHPA 自身提供对于 Metric 的配置,Metric 配置完成后,Controller 会将每一个 iHPA Metric 转换为两个 HPA Metric,分别对应实时指标和预测指标。
对于实时指标来说,其会直接透传到 HPA Metric;
对于预测指标来说,iHPA Controller 会进行转换:
预测指标类型均为 External,即由 Metric Adapter 提供;
预测指标名称均相同,通过标签区分不同类型的预测指标;
预测指标的值类型只会为 Value 或 AverageValue,对于 AverageUtilization 类型,iHPA Controller 会将其换算为 AverageValue 类型。
◇ 时间序列预测配置下发
iHPA CRD 中会记录时序预测算法的相关配置信息,这些信息在 iHPA Controller 创建或更新 TimeSeriesPrediction 时会被传递和补全。
对应于 iHPA 中的每一个指标,iHPA Controller 会在 TimeSeriesPrediction 中自动生成 PromQL,Predictor Controller 会通过该 PromQL 查询指标的历史数据,并根据预测算法配置请求预测推理服务,最终预测数据会保存在 TimeSeriesPrediction CRD 中。
◇ 时间序列预测数据刷新
TimeSeriesPrediction CRD 中包含了时序预测算法的配置和查询指标历史数据的 PromQL,其中包括两个自动填充的配置:预测时间段和刷新时间,预测时间段指时序预测算法预测未来数据的时间段长度,其会被设置为 1 天,刷新时间指从预测时间段内的第一个时间节点开始算起的刷新预测时间时间,刷新预测数据将重新拉取指标历史数据并调用时序推理服务,将新产生的数据覆盖到 CRD 中,使得 TimeSeriesPrediction CRD 总会包含当前时间的预测数据,其值通常会被填充为预测时间段的时长的 1/2 或 1/3。
考虑到 iHPA 需要在负载的波峰到达前触发扩容,因此 TimeSeriesPrediction CRD 会将预测数据在时间轴上整体左移,从而将预测出的波峰指标提前提供给 HPA Controller,再由 HPA Controller 按照 HPA 的既定逻辑对负载进行伸缩。
在资源能够快速弹性并提供极致性价比的场景下,能否为业务提供充足的稳定性也是许多企业所关心的问题。广告业务常常需要应对线上营销活动所产生的流量尖刺,这些流量尖刺一方面会给每个业务实例带来突发的网络负载增长,另一方面也会对服务发现和流量转发等中心服务节点造成巨大压力。通过 Serverless 容器 VCI 的方案,我们能够从底层提供完备的稳定性保障措施,无需运维人员介入,从而降低用户的运维成本。
在单个业务实例层面,VCI 提供了网络性能突发能力,每个实例除了能享受根据规格而分配的基础带宽、IOPS 性能以外,在面对突发性流量时还能够短时间突破这些限制以承载超额流量,从而在 HPA 扩容新节点的过程中保障服务不被限流。在 CPU 处理能力上,VCI 可以通过设置容器的 limit&request 额度以合理分配单个实例内的计算资源,比如业务容器仅配置 request 而辅助容器(如日志采集、服务注册等 sidecar 容器)配置 limit 与 request 的策略,当业务容器算力紧张时可以通过抢占辅助容器算力来短暂过渡。
在中心服务节点层面,CoreDNS 组件是 VKE 集群域名解析服务器组件,为集群内部提供服务发现及域名解析服务,当业务流量爆发时,CoreDNS 将会承载大量业务流量依赖,稳定性极其重要。VCI 提供了对 NodeLocal DNSCache 缓存方案的支持,在开启 VKE 的 node-local-dns 组件后,VCI 实例也会获得本地缓存 DNS 的能力,从而大幅降低中心 DNS 节点的压力。此外,VKE 也为 CoreDNS 提供了丰富的稳定性能力和最佳实践,支持提供了通过 Prometheus 监控 CoreDNS 关键指标、通过日志服务收集 CoreDNS 日志,用户可以通过这些指标和日志对业务流量峰值所需的容量做好预估,并在下次峰值到来前调整合适的 CoreDNS 副本数以做好应对。
在广告业务中,每次调用都对应一次广告投放,流量请求的失败将直接造成业务损失,因此在资源弹性扩容过程中,如何保证流量无损至关重要。
和大多数在线业务类似,广告业务一般使用 Java 语言构建,Java 运行时生命周期要经过五个阶段,首先是 JVM 虚拟机的初始化阶段,然后是 App init 应用的初始化阶段,再经过 App active(warmup)的应用预热时期,在预热一段时间后进入 App active(steady)达到性能巅峰期,最后应用结束完成整个生命周期。可以发现在预热阶段 Java 应用的处理能力是远小于稳定状态的,但是在 Kubernetes 的 Pod 启动时、如果使用常见的负载均衡算法、都会向新启动 Pod 发送一定比例的流量,有可能造成 Java 应用在预热阶段就接收到大量请求,导致这部分请求的响应时间增加甚至失败。
为了确保实例在上线后的一段时间内,流量能够更加平稳地接入,从而保证其在 Kubernetes 弹性伸缩和更新场景中的高稳定性。火山引擎微服务引擎 MSE(Microservices Engine)的治理中心特别实现了流量预热功能。在这个功能的帮助下,实例可以在上线前逐渐增加流量,从而让系统更加平稳地适应新的负载。同时,小流量也可以帮助用户发现服务上线后可能存在的问题,例如资源不足、连接数过多等。
广告业务存在瞬时高并发流量,突发的流量很容易造成服务实例负载过高、请求延迟增大导致服务雪崩,引发业务大面积异常。微服务引擎 MSE 提供限流能力,会将服务无法正常响应、超出限流阈值的流量丢弃,保证在业务突增、恶意攻击等情况下,防止系统负载持续升高导致系统崩溃或无法响应。同时 MSE 还提供了热点参数限流能力,仅对包含热点参数(如 HTTP Header 中携带的 userId)调用限流生效,可以在屏蔽恶意用户、为特定业务渠道提供高优保护等场景中提供更精细化的限流设置。
在广告业务实际生产调用时, 下游服务的实例难免会过载或报错产生,当下游出现问题时,如果上游继续对其进行调用,会影响下游的恢复引发雪崩,也浪费了上游的资源。MSE 提供熔断能力,在发起服务调用时,如果被调用方返回的错误率超过一定阈值,那么后续的请求将不会真正发起,而是在调用方直接返回错误;同时,当下游服务恢复时,会逐步开放对该服务的请求访问,当成功请求达到一定阈值后,将关闭熔断操作进行正常请求访问。
结合前文对广告业务架构的剖析,其架构特点表现在延迟敏感、流量波动剧烈。广告业务每时每刻都有大量用户请求,且每笔请求都具有较高的业务价值,因此对云原生场景下的故障定位和系统性能瓶颈分析提出更高要求。
对于云原生观测体系构建,业内给出 Prometheus + Grafana + SkyWalking 等开源套件,但它们都无法为广告业务提供高吞吐、低延迟、海量数据的一站式观测、排障定位能力,由于业务对于响应延迟要求极高,通过 Java agent 探针注入的方式将明显增加业务延迟。
火山引擎云原生团队结合多年在 eBPF 可观测性的沉淀,推出一站式全栈可观测 VKO 平台,利用 eBPF 技术运行效率媲美内核原生代码,能以极低的消耗获取丰富的网络性能数据,并且无需业务重新部署即可完成观测接入,适用于无法频繁发布变更的有状态应用,真正实现业务无侵入、极低性能损耗。同时基于 eBPF,我们也实现了 L3/L4 层相关网络性能指标,基于网络协议数据流生成服务依赖流量拓扑数据,并可针对特定观测节点的网络抓包,适用于容器复杂网络观测。
VKO 通过整合容器基础资源观测、控制面组件观测、容器网络观测、应用层流量观测、AI Infra 观测等容器场景下全栈观测能力,能帮助用户构建清晰的排障路径,提升排障效率,辅助用户事前快速感知问题、事中高效定位问题、事后问题根因分析。
服务在特定时间段内慢响应是广告业务日常运维的一个突出问题,它可能由应用服务、中间件、网络流量、资源负载等故障点或其他原因综合导致,如果不熟悉业务服务依赖关系,技术团队将很难给出高效、准确的定位路径。下面我们通过一个典型的服务慢请求排查说明使用 VKO 快速定位故障根因的能力。
1. 告警中心触发了 rating-v1 的响应时间长的告警,响应时间已超过 2 秒。
2. 查看 rating-v1 服务端视角的接口调用响应时间,发现服务自身的响应时间正常。
3. 查看从上游客户端访问 rating-v1 的响应时间,发现响应时间较长,超过 2 秒,可以说明问题并不在应用层接口逻辑本身,而是可能发生在客户端侧的网络层面,我们可以继续往下排查。
4. 进一步查看应用指标和网络指标,发现 TCP 重传和建连耗时指标异常,定位到是网络问题引发的服务响应慢问题。
如下图所示,当 productpage、review、rating 多个资源相互访问的耗时长时,通过服务拓扑的依赖关系,便能够发现调用链路是 loop-productpage → productpage → review → rating,问题大概率是 review → rating 的访问耗时高导致,之后便可以将故障排查集中在 review → rating 这一段。
最后,我们以一个真实的火山引擎企业用户场景为例,展示上文提到的云原生能力是如何应用在广告业务中发挥价值的。
用户背景:该用户是一家以互联网程序化营销为核心的广告交易平台供应商,作为媒体方和广告主之间的桥梁,服务上下游,综合提供 SSP、ADX、DSP 等平台能力。
业务模型:SSP 平台需要承接全网流量,满足日均 200 亿请求,峰值 60 万 QPS 的媒体流量接入,同时服务广告主,完成广告竞价、投放决策以及广告投递。
用户需求:
资源:广告业务有明显的波峰波谷,通常在夜间资源需求降到最低,峰谷差距约 3 倍左右,峰值 CPU 需求可到 3600c,低谷只需 1200c,如果使用传统的固定机器模式,需要按照峰值资源要求占比 80%考虑,至少需要准备 4500c 的常驻资源满足业务需求,有大量的资源长时间处于浪费状态。
弹性:在广告业务中,经常性会出现一些突增流量的场景,在激增流量压力的情况下,业务需保持稳定并尽可能处理外部请求,同时能够快速扩容底层资源,承载全部外部流量请求。
最终效果:用户将业务搬迁至火山引擎后,首先在流量入口层使用了火山引擎自研的高性能网关 APIG,通过 APIG 完成了流量入口及路由的统一管理,实现了路由优先级配置、路由规则灵活上下线、黑白名单等一系列路由精细化管理能力。
同时 APIG 底层部署资源使用了火山引擎 Serverless 容器 VCI,充分发挥了 VCI 弹性、动态的特点,在面对突增流量时,通过灵活的弹性伸缩策略,快速扩容 APIG 后端实例,并通过实例限流、熔断等策略,保障现有实例不会被突增的压力打挂,帮助业务平稳度过洪峰期。
用户核心业务的后端实例也同样基于 Serverless 容器 VCI 来进行部署,充分发挥了弹性容器免运维、按需使用的特点,原本需要固定准备 4500c 的基础资源满足业务峰值需求,通过灵活的 HPA 策略配置,后端实例根据实时的压力变化,动态弹性伸缩资源,在保障稳定性的前提下,极致节省算力,全天范围内整体降本 30%以上。
火山引擎云原生团队致力于将字节跳动内部积累的方案、技术能力和应用工具开放给用户,帮助用户打造高度敏捷、高度弹性的数字化转型底座,实现业务可持续增长。结合广告业用户反馈的使用体验和更进一步的业务挑战,未来,我们将基于云原生服务治理的能力,继续探索如何在极端场景下实现广告这类延时敏感型业务的优雅上下线。
欢迎感兴趣的用户扫码咨询: