背景
人力背景
因为是一个创新业务部门,所以人力在初期还是比较紧张(后期虽然拿到了大量的hc,但是因为组内招聘条件限制,只补充了三个校招人力),因此,数据组的宗旨是,尽可能解放开发人力,固定的事情交给定时任务来做。结构尽量简化,以保证业务快速验证迭代为主。
广告归因背景
创新产品的起量,尤其是冷启,离不开广告投放。部分社交产品可能通过地推的方式进行冷启,但是大部分产品还是通过广告投放来起量。在广告的出价上,现在的广告平台基本都支持oCPX方式,即根据广告主反映的转化情况实时出价。具体的操作是,广告平台将广告点击信息交给广告主,广告主通过业务内部的转化信息通知广告平台,广告平台使用用户转化信息作为正样本训练投放模型,同时,发生了转化的广告的出价会相应上升,代表广告主希望这些广告更多的投放到用户。
结构
广告归因系统独立于业务系统之外,即使广告归因系统完全不可用,也不影响业务系统的正常运行。
和外部交互
广告归因系统和外部交互的主要有三个部分
- 从广告平台获取广告点击信息。广告平台允许广告主配置一个带有通配符的链接,当用户点击广告时,会向广告主配置的链接发起请求,同时将通配符替换成对应的信息。这个接口上没有鉴权,容易被刷
- 从业务系统获取用户转化信息。广告归因平台独立于业务之外,但是需要业务传递业务内部的用户转化信息。对转化的定义由运营同学确定。这里使用mq进行消息的传递。
- 向广告平台回传广告归因结果。广告平台提供通用接口,通过特定参数的调用被认为是一次点击广告并转化的事件。这个接口大部分使用nonse和traceid鉴权,部分使用hash鉴权。
内部结构
广告归因系统内部分为四个微服务,分别是点击监测,广告归因,结果回传和离线数据收集。
各个微服务之间并不是通过rpc调用,而是通过上传事件到athens,以及消费athens产出的mq进行关联。这样进行了各个微服务之间的解耦,防止在某个服务发生问题时整个广告归因链路失败,同时也可以通过athens的持久化能力,将链路中的数据持久化到hive表中,方便da进行数据分析和报表产出。
点击数据流从广告平台通过点击监测服务流向归因服务,转化数据通过athens流入归因服务,两条流在归因服务中经过处理产出归因结果流,并通过回传服务流回广告平台。
离线数据收集是定时服务,通过每小时定时向广告平台拉取全量消息,获取每个时间段的广告消耗数据,同时和历史消息一起组成全量的广告信息数据。通过和回传服务进行对比,还可以发现链路外是否有问题。
实现
双流连接
本质上是点击流和转化流的二流连接的实现。这里因为这两个事件具有时间上的先后性,因此采用了缓存点击流,匹配转化流的方案。具体的逻辑由特定的归因逻辑实现。
问题:如果点击事件晚于转化事件到来怎么办
用户特征
为了获取用户最近点击广告的内容,接入了特征能力,将用户最近看到的广告信息写入特征库中。在用户首次打开app时,可以让用户直接看到广告中的商品。
为了通过广告id获取广告内容,需要离线从广告平台接入广告详细信息。这里的鉴权用到了oAuth2协议。为了实现token的刷新,使用了分布式锁。
拿到token后,定时从广告平台拉取广告信息,并上报事件。这里不仅拿了广告的内容信息,还拿了广告的投放信息,比如当前的出价,广告的曝光数和点击数等。
为什么只在首次打开这么处理:成本考虑。广告方向的考核指标是新付费用户数。
归因逻辑
由于广告平台和业务平台的id不互通,无法通过id进行join操作,因此,使用了用户的设备id进行匹配。在redis中使用用户的设备id作为key,用户的点击历史作为value存储用户的点击历史。这里点击历史有长度限制100,如果点击超过100次,那么之前的点击历史就从redis中舍弃。
由于某些操作系统上的权限管理,无法获取到设备信息,因此存在一些其他的归因方式,如剪切板归因,channelid,ip+ua+mac等。但都不如设备id好用。
归因截断
应用商店的安装包使用了特殊的channelid,用户通过其他链接下载了安装包之后,在安装时可能被系统引导到应用商店重新下载。这时用户就安装了
优化
配置化改造
为什么要做
因为是个创新部门, 需要尽可能降低人力消耗. 同时, 运营的外包人力比开发的外包人力更多, 而且进行配置化改造后, 业务的处理速度也会提升, 减少了运营的等待开发时间.
怎么做
首先让暴露一层给运营理解的概念, 如监测链接的参数, 业务id, 平台id, 渠道商id等, 这些参数由我来做归一化, 同时, 在广告归因系统内部, 将原有的零散的特殊处理的逻辑根据新的抽象概念进行重新组织, 重新编排了流式处理的整个流程, 使得业务对运营更容易理解和配置.
详细方案
- 给出监测链接的产出excel, 运营只需要在监测链接上手动选择广告平台, 并配置业务id和渠道商的id便可生成新的监测链接. 在新的平台接入时, 需要开发参照平台文档进行拓展.
- 通过无极平台给出了底层配置db的简单界面, 运营可以通过配置在db中新增业务id. 同时通过实时更新机制, 避免了业务新增时的服务重启. 同时增加了开关, 可以在紧急情况时快速停止某个业务的归因结果回传.
- 将服务涉及的上下游mq信息从配置文件中转移到db中, 可以在mq有变动时通过
问题
逻辑问题
监测链接鉴权
监测链接由于是配置到广告平台,只能配置get请求,难以鉴权。
解决方案
- 使用正则匹配过滤参数不合法的请求,防止修改参数攻击
- 使用redis记录30d内的历史数据,key为广告点击时间和设备id(或者traceid),如果重复则认为是重复记录,不继续处理
为什么不把负样本回传
不知道用户之后会不会发生转化,无法确定回传负样本的时机
实现问题
缓冲一段时间再归因
为什么没用:因为数据不一定是因为网络延迟没有到,也可能是因为mq问题卡住了,缓冲一小段时间无法解决问题。
分布式锁
为了更新access token,需要使用分布式锁防止多个节点同时进行更新(因为更新失败会导致整个数据拉取不可用,告警优先级较高)。
使用redis实现分布式锁,锁的value是当前节点的ip,
分布式锁的唯一性和互斥性:redis setnx
分布式锁的死锁:设置超时时间
分布式锁的操作原子性:set nx ex
分布式锁的时效性:使用定时协程续期
分布式锁的退出:删除key
分布式锁的归属:使用本机节点作为value
监控和容灾
域名服务出错
体现为所有业务在一段时间内无法获取点击监测信息。
验证方案:在公网环境(手机4G网络)手动ping监测链接域名失败。
解决方案:先联系运营和渠道商,停止所有广告投放,等待域名服务修复后再开始投放。
点击监测服务出错
体现为点击监测服务在短时间内出现大量报错,或者服务出现异常,同时点击监测数据量出现异常归零现象。
验证方案:登录广告平台,使用调试工具模拟点击监测链接请求,并在athens上检测染色数据是否上报,以及是否正确。
解决方案:先联系运营商和渠道商,停止所有广告投放,等待点击监测服务恢复后再开始投放。
athens出错
体现为所有服务都无法接收到新的消息
验证方案:咨询athens管理员,或者查看athens监控
解决方案:先联系运营和渠道商,停止广告投放,等待athens回复后再开始投放
业务字段出错
体现为点击流监控正常, 转化流数量正常, 但是没有回传数据.
验证方案: 咨询后端是否有发布, 查看日志, 查看是否有报错, 是否有编解码失败的情况
解决方案: 先联系运营和渠道商,停止广告投放, 和后端同步有无事件的字段改动, 根据改动恢复服务处理逻辑后再恢复投放.
实例: 业务调整了时间字段的编码格式, 导致归因系统无法解析转化的时间, 进而导致无法正常归因. 在停止广告投放后, 新增了额外的处理逻辑, 兼容两种时间格式, 然后将解析错误的转化mq指定偏移点重放, 恢复归因结果.
其他
遇到的问题和解决方案
点击流晚于转化流到达
经统计, 点击时间存储到缓存和点击时间相差超过30s的情况少于1%,
- 先到达的转化时间已经归因成功并回传 这时直接使用已经到达的结果并不再回传. 由于只有少数点击数据会最后产生转化, 和运营同步后, 运营认为这个误差是可接受的.
- 先到达的转化因为没有点击, 所以没有匹配成功 使用spark定时任务每小时将前一天的数据进行重新归因, 将其中的归因结果给到回传服务. 回传服务如果判断某个转化事件还没有对应的回传, 则将这个归因结果进行回传, 如果已经回传过, 则不再回传.
- 点击时间迟到超过1天 经过统计, 这种情况发生的概率非常低, 直接忽略.