美团外卖已经成为公司最为重要的业务之一,而商业变现又是整个外卖生态重要的组成部分。经过多年的发展,广告业务覆盖了Feed流形式的列表广告,针对KA以及大商家的展示广告,根据用户查询Query的搜索广告,以及一些创新场景的创新广告等多个产品线,并对应十几个细分的业务场景。
从技术层面而言,一次广告请求的过程,可以分为以下几个主要步骤:广告的触发、召回、精排、创意优选、机制策略等过程。如下图所示:即通过触发得到用户的意图,再通过召回得到广告候选集,通过预估对候选集的店铺打分、排序,再对于Top的店铺再进行创意的选择,最后经过一些机制策略得到广告结果。
在业务迭代的过程中,随着新业务场景的不断接入,以及原有业务场景功能的不断迭代,系统变得越来越复杂,业务迭代的需求响应逐渐变慢。在业务发展前期,开展过单个模块的架构重构,如机制策略、召回服务,虽然对于效率提升有一定的改善,但是还会存在以下一些问题:
针对以上的问题,我们从2020年初,启动美团外卖广告引擎平台化项目,旨在通过平台化的项目达成以下目标。
提升产研效率
提升交付质量
PM可通过可视化的平台化页面,了解其他产品线的能力,互相赋能,助力产品迭代。
目前,业界已经有不少“平台化”方向的研究,比如阿里巴巴的TMF,定位于泛交易类系统的平台化领域范畴,主要建设思想是,流程编排与领域扩展分层,业务包与平台分离的插件化架构,管理域与运行域分离。而阿里巴巴的AIOS则定位于搜推平台化领域范畴,主要依赖于底层5大核心组件,以算子流程图定制的模式对组件快速组合与部署,从而实现了业务的快速交付。
美团外卖在平台化项目启动时,从业务场景和业务痛点出发,确定了我们项目的核心目标:利用平台化设计理念构建相适应的技术能力,将现有外卖广告的业务系统和产研流程转变为平台化模式,快速支持外卖广告多业务进行交付。我们借鉴了行业内平台化的成熟思想,确定了以业务能力标准化为基础、构建平台化框架技术能力为支撑、产研平台化模式升级为保障的平台化建设整体思想,整体思想可分为三部分:业务能力标准化、技术能力框架化、平台化产研新流程。
即通过标准化提供复用的保证,通过框架承载平台化落地的能力,通过产研新流程的运行机制保证了整体提效的持续性。整个广告引擎服务涉及到的模块都遵循了平台化的思想,支撑上游各个产品场景,如下图所示:
提效是平台化最重要的目标之一,而提效最重要的手段是让功能在系统中得到最大程度上的复用。我们首先针对外卖广告业务线场景和流量的现状做了统一的分析,得出以下两点结论:
第一,各业务线大的流程基本类似,都包括预处理、召回、预估、机制策略、排序、创意、结果组装等几个大的步骤;同时,不同业务相同的步骤里会有很多相似的功能和业务线特有的功能。第二,这些功能理论上都是可以整体进行复用的,但现状是这些功能都集中在业务线内部,不同的业务线之间,不同的小组之间的复用状况也不尽相同。而造成这一问题的主要原因是:
因此,阻碍外卖广告进一步提升复用程度的主要原因,在于整体的标准化程度不足,各业务线间没有统一的标准,所以我们要先解决标准化建设的问题。
标准化建设的广度和深度决定了系统复用能力的高低。因此,本次标准化的建设目标要覆盖到所有方面。我们对广告系统所有的服务,从业务开发的三个维度,包括实现的功能、功能使用的数据、功能组合的流程出发,来进行统一广告的标准化建设。从而使得:
4.2.2.1 功能的标准化
针对功能的标准化问题,我们首先依据功能是否跟业务逻辑相关,将其划分为两部分:业务逻辑相关和业务逻辑无关。
① 与业务逻辑无关的功能通过双层抽象来统一共建
② 与业务逻辑有关的功能,在复用范围上进行分层复用
4.2.2.2 数据的标准化
数据作为实现功能的基本元素,不同业务的数据来源大同小异。如果不对数据进行标准化设计,就无法实现功能标准化的落地,也无法实现数据层面的最大化复用。我们从数据来源和数据使用方式两方面来划分数据:对于业务能力的输入数据、中间数据,输出数据,通过标准化的数据上下文来实现;同时对于第三方外部数据及词表等内部数据,通过统一的容器存储和接口获取。
① 使用上下文Context描述Action执行的环境依赖
② 第三方外部数据的统一处理
③ 词表数据的全生命周期管理
4.2.2.3 调用流程的标准化
最后,将功能和数据进行组合的是业务的调用流程,统一的流程设计模式是业务功能复用和提效的核心手段。流程设计统一的最佳方式就是标准化业务流程。其中对于第三方接口的调用方式,让框架研发的同学用集中封装的方式进行统一。对于接口的调用时机,则基于性能优先并兼顾负载,且在没有重复调用出现的原则下,进行标准化。
在具体实践中,我们首先梳理业务逻辑所使用到的标准化功能,然后分析这些功能之间的依赖关系,最后以性能优先并兼顾负载、无重复调用等原则,完成整个业务逻辑流程的标准设计。
从横向维度看,通过比较不同业务逻辑流程的相似性,我们也提炼了一定的实践经验,以中控模块为例:
平台主要有两个部分组成,一部分是平台前台部分,另一部分是平台开发框架包。其中前台部分是一个给研发人员、PM以及QA三种角色使用的Web前台,主要功能是跟集成了平台开发框架包的引擎服务进行可视化的交互,我们也给这个平台起了个名字,叫Camp平台,这是大本营的意思,寓意助力业务方攀登业务高峰。平台开发框架包被引擎后台服务所集成,提供引擎调度隔离、能力沉淀、信息上报等功能,同时还能确保各个模块保持同样标准的框架和业务能力风格。
各个在线服务都需要引入平台开发框架包,服务性能与平台通用性之间如何平衡也是我们需要着重考虑的地方。这是因为,引入平台框架会对原有的代码细节进行增强性扩展;在C端大流量场景下,平台框架做得越通用,底层功能做得越丰富,与单纯的“裸写”代码相比,会带来一些性能上的折损。因此,在性能开销与平台抽象能力上,需要尽量做到一个折中。我们结合自身业务的特性,给出的安全阈值是TP999损失在5ms以内,将各个业务通用的能力下沉至框架,提供给上层的在线服务。
综上,整个系统架构设计如下:
① Camp平台提供管理控制和展示的功能,该平台由以下几个子模块包组成:
② 平台开发框架包被广告引擎的多个服务引入,执行编排好的业务流程并对外提供服务,平台框架开发包由以下几个子模块包组成:
一个典型的开发流程如上图所示 ,开发人员开发完业务能力后(1),业务能力的静态信息会被采集到Camp平台(2),同时,经过全图化依赖推导得到最优DAG图(3),业务同学再根据实际业务情况对DAG图进行调整,引擎在线服务运行期间会得到最新的DAG流程并对外提供最新的业务流程服务(4,5),同时会把业务运行的动态信息上报至Camp平台(6)。
在下面的章节中,我们将对几个比较关键的技术点进行详细描述,其中就包括了可视化相关的组件自动上报和DAG执行相关的全图化编排、执行调度等,最后,本文还会介绍一下跟广告业务强相关的、词典在平台化中统一封装的工作。
为了方便管理和查询已有业务能力,平台开发框架包会在编译时扫描@LppAbility注解和@LppExtension注解来上报元数据到Camp平台。业务同学可以在Camp平台中对已有组件进行查询和可视化的拖拽。
//原子能力(Action)
@LppAbility(name = "POI、Plan、Unit数据聚合平铺能力", desc = "做预算过滤之前,需要把对象打平",
param = "AdFlatAction.Param", response = "List<KvPoiInfoWrapper>", prd = "无产品需求", func = "POI、Plan、Unit数据聚合平铺能力", cost = 1)
public abstract class AdFlatAction extends AbstractNotForceExecuteBaseAction {
}
//扩展点
@LppExtension(name = "数据聚合平铺扩展点",
func = "POI、Plan、Unit数据聚合平铺", diff = "默认的扩展点,各业务线直接无差异", prd = "无", cost = 3)
public class FlatAction extends AdFlatAction {
@Override
protected Object process(AdFlatAction.Param param) {
//do something
return new Object();
}
}
在广告投放引擎服务中,每个业务的DAG图,动辄便会有几十甚至上百的Action,通过传统的人工编排或业务驱动编排,很难做到Action编排的最优并行化。因此,平台化框架包采用数据驱动的思想,通过Action之间的数据依赖关系,由程序自动推导出并行化最优的DAG图,即全图化编排,此后再由业务人员根据业务场景和流量场景进行定制化调整,动态下发到服务节点,交由调度引擎执行,这样通过自动推导+场景调优的方式便达到了场景下的最优并行。
① 全图化自动编排的基本原理
我们定义某个Action x的入参集合为该Action x执行时使用的字段,表示如下:
$input_x(A,B,C……N)$
定义某个Action y的出参集合为该Action执行后产出的字段,表示如下:
$output_y(A,B,C……M)$
当存在任意以下两种情况之一时,我们会认为Action x依赖于Action y。
② 全图化自动编排总设计
全图化自动编排总体分为两个模块:解析模块、依赖分析模块。
③ 全图化自动编排收益效果
自动纠正人工错误编排,并最大化编排并行度。某实际业务场景中,全图化前后的DAG对比,如下图所示:
标记蓝色的两个Action,会同时操作同一个Map,如果并发执行会有线程安全风险。由于方法调用栈过深,业务开发同学很难关注到该问题,导致错误的并行化编排。经过全图化分析后,编排为串行执行。
标记绿色、红色、黄色的三组Action,每组内的两个Action并没有数据依赖关系,业务开发同学串行化编排。经过全图化分析后,编排为并行。
调度引擎的核心功能是对上述下发后的DAG进行调度。因此引擎需要具备以下两个功能:
整个调度引擎的工作原理如下图:
出于对性能的考虑,调度引擎摒弃了流量请求实时构图的方法,而是采用“静态构图+动态调度”的方式。
由于广告投放引擎服务于C端用户,对服务的性能、可用性、扩展性要求很高。调度引擎的设计难点也落在了这三个方面,接下来我们将进行简要的阐述。
4.3.4.1 高性能实践
流程引擎服务于C端服务,与传统的硬编码调度相比,引擎的调度性能要至少能持平或在一个可接受的性能损失阈值内。下面,我们将从调度器设计、调度线程调优这两个有代表性的方面介绍下我们的性能实践。
① 调度器设计
含义:如何让节点一个一个的执行;一个节点执行完成,如何让其他节点感知并开始执行。如下图中,A节点在执行完成后,如何通知B,C节点并执行。常见的思路是,节点的分层调度,它的含义及特点如下:
另一种常见的思路是,基于流水线思想的队列通知驱动模式:
如上图所示,调度引擎目前支持这两种调度模型。针对多串行节点的图推荐使用分层调度器,针对多并行节点的图推荐使用队列流水线调度器。
分层调度器
依赖于上面提到的分层算法,节点分批执行,串行节点单线程执行,并行节点池化执行。
队列流水线调度器
无论是外层的图任务(GraphTask)还是内部节点任务(NodeTask)均采用池化的方式执行。
节点调度机制
这样做的出发点是:
因此,在项目实际开发中,考虑到实现的难度、可维护性、以及综合考量性能等因素,最终采用集中式调度。
② 调度线程调优
调度引擎在DAG执行上,提供了两种API给调用方,分别为:
而底层调度器,目前提供上述讲到两种调度器。具体如下图所示:
由此看出,调度引擎在内部任务执行上,多次用到了线程池。在CPU密集型的服务上,请求量过大或节点过多的话,大量线程切换势必会影响到服务的整体性能。针对队列通知调度器,我们做了一些调度优化,尽量将性能拉回到没有接入调度引擎之前。
4.3.4.2 高可用实践
在高可用上,我们从隔离和监控上简要介绍下我们的实践,它的核心原理如下图所示:
① 业务隔离
广告场景中,同一服务中经常会存在多条子业务线,每条业务线的逻辑对应一张DAG。对于同一服务内各个业务线的隔离,我们采用的是“单实例-多租户”的方案。这是因为:
除DAG调度和Node调度为静态代码外,图的存储、DAG的选取与执行、Node节点的选取与执行、各DAG的节点通知队列都采用多租户隔离的思想。
② 调度任务隔离
调度任务主要分为:DAG任务(GraphTask)、节点任务(NodeTask)两类。其中一个GraphTask对应多个NodeTask,并且其执行状态依赖所有的NodeTask。调度引擎在执行时,采用二级线程池隔离的方式将GraphTask和NodeTask的执行进行隔离。
这样隔离的出发点是:
因此,无论是线程精细化管理还是隔离性上,两级线程池调度的方式都要优于一级线程池调度。
③ 过程监控
对DAG调度的监控,我们将其分成三类。分别为异常、超时、统计,具体如下:
4.3.4.3 高可用实践
广告业务逻辑复杂,在投放链路上存在大量的实验、分支判断、条件执行等。并且广告投放服务的迭代频率和发版频率也非常高。因此,调度引擎在可扩展上首先要考虑的是如何调度条件节点,以及编排配置如何在无发布下快速生效这两个问题。
① 节点条件执行
对于节点的条件执行,我们在配置DAG时,需要显示的增加Condition表达式。调度引擎在执行节点前,会动态计算表达式的值,只有满足执行条件,才会执行该节点。
② 配置动态下发
4.3.4.4 调度引擎总结
① 功能方面
DAG核心调度
节点条件执行
超时处理
节点可配置化
② 性能方面
如“4.2.2.1 功能的标准化”中给出的定义,可独立实现并部署的业务功能模块抽象为业务组件。从业务逻辑中提取高内聚、低耦合的业务组件,是提升代码复用能力的重要手段。在实践中,我们发现不同业务组件包含的逻辑千差万别,具体实现方式和设计与代码风格也参差不齐。因此,为了统一业务组件的设计思路和实现方式,我们实现了一套标准化的组件框架,以减少新组件开发的重复性工作,并降低使用方的学习和接入成本。
上图左边展示了业务组件的整体框架,底层为统一的公共域和公共依赖,上层为业务组件标准的实现流程,切面能力则实现对业务逻辑的支持。右边为基于框架开发的智能出价组件示例。框架的作用是:
① 统一的公共域和依赖管理
② 统一的接口和流程
③ 统一的切面能力
智能出价组件即为基于以上框架开发的业务组件。智能出价组件是对广告出价策略的抽象聚合,包括PID、CEM等多个算法。出价策略依赖的用户特征获取、实验信息解析等数据统一采用Prepare模板实现;具体PID、CEM算法的实施统一采用Process模板实现;对出价结果的校验、参数监控等后置操作则统一采用Post模板实现。整个组件所使用的公用域对象和第三方依赖也统一托管于框架进行管理。
在“4.2.2.1 功能的标准化”中也定义了工具包的含义,即单个的、简单的非业务功能模块抽象为工具。工具包的建设是广告平台化工作提效的重要基础,其主要的作用是处理业务逻辑无关的辅助类通用流程或功能。例如:广告系统中存在大量的KV类数据需要加载到内存中使用,我们称之为词表文件。为了实现词表文件的全生命周期管理,广告平台化进行了词表管理工具的设计与开发,并在业务使用过程中积累了很好的实践效果。
① 词表管理的设计
上图是词表管理平台的整体架构,词表管理平台整体采用分层设计,自上而下分别五层:
② 词表管理的业务收益
平台化词典管理工具在业务实践中具有的主要优势为:
上文中提到,由于广告业务线较多,且涉及诸多上下游,工程与策略经过几年快速迭代之后,现有业务逻辑已极为复杂,导致在日常迭代中,一些流程性问题也逐步凸显。
① PM信息获取困难
PM在进行产品调研与设计时,对涉及的相关模块当前逻辑不是很清楚,往往通过线下咨询研发人员的方式来解决,影响双方的效率,同时产品设计文档中纯以业务视角和流程来阐述,导致每次评审时,QA和研发人员很难直观获取到改动点和改动范围,中间又会花费大量时间来相互沟通,从而确认边界与现有逻辑的兼容性等问题。
② 研发人员的功能评估完全依赖经验
研发人员在方案设计时,很难直接获取到横向相关模块是否有类似功能点(可复用或可扩展),导致复用率低,同时在项目排期时完全依赖个人经验,且没有统一的参考标准,经常出现因工作量评估不准而导致项目延期的情况。
③ QA测试及评估效率低
QA在功能范围评估时,完全依赖研发同学(RD)的技术方案,且大多数也是通过口头交流的方式来确认功能改动涉及的范围和边界,在影响效率的同时,还会导致一些测试问题在整个项目周期中被后置,影响项目的进度。同时,平台化后基础JAR包的管理完全依靠人工,对一些Action,尤其是基础Action也没有统一的测试标准。以上问题可以概括如下:
借助平台化,对项目交付的整个过程(如下图所示),实施产研新流程,以解决产品、研发与测试人员在迭代中遇到的问题,赋能业务,从而提升整体项目的交付效率与交付质量。
基于平台化实施产研新流程,即利用Stage/Action的方式来驱动整个项目的交付,如下图所示:
4.4.2.1 产品侧
下图所示的是产研功能建设后的应用与实践效果。前两张为建设的业务能力可视化,为PM提供一个了解各业务最新流程及详细Action能力的可视化功能,第三张图为产品设计中相关业务的调研与功能描述(出于数据安全原因,以下截图采用非真实项目举例说明)。
4.4.2.2 研发侧
根据项目开发周期中研发工作的不同阶段,我们制定了基于代码开发前后的流程规范,以保证整个开发周期中研发同学能充分利用平台的能力进行设计与开发提效。
开发前
开发后
4.4.2.3 测试侧
采用Stage/Action统一沟通协作语言:在需求设计与评审、方案设计与评审、测试用例编写与评审等多方参与的项目环节,统一采用Stage/Action为功能描述与设计的沟通语言,以便将后续流程中问题的发现尽可能前置,同时各参与方更加明确变更及测试内容,为QA更好的评估测试范围提供支撑,进而更好的保证项目测试质量。
推动基础Aaction UT全覆盖:针对基础Action,构建单元测试,在Merge代码时自动触发单元测试流水线,输出执行单测的成功率和覆盖率,并评定指标基线,保证可持续测试的效率与质量。
改进JAR管理工具化与自动化分析及测试:一级Action都集中写在平台JAR包中,对类似这种公共JAR包的管理,开发专属的管理与维护工具,解决升级公共JAR自动化单测覆盖问题以及每次升级JAR版本需要人工分析人工维护的测试效率问题,打通集成测试自动化的全流程。
① 产研效率的提升
系统能力沉淀
人效的提升
② 提升交付质量及赋能产品
本文分别从标准化、框架、产研新流程3个方面介绍了外卖广告平台化在建设与实践中的思考与落地方案。经过两年的摸索建设和实践,美团外卖广告平台化已经初具规模、有力地支撑了多条业务线的快速迭代。
未来,平台化会细化标准化的力度,降低业务开发同学成本;深化框架能力,在稳定性、性能、易用性方面持续进行提升。此外,我们在产研新流程方向也会持续优化用户体验,完善运营机制,不断提升产研迭代的流程。
以上就是外卖广告针对业务平台化上的一些探索和实践,在广告工程架构等其他领域的探索,敬请期待下一篇系列文章。
乐彬、国梁、玉龙、吴亮、磊兴、王焜、刘研、思远等,均来自美团外卖广告技术团队。
美团外卖广告技术团队大量岗位持续招聘中,诚招广告后台/算法开发工程师及专家,坐标北京。欢迎感兴趣的同学加入我们。可投简历至:[email protected](邮件主题请注明:美团外卖广告技术团队)