本文作者:huangleilei02
业务背景
投放广告买量,不管是拉新还是促活场景,都是互联网用户增长的重要手段。RTA(Real Time API),是广告投放领域非常重要的一种投放方式,用于满足广告主实时个性化的投放需求。顾名思义,RTA指的是API接口实时调用,将直投的广告主的流量选择权交给广告主,媒体传入设备号调用RTA问询接口,进行用户投放的筛选,让广告主在广告曝光前进行投放策略的判断,满足拉新、促活等个性化需求,同时也能做到媒体和广告主数据隔离。
云音乐在对外买量时,作为广告主,也建设了一套从RTA人群圈选、到媒体响应、再到站内承接的完整系统。云音乐在过去一段时间里,不断从业务及技术等多个角度,对该系统进行建设和完善。
而在2023H2,RTA业务以RTA专项的方式进行实践,专项目标包含RTA接入多个媒体、投放量级增长、缩短人群圈选流程等。本文将以RTA专项建设过程中的解决方案为主,结合H2前的一些建设,来介绍云音乐RTA投放与承接系统建设中的一些思路。
RTA投放侧架构
RTA投放方式的显著特点是请求量大、实时性要求高。云音乐对接多个广告媒体,单媒体的请求QPS在数万-数十万不等,单次请求从媒体测请求发起到收到响应的超时时间一般仅约五六十毫秒,对于服务来说有着不小的挑战。
因此,云音乐RTA投放侧(即不包含站内承接的业务领域)的技术架构有以下特点:
- 独立Nginx集群及专线,和主站隔离
- 分层解耦。请求适配层+投放策略层+设备映射层+设备写入层
- 异步模式,高性能、高吞吐量、低延迟。使用Netty处理请求、使用Lettuce作为redis客户端
- 针对海量请求,通过时间轮实现超时处理,大幅提升性能
RTA自动化圈选人群
RTA投放的核心部分,就是圈选出一定规则的以设备号为主体的人群,针对不同媒体的不同投放账户,投放不同的人群。
由于用户增长领域广告投放复杂度高,以及当前互联网存量时代对精细化运营有更高诉求,业务对RTA投放人群包圈选的自动化和灵活性有一定诉求。云音乐RTA投放系统建设了自动化圈选后台,结合云音乐人群圈选平台“魔镜”,做到设备维度的RTA投放人群包的灵活圈选和快速设备同步。
其整体链路如下:
在圈包自动化能力的基础上,RTA投放平台做了一些扩展性工作,从而做到系统稳定性提升、数据问题快速响应、圈包流程缩短提效,上线后对业务的圈包投放动作提效60%以上,也为后续的自动化、精细化运营打下基础。
圈包后台可以通过以下几点概括:
- 打通魔镜人群圈选平台、云音乐设备数据,使得能够通过简单的标签圈选或者自定义圈选条件SQL,完成投放设备的定向圈选,成本低、响应快速;
- 打通AB实验能力,能够灵活圈包并验证投放策略;
- 整个数据准备流程由运营操作,平台自动化进行校验;
- 能够应对单日数亿至十亿级别总数人群包数据写入;
- 数据同步结果通知,调度过程记录;
- 数据链路多类异常告警通知,做到快速定位问题并响应解决。
RTA存储痛点优化
业内对于RTA设备数据的存储方式大同小异,因为设备数据和人群信息的关系以kv为主,所以redis是比较多的一种选择,云音乐也使用独立集群的redis cluster作为RTA数据存储的数据库。在业务初期,采用的是redis的String结构进行存储,例如将设备Oaid的MD5值作为key,对于人群包列表及其他相关信息的Json作为value,但该方式在存储成本上有着很大问题。
目前投放数据,会针对Oaid、Imei、Idfa为主的几种设备信息的MD5值进行存储,此外,在与字节抖音合作时,应对数据安全规范,还需要进行Prl加密存储,因此,一个手机会产生多条设备记录。rta投放人群设备数据非常庞大,用户数量数亿甚至十数亿左右量级,结合上述多种设备及多种加密方式,整体数十亿量级记录。在初期,投放平台使用redis的String类型存储数据,随着新加密方式的接入和设备数的增多,对存储造成了压力,如不做改造,将占用数百GB甚至1000GB的存储空间。
投放平台通过以下几个方式的组合,完成了超过80%的存储空间优化:key改造、value改造(分为人群包名改造、过期时间改造、序列化压缩)、存储结构改造。我们先从更直观的key、value本身对压缩开始介绍。
key改造:此处的key指的是redis存储的key,其内容为设备号相关信息,由设备号类型、加密信息以及设备号加密结果(如MD5或者其他方式加密结果)组装而成的String,在39-86字节不等。使用MurmurHash2的64位hash算法对原本对长字符串进行哈希,此时冲突率非常低,对业务几乎无影响。并将其作为字节数组转为String,转为String时采用的编码字符集为ISO_8859_1,相比默认的UTF-8存储更少。当然,如果读写均选择合适的redis客户端,也可以直接用字节数组。
value改造:原本的value信息相对冗余,经过以下三组改造的结合,可以将50-100+字节的Json压缩为8-16字节。
- 人群包名改造:原本的value为人群包信息列表,包含了冗长的人群包名、毫秒级别的业务过期时间以及其他相关信息。改造时将人群包名字序号化,将string转为int。
- 业务过期时间改造:将毫秒级过期时间减去6未有效数字,粗化到十几分钟级别,在对业务影响很小的前提下有效减少了存储空间。
- 序列化压缩:将原本JSON序列化结果转为Protostuff(Google ProtoBuf的改进版)序列化,虽然牺牲了可读性,但大幅压缩了存储空间。关于Protostuff序列化方式的压缩原理,各位可以自行检索研究,本文中就不再赘述了。
数据结构改造:除了业务数据的压缩,本身在redis中的存储结构也大有可为。为了更好介绍该项改造,我们不妨来看一个case,当我们使用redis的String类型存储单条数据{"wyyyykey":"wyyyyval"},该数据的key大小为8字节,value大小为8字节,那么这条键值对占用了多少内存呢?
set wyyyykey wyyyyval
memory usage wyyyykey
--72
72字节比键值对本身的16字节大了不少,那么多出来的这部分内存用在哪里了呢?这就得提到redis中字符串的实现方式了。redis首先会为每个键和每个值创建一个redisObject(以下简称robj),带有一些对象头信息;而我们都知道,redis的字符串对象的类型为简单动态字符串(Simple Dynamic String,SDS),其中有部分空间用于记录实际内容存储情况和存储时的预留空间;在维护全局哈希表的dictEntry时,需要维护指向key、value、和下一个节点的指针,这些都会造成额外的存储。
下图为一个字符串类型的键值对存储结构,关于该key和value的SDS实现结构为sdshdr5还是sdshdr8对于整体存储逻辑和量级影响很小,因此此处不做过多讨论,读者可自行阅读源码,此处参考引文2。需要注意的是,Redis的内存管理和优化策略是复杂的,并且在不同的版本和配置下可能会有所不同。因此,在具体情况下,实际的内存占用可能会有所变化。
而想要优化掉大量的robj元数据、dictEntry、sds信息,redis的Hash结构是一个很好的方案。在默认设置下,当哈希对象可以同时满足以下两个条件时,哈希对象使用压缩列表(ziplist)编码:哈希对象保存的所有键值对的键和值的字符串长度都小于64;哈希对象保存的键值对数量小于512。ziplist非常节省内存,是由一系列特殊编码的连续内存块组成的顺序性数据结构,一个ziplist可以包含多个节点,每个键值均为一个节点,每个节点紧挨着,能够大幅减少内存。
不妨假设原本有30亿个key需要存储,将这30亿个key通过hash打散成1500万个redis的hash对象,可以大幅减少robj元数据、dictEntry、sds信息等占用的内存。而虽然查询的时间复杂度由O(1)变为了O(n),但由于此处n为ziplist的entry数量大约为200,对整体的时间影响非常小。
实时站内承接
为了提高使买量用户的留存(是让钱花的更值),云音乐建设了投放用户实时站内内容承接,能够覆盖新客和召回用户等多种用户类型。当然,广告投放方式往往要多组合才能得到更好的效果,本节介绍的云音乐站内承接建设,并不只覆盖RTA链路投放到广告用户,也能够对其他方式的投放用户进行承接。
本节主要以云音乐首页模块及内容的投放用户承接作为例子进行介绍,除此之外,站内落地页直达、资源自动订阅、搜索底纹词等都是可以承接的手段,而用户的投放归因信息也可以辅助算法进行决策。
用户来源实时归因
假如云音乐在各媒体同时投了某首单曲和某个歌单,需要知晓用户成为新客或者回流APP来源于哪个广告资源,才能进行承接,这个步骤就是归因。广告的投放方式是多种多样的,例如A用户是点击广告后初次下载云音乐APP进站;B用户是云音乐老用户但已经卸载,也是点击广告后下载APP;而C用户则是老用户但手机上已经安装云音乐APP,直接通过广告所带的deeplink链接唤端;此外,还有渠道包等其他投放的方式。针对上述多种情况,需要聚合建立用户来源实时归因能力。
针对直接通过广告所带的deeplink链接唤端的用户,通过在deeplink上拼接业务字段,客户端在打开APP时进行解析并传给服务端,即可完成用户本次的归因,该方式非常直接且准确。针对下载APP的用户,则相对曲折一些。在用户初次激活APP时,将云音乐deviceId和历史数据进行比较,来判断该设备是新设备或者是回流设备,将新设备、回流设备数据和广告点击数据根据一定策略进行业务归因,从而定位到该次激活是否投放用户以及具体是哪个投放资源带来的用户。
首页模块承接
在首页场景下,上下文组装阶段进行投放归因信息和部分提权策略信息的组装,该信息将用于后续模块排序和各模块资源的组装过程中。在模块排序阶段,利用灵渠投放平台(感兴趣的读者可阅读参考第3篇)进行灵活可配的模块排序干预;在模块内容组装阶段,通过服务端强插干预或者算法策略干预的方式,将对应的投放资源按照一定业务策略进行提权。模块+资源的组装方式有效提高了投放用户的站内留存。
针对多模块多种资源的首页模块及资源承接策略,设计了一套通过json字段匹配方式灵活更改承接内容的解析规则,能够实现从广告媒体到广告计划到广告投放资源等多级可变的规则匹配。同时,针对业务透出规则及投放资源特性,采用过滤器链模式实现提权与否的校验。该承接能力涵盖多种用户类型(新用户、流失用户等)、涵盖多种首页类型(老首页、首页新框架等)、多种客户端(android投放、IOS投放)。
总结与展望
本文从整体架构、投放数据、站内承接等多个角度介绍了云音乐RTA投放与承接系统建设中的一些要点;从业务、技术两个角度结合,为广告投放业务提供了一些思路。
同时,从业务专项角度,较好地完成了专项目标,提高RTA渠道接入数量和业务量级,通过自动化圈选人群能力的建设缩短投放圈包配置周期60%,通过站内承接系统有效提高了投放用户的留存。
展望未来,处于成本和价值的考量,RTA投放及承接的精细化将是非常重要的一个方向。在有完善用户价值体系的基础上,RTA人群圈选及投放需要和个性化出价策略结合得更加紧密,而不同用户在站内的不同承接策略也需要更加精细和深入,形成相对完整的生态。
参考链接
- https://github.com/protostuff/protostuff
- https://cloud.tencent.com/developer/article/1837860
- https://juejin.cn/post/7290741484364562432
最后
更多岗位,可进入网易招聘官网查看 https://hr.163.com/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。