如何打造高质量的SSP广告引擎(内部干货分享)
女主宣言
当今互联网有几种主流的商业模式:广告、游戏、增值服务等。毫无疑问“广告推送”带给互联网公司的收入绝对是相当可观。今天小主就为大家分享一篇来自360手机卫士团队分享的SSP广告引擎,这么高质量的内部干货不拿出来分享实在对不起大家。
PS:丰富的一线技术、多元化的表现形式,尽在“HULK一线技术杂谈”,点关注哦!
一、概述
当今互联网有几种主流的商业模式:广告、游戏、增值服务等。今天我想谈一谈广告系统中的SSP引擎。SSP(全称:Sell-Side Platform)是一个媒体服务平台,该平台通过人群定向技术,智能的管理媒体广告位库存、优化广告的投放,助网络媒体实现其广告资源优化,提高其广告资源价值,达到帮助媒体提高收益的目的(以上摘自360百科)。大白话就是:各种端(app端)找SSP要广告, SSP选出一批广告, 并告诉这些端,按照某些样式展示。SSP负责如何去选广告, 以及相应的样式是什么样子。SSP不断优化选择广告和确定样式的策略,让各个产品能赚到更多的钱。
一个好的SSP系统应该具备那些能力? 我总结了五点,列在下面:
-
灵活扩展能力
-
快速接入各种广告源
-
快速接入各个产品
-
快速验证广告的不同样式
-
快速调整广告页面布局
-
快速调整广告策略
2. 高性能、高并发能力
3. 高效发布和在线灰度能力
4. 快速调试定位错误能力
5. 强大的数据处理和分析能力
-
精准的用户画像刻画
-
准确的广告推荐
-
分钟级数据监控
-
支持海量数据细粒度的多维数据查询
二、SSP系统架构
SSP作为一个大型的业务系统, 系统结构还是比较复杂的,架构上可以分为以下六层:产品层、接口层、业务中间层、微服务层、数据处理层、数据存储层。分层系统架构图见图2-1, 详细架构图见图2-2
三、Flexible and Scaling (灵活扩展性)
在上一章 SSP 系统架构图中所展示的,系统灵活扩展能力主要体现在业务中间层,从大到小我们分了四个层次来为系统提供足够的灵活扩展性, 分别是架构层、业务层、广告控制层、广告展示层。 我们抽象出四个概念用来对应,Micro Service对应架构层, Topology Organizer对应的是业务层, Rule Manage System 对应的是广告控制层, Template Manage System对应的广告展示层。
3.1 Micro Service
micro service 翻译过来是微服务, 与微服务对应的是单体式架构(传统方式),单体式架构特点是所有功能打包在一起,基本没有外部依赖,优点是开发管理简单;而微服务架构整个系统由多个子服务组成,各个服务功能独立,服务之间通过消息传递来耦合,最大优点是系统耦合性低,有非常好的扩展性,架构示意图见 图3.1-1 和 图3.1-2。
从第二章的SSP系统架构图上可以看到, SSP是一个非常复杂的系统, 并且SSP有个特点就是各个子服务(各个DSP, 用户画像,红
包等)之间几乎没有什么关系, 如果想要快速的接入一个广告源,快速的上线一个产品,具有非常灵活的DSP上下线规则, 那么微服务
架构是我们唯一的选择。
3.2 Topology Organizer (based DAG Algorithm)
系统架构层我们采用了微服务解决了独立子服务接入的灵活性,那么业务逻辑的多变组合如何来高效解决呢? 在Topoplogy Organizer 框架中我们给出了比较好的一个解决方案。该框架包含DAG, topology, stage, context四个概念,我们把业务拆解成一个功能单元的组合体, 每个功能单元叫做stage, DAG代表了stage 的执行逻辑图,topology用来控制stage的执行先后顺序,context用来做stage之间的数据交换,这个结构只有数据耦合,具备并行操作的基础(这在migo架构中会详细说)。不同业务可以共用stage模块代码,大大减少了代码的重复开发量,提高了开发效率。示意图如下:
图中左右两个业务的DAG表示图,可见初始化规则库、请求解析、用户画像服务、过滤服务、返回广告等stage是可以共用的,
业务可以由这些stage灵活组合而成。
3.2.1 DAG
DAG(有向无环图), 如图3.2-1所示, 业务可以组织成DAG,这种图结构保证了功能模块的执行顺序, 定义好一个DAG也就
定义好了一个业务的流程。
3.2.2 topology
topology算法用来实现DAG的功能块执行顺序,保证功能块的次序不会被颠倒,如图3.2-1的左图, 通过topology会被分解成初始化规则库-> 请求解析->用户画像->点睛DSP(或自运营DSP、MAX DSP, 以上可以通过异步技术手段并行化处理)->过滤服务 ->重排服务 ->返回广告
3.2.3 stage
我们把每个功能模块称为stage, 负责完成某一特定功能。stage模块可以逐步沉淀出很多公共模块,为代码共享,加快开发提供了很好的基础。比如各类DSP stage 可以抽象出公共DSP stage, 公共DSP stage负责完成和下游DSP的RPC交互操作,派生的DSP stage 只用改写自己的各种参数即可。
3.2.4 context
context 是一种表示上下文信息的数据结构,在Topology Organizer 框架中起到stage之间传递数据的作用,正式由于这个结构的提出,才使得stage之间逻辑解耦,交互完全变成数据依赖,相当于贯穿一个业务的数据总线。
context结构的定义大致类似如下的结构:
<business name="卫士广告">
<stage name="用户画像">
<attrlist>{年龄:20;收入:3k;爱好:旅游}</attrlist>
</stage>
<stage name ="点睛DSP">
<adlist>{ad1:com.mobilesafe.apk ;ad2:com.zhushou.apk; ad3:com.browser.apk}</adlist>
</stage>
。。。。。。
</business>
支持add, delete, get,set 操作。add, delete用来添加删除context的任意路径的节点, get,set操作用来对context某一路径节
点取值或者赋值。
3.3 Rule Manage System
我们用micro service 和 Topology Organizer 解决了架构和业务的灵活扩展性,但光做到这两点还不足以提供足够的灵活性。不同业务在同一stage下的数据处理逻辑可能会有一些不同,比如频次控制服务,卫士广告可能每个广告控制10次, 卫士CPM可能是每个广告控制100次。如果我们把这些限制条件写入到代码中,那么随后带来的将是无休止的策略调整和代码上线。下面我们介绍一下Rule Manage System,如何优雅的在数据项这么细的粒度提供灵活性。
3.3.1 rule engine
什么是rule ,我们可以认为rule是条件到输出的一个函数映射,形式描述如下:
Output = R(condition,condition,....) ; Output为输出, R 为rule映射关系,condtion_list 是条件, 为输入参数。
我们发现,上节描述的频次控制功能可以描述成一个规则 Output = R(product,ad_control,model),展开成如下两个具体
规则 :
通过添加上面两个规则,我们不用修改代码即可实现对频次控制策略的运营,如果卫士广告频次想改成15次, 运营改这个规则就可以了,而不用去改引擎代码,避免了引擎测试、上线、重启后续一系列麻烦的事情。
rule table : 许多的rule 构成一张规则表, 如果我们想查找符合某种条件的输出是什么, 查这个规则表即可, 如果我们想灵活的控制某一数据项, 向这个规则表插入一条rule即可。图3.3-1 展示了一个规则表实例。
rule engine:对rule table 进行管理,组织成特定数据结构(可以是上图所示的表的结构, 也可以是其他更加复杂的数据结构),提供插入,删除,查找等操作。
3.3.2 quick match rule
上节我们谈到rule engine 可以不同的组织方式,不同的数据结构带来的查询性能会有很大的区别,SSP引擎由于请求量非常大,而且每个请求都会对rule engine进行规则查询,那么如何快速的进行rule match对SSP的性能影响会非常的大。
3.4 Template Manage System
SSP系统有一项很重要的功能就是管理广告样式以及管理广告展示布局,例子(见图3.4-1 和 图3.4-2)可以形象的展示不同的广告样式和广告布局。 我们的产品往往不断调整广告的样式,如三图变长图;或者增加新的广告样式如视频广告,轮播广告;又或者针对不同产品的不同功能页面会尝试不同的广告组合来布局。 如何支持广告的灵活性运营, 对SSP系统提出非常高的要求。我们使用两个机制来很好的解决了这个问题,base template 机制,polymorphism and combination 机制 。下面我们来具体的介绍这两个机制,以及这两种机制如何结合起来支持广告运营的灵活性。
3.4.1 base template
base template 我们叫做基础模版,用来解决广告样式的多样性。在处理广告展示的过程中,如果每一个广告都写代码去针对性处理,广告千变万化,这个工作量将是非常复杂的。如果仔细的研究一下,会发现虽然广告千变万化(广告素材、广告标题等)但是广告的样式种类确可以归成很少的几十类,最多几百类。 我们把广告剥离成静态和动态两部分, 静态的是广告样式, 种类只有数十种,这样管理起来就非常的简单, 只需要运营这几十个静态模版类即可;动态的是广告的素材啊,标题啊,跳转连接啊,点击动作啊, 这些可以通过下一节的polymorphism机制进行实例化, 从而实现动态部分的灵活运营化。
3.4.2 polymorphism and combination
polymorphism 叫做多态实例化机制, combination叫做组合机制, 这两个机制结合起来用来解决页面广告布局的灵活性。
base template 可以决定广告的样式,从基本模版库中选中样式模版后,通过polymorphism可以对该模版进行实例化,比如两个同样的长图模版,填入不同的素材链接地址,标题,点击动作可以形成两个广告实例。polymorphism的灵活性如何实现?通过3.3节的规则管理系统可以动态的配置基础样式模版所需要的属性信息, 这样就可以完成polymorphism的实例化,并且可以交付运营来配置管理(实例化形象例子可以参考图3.4-2)。
有了实例化的模版,我们的工作完成了一半, 产品所需要的不是单一的一个广告,产品端的页面是一系列广告的展示, 但是产品的广告页面布局会不断调整的,不能每个页面写死由那些固定模版组成。 我们通过combination机制(组合机制)很好的解决页面布局的灵活性, 也就是通过3.3节的规则管理系统由运营来动态组合样式实例来形成页面布局(布局形象化例子可以参考图3.4-2)。base template,polymorphism, combination机制结合起来形成页面布局见图3.4-3。
四、高性能、高并发引擎架构
SSP 每天请求量超过30亿,支持这么大的流量需要一个非常出色的高性能http引擎。http引擎框架大多归为两类,一类是多进程框架,一类是多线程框架, SSP的演进过程中也分别使用过这两类框架,一个是360大流程云引擎部门开发的cloudUcs, 一个是核心安全部门开发的trident,下面我们来详细介绍一下,最后我们来谈谈基于go协程机制的新的http引擎框架。
4.1 多进程框架(cloudUcs)
cloudUcs 是大流程云引擎部门开发的, 是一个比较优秀的高性能http引擎,目前搜索一级引擎还是采用这个框架,框架图见图4.1-1。
Ucs的优点有如下几点:
master-slave多进程模型,可以保障引擎的可靠性
worker进程支持多线程异步化处理各种IO事件
网络通讯细节封装良好,业务逻辑不需要关心网络底层细节
进程间请求流量均衡
Ucs 当时存在如下一些缺点,我们后来改用了trident框架,列一下当时遇到的问题:
ucs的多进程模型在加载了短信的预测模型后,导致内存占用特别大,系统负载高;
在当时ucs的情况下,配置加载必须要进行进程reload,会有丢包现象,不适合ssp这种强运营性的系统;
ucs的进程模型相比于trident,事件分发速度慢,而且多进程间负载均衡做的不好,work进程负载不均匀;
ucs编程模型不够友好和搜索业务绑定的比较严重,新人上手门槛高;
4.2 多线程框架(trident)
trident是大流程云查杀组开发的,现属于核心安全部门, 也是一个比较优秀的高性能http引擎,目前云查杀引擎是采用这个框架,SSP也是采用这个框架,框架图见4.2-1。
trident优点:
单进程加载资源,占用系统内存少;
支持配置的热加载,尽管存在互斥锁的问题;
trient,顾名思义,单一进程内可以支持tcp、udp、http三种服务;
worker线程之间的负载,相对于ucs来说,负载更均衡,之前ucs因为worker负载不均衡问题,我们只能把每个worker的队列长度都设置为1以便强制所有worker参与调度,但这也限制了ucs的qps,而trident因为负载叫均衡相对较好,qps要比ucs好些,但是
这个是理论上,没有做过极限压力测试;
trident缺点:
配置热加载过程采用互斥锁,影响性能,ssp已经规避了该问题;
当前trident采用的单进程多线程方式,已经整个ssp系统采用的异步stored、异步redis等,造成单进程内线程数量太多,线
程间同步和竞争关系复杂;trident的对外IO事件和分发事件只有一个线程在处理,业务量大时该线程时瓶颈;
大量的异步应用,多线程模型,导致开发的debug成本较高,新人入手困难。
还存在一个极端状况下会出现的bug,请求量极速上升,内存占用会急剧上升(接收和处理线程之间的队列无队列长度上
限)。
4.3 go 微服务框架(migo)
4.3.1 migo简介
migo是我们团队用go语言重新开发的http引擎框架, 充分挖掘利用了go的协程机制,拥有非常强大的性能。由于多线程模型在发生线程调度的时候代价很高,不能充分利用cpu的时间片,所以人们又提出协程概念,相当于在线程的工作时间片上又切成很多小段,分给协程来工作,有统一协程调度器来调度协程,而不会触发线程的调度,得以充分的利用线程的工作时间片。go 语言在语言层面对协程进行了支持,所以简单易上手,目前在高性能、高并发领域是非常火热的编程语言。所以我们采用go语言重新打造了一个新的微服务引擎框架migo。migo本身我们设计成一个通用的微服务框架,也具备SSP的很多特点,也具有DAG,topology, stage, context,micro service这些组件(组件介绍见第三章)。与C++或者php语言相比,DAG,topology, stage反而更好的把go的协程机制发挥出来,提供充分的并行性。 migo的系统框架图见图4.3-1和图4.3-2。
4.3.2 migo 性能评测
我们分别对migo, php-fpm,trident进行性能测试,用来对比分析这几个框架的性能差异,migo和php -fpm框架性能对比见图4.3-3 和图4.3-4:
简单业务migo性能是php-fpm的3-5倍;
复杂业务migo性能可以达到php-fpm的20倍;
4.3.3 总结以及migo 未来规划
综上我们总结一下migo的特点:
-
性能优越,充分利用goroutine机制,最大化的利用cpu的分片时间,不但一个请求可以用goroutine来实现,请求里面还可以分成小段stage,这些小段stage也可以启用goroutine来实现,并行化的粒度变成了一次请求中的小单元而不是线程框架下的一次请
求; -
新人上手简单,易学易用;
-
有很好的灵活扩展性,模块很好重用,极大提高业务编写效率;
migo未来的规划:
-
进一步探索框架性能的提升,提出类似spark RDD概念的的gRdd,研究stage之间数据共享的可行性;
-
研究migo和docker部署的结合;
-
研究migo如何对不同业务(topology)进行调度,提供更高层次的灵活扩展性;
-
更多公共stage模块的开发,提供更强大的能力;