作为系列文章的第三篇,本文将重点探讨数据采集层中的用户行为数据采集系统。这里的用户行为,指的是用户与产品UI的交互行为,主要表现在Android App、IOS App与Web页面上。这些交互行为,有的会与后端服务通信,有的仅仅引起前端UI的变化,但是不管是哪种行为,其背后总是伴随着一组属性数据。对于与后端发生交互的行为,我们可以从后端服务日志、业务数据库中拿到相关数据;而对于那些仅仅发生在前端的行为,则需要依靠前端主动上报给后端才能知晓。用户行为数据采集系统,便是负责从前端采集所需的完整的用户行为信息,用于数据分析和其他业务。
举个例子,下图所示是一次营销活动(简化版)的注册流程。如果仅仅依靠后端业务数据库,我们只能知道活动带来了多少新注册用户。而通过采集用户在前端的操作行为,则可以分析出整个活动的转化情况:海报页面浏览量—>>点击”立即注册”跳转注册页面量—>>点击“获取验证码”数量—>>提交注册信息数量—>>真实注册用户量。而前端用户行为数据的价值不仅限于这样的转化率分析,还可以挖掘出更多的有用信息,甚至可以与产品业务结合,比如笔者最近在做的用户评分系统,便会从用户行为中抽取一部分数据作为评分依据。
在早期的产品开发中,后端研发人员每人负责一个摊子,虽然也会做些数据采集的事情,但是基本上只针对自己的功能,各做各的。通常做法是,根据产品经理提出的数据需求,设计一个结构化的数据表来存储数据,然后开个REST API给前端,用来上报数据;前端负责在相应的位置埋点,按照协商好的数据格式上报给后端。随着业务的发展,这样的做法暴露了很多问题,给前后端都带来了混乱,主要表现在:前端四处埋点,上报时调用的API不统一,上报的数据格式不统一;后端数据分散在多个数据表中,与业务逻辑耦合严重。
于是,我们考虑做一个统一的用户行为数据采集系统,基本的原则是:统一上报方式、统一数据格式、数据集中存储、尽可能全量采集。具体到实现上,归纳起来主要要解决三个问题:
- 采什么。搞清楚需要什么数据,抽象出一个统一的数据格式。
- 前端怎么采。解决前端如何有效埋点、全量采集的问题。
- 后端怎么存。解决数据集中存储、易于分析的问题。
采什么
用户在前端UI上的操作,大多数表现为两类:第一类,打开某个页面,浏览其中的信息,然后点击感兴趣的内容进一步浏览;第二类,打开某个页面,根据UI的提示输入相关信息,然后点击提交。其行为可以归纳为三种:浏览、输入和点击(在移动端,有时也表现为滑动)。其中,浏览和点击是引起页面变化和逻辑处理的重要事件,输入总是与点击事件关联在一起。
因此,浏览和点击便是我们要采集的对象。对于浏览,我们关注的是浏览了哪个页面,以及与之相关的元数据;对于点击,我们关注的是点击了哪个页面的哪个元素,与该元素相关联的其他元素的信息,以及相关的元数据。页面,在Android与IOS上使用View名称来表示,在Web页面上使用URL(hostname+pathname)来表示。元素,使用前端开发中的UI元素id来表示。与元素相关联的其他元素信息,指的是与“点击”相关联的输入/选择信息,比如在上面的注册页面中,与“提交”按钮相关联的信息有手机号、验证码、姓名。元数据,是指页面能提供的其他有用信息,比如URL中的参数、App中跳转页面时传递的参数等等,这些数据往往都是很重要的维度信息。
除了这些页面中的数据信息,还有两个重要的维度信息:用户和时间。用户维度,用来关联同一用户在某个客户端上的行为,采用的方案是由后端生成一个随机的UUID,前端拿到后自己缓存,如果是登录用户,可以通过元数据中的用户id来关联;时间维度,主要用于数据统计,考虑到前端可能延迟上报,前端上报时会加上事件的发生时间(目前大多数正常使用的移动端,时间信息应该是自动同步的)。
综合起来,将前端上报的数据格式定义如下。uuid、event_time、page是必填字段,element是点击事件的必填字段,attrs包含了上述的元数据、与元素相关联的其他元素的信息,是动态变化的。
{
"