关于后端:为什么你的数据总是丢

50次阅读

共计 7252 个字符,预计需要花费 19 分钟才能阅读完成。

前言

大数据分析最外围的是数据,而数据的发送尤为重要。如何保证数据可能残缺、精确、及时地上传到指定的服务端,是所有数据采集 SDK 须要面临的外围问题。神策数据 Web JS SDK 综合思考数据发送的各项性能,设计并实现了一套实用于前端数据发送的计划。上面针对神策数据 Web JS SDK 数据发送计划进行具体的介绍,心愿可能给大家提供一些参考。
数据发送架构解析

简介

Web JS SDK 致力于高效且稳固地采集页面数据,这就须要一个稳固简洁的架构。网页是基于浏览器运行的,浏览器自身并没有多少缓存性能。因而,Web JS SDK 的架构设计中数据默认是即时采集、即时发送的策略。
数据发送架构图

Web JS SDK 数据发送架构如图 2-1 所示:

图 2-1 Web JS SDK 数据发送架构图

数据发送流程

Web JS SDK 数据从采集到发送的流程如图 2-2 所示:

图 2-2 Web JS SDK 数据发送流程图

通过上图能够晓得,数据发送流程次要包含上面几个步骤:
1. 首先会针对上传的事件以及属性做格局校验;

2. 批量发送配置下,将数据缓存到 localstorage,达到发送条件后通过网络发送。发送胜利后将已发送的数据删除,发送失败后期待下次发送机会;

3.H5 页面在 App 中关上,能够通过配置项将 H5 采集的数据上报给 App 端的 SDK(Android SDK 或 iOS SDK),数据实时通过 WebView 中间层上报给 App 端,App 端控制数据上报;

4. 实时发送配置下,数据会先保留到发送队列中,而后逐条发送,发送实现后(不管胜利与否)将已发送的数据删除;

5. 无论批量发送还是实时发送,申请工夫超过设置的超时工夫(实时发送默认为 3 秒,批量发送默认为 6 秒)后,都会勾销该申请。

数据发送工夫

Web JS SDK 监控到用户产生了行为,就会生成一条数据。为了最大限度地保证数据的准确性和安全性,会要求数据采集 SDK 尽快将数据同步到指定的服务端。因而,如何抉择适合的工夫是发送数据须要面临的外围问题。
工夫戳是指格林威治工夫 1970 年 01 月 01 日 00 时 00 分 00 秒(北京工夫 1970 年 01 月 01 日 08 时 00 分 00 秒)起至当初的总秒数。艰深的讲,工夫戳可能示意在一个特定工夫点曾经存在的、残缺的、可验证的数据。
物理工夫指的就是工夫戳;显示工夫是指物理工夫依照不同的时区,转化成合乎各地区的显示工夫。例如:咱们生存中常见的「北京工夫」和「纽约工夫」。

客户端工夫戳

客户端工夫戳会存在被用户批改的危险,从而导致采集的 time 不符合实际状况。针对工夫戳的问题,首先想到的应该是同步或者校准客户端工夫。然而,同步或者校准不仅须要网络权限,还须要一个稳固的工夫服务器。因而,这并不适用于 Web 端。针对这一问题,目前神策采纳的是“工夫修改”策略。
工夫修改机制解释如下:产生事件时的工夫(time)值为 t1,发送数据时的工夫(_flush_time)的值为 t2(客户端工夫,且 _flush_time 不入库),服务端接管到数据的工夫($recive_time)为 t3(服务端工夫)。如果 |t3 – t2| > 60s,则认为客户端的工夫不精确,会对事件触发工夫进行修改,修改后事件工夫 t1‘= t1 + (t3 – t2)。
上面以图 3-1 为例介绍“工夫修改”的过程:

图 3-1 事件工夫修改策略
用户在工夫戳 T1(8:00)时触发了一个 $pageview(页面浏览事件),而后事件存入本地缓存。数据采集 SDK 在用户客户端工夫 T2(9:00)时开始同步事件数据,而后服务端通过 http(s) 的 Request Header Date 能够获取到数据采集 SDK 发动 Request 时用户客户端工夫戳 T2。如果 T2 与 以后服务端的工夫戳 T3 的误差在可承受范畴内(60s,因为网络申请须要肯定的工夫),则阐明用户的客户端工夫精确,不须要做修改解决。如果 T2 与 T3 的误差较大(大于 60s,这里为 1 小时),则能够确定客户端工夫 T2 不精确,比服务端的工夫戳晚了 1 小时。因而,服务端在接管到事件的时候会把 $pageview 的工夫戳加上 1 小时,变成 9:00,这样就达到了“工夫修改”的成果。

服务端工夫戳

客户端工夫戳存在被批改的危险,且须要服务端进行查看修改,那么间接应用服务端的工夫戳是否更适合呢?的确能够间接应用服务端工夫,不过也会存在一些问题。例如:浏览器端按程序间断发送的多条数据,服务器接管到的程序并不一定是事件产生的程序。为了确保客户端数据发送程序的准确性,会在上面章节中介绍神策 Web JS SDK 采纳的队列发送形式。

数据发送的程序

Web JS SDK 采集的是网页端的数据,通过网络申请发送到指定的服务端。然而,网络申请是有稳定的,导致服务器接管到的程序并不一定是事件产生的程序。例如:点击按钮跳转页面,会先发送一个点击事件 A,在跳转到新页面时会发送一个页面浏览事件 B。事实上,常常会呈现用户的行为序列中 A 和 B 事件程序颠倒的问题。直观来看,用户行为会十分不合理:先触发了后一个页面的页面浏览事件,接下来的行为却是前一个页面的某个按钮的点击事件。
为了保障发送的程序,SDK 在数据发送之前会构建数据发送队列,保障用户行为数据依照正确的程序入库,造成正确的行为序列。这是如何做到的呢?
SDK 在发送数据队列中的数据时,默认会依照程序发送:以后一条数据返回发送胜利的状态后,顺次发送下一条数据,这保障了大部分失常流程的数据发送正确。然而,万一后面的数据发送卡住了,始终没有状态返回怎么办?SDK 的解决方案是设置超时工夫:
queue_timeout:队列发送超时工夫,默认值 300 毫秒,如果数据发送工夫超过 queue_timeout 还未返回后果,会强制发送下一条数据;
callback_timeout:回调函数超时工夫,默认值 200 毫秒,如果数据发送工夫超过 callback_timeout 还未返回后果,会强制执行回调函数;
datasend_timeout: 数据发送超时工夫,默认值 3000 毫秒,如果数据发送工夫超过 datasend_timeout 还未返回后果,会强制勾销该申请。
队列发送以及超时工夫的具体逻辑如图 4-1 所示:

图 4-1 队列发送策略

上面是队列发送的具体实现代码:

_.autoExeQueue = function(){ var queue = { // 简略队列 items : [], enqueue: function(val){this.items.push(val); this.start();}, dequeue: function(){ return this.items.shift(); }, getCurrentItem: function(){ return this.items[0]; }, // 主动循环执行队列 isRun: false, start: function(){ if(this.items.length > 0 && !this.isRun){this.isRun = true; this.getCurrentItem().start();} }, close: function(){ this.dequeue(); this.isRun = false; this.start();} }; return queue;};

数据发送形式

实时发送

大部分 Web JS SDK 数据采集应用的是即时采集、即时发送的策略,没有应用本地缓存。这样缩小了简单的缓存、读取和发送的管制流程。确保数据疾速、灵便、精确地发往指定的服务端。
常见的数据发送形式有 img 发送、ajax 发送和 beacon 发送,上面对这几种发送形式进行简要的介绍:

img 发送:默认应用 img 发送数据。对于跨域的兼容比拟好,发送的模式就是创立一个 img 元素,src 带上所有要发送的数据。执行过程无阻塞,不会影响用户体验。同时,绝对于 XMLHttpRequest 对象,发送 GET 申请性能上更好。局限性是 GET 申请所携带的数据大小是无限的;

ajax 发送:常见的一种申请形式,为了不影响业务流程,默认是异步发送数据。跨域时默认不携带申请域名下的 cookie。采纳的是 post 模式发送数据,数据较为平安,且发送数据的大小根本不受限制;

beacon 发送:当敞开页面发送数据的时候,常常受到页面容器 destroy 的影响,会导致数据来不及发送,进而产生失落的问题。sendBeacon 是浏览器的新发送策略,能够防止页面容器 destroy 时数据发送失落的问题。不过,目前该性能还未遍及。

SDK 默认是抉择立刻发送的 img 形式。也能够通过配置项,批改为 ‘ajax’ 或 ‘beacon’ 形式发送数据。上面从多个角度来剖析 img、ajax、sendBeacon 的优缺点,如表 5-1 所示:

表 5-1 发送形式比照

批量发送

缓存计划

咱们无奈防止当网络状况不佳时,数据发送失败的问题。数据一旦发送失败,因为没有缓存的逻辑,就会造成数据失落。一个常见的场景是:敞开页面时,有发送数据的需要。受过后网络环境和页面申请量的影响,发送数据时浏览器曾经敞开,导致数据申请存在未发送进来的危险,从而导致失落数据。基于以上起因 SDK 减少了缓存模式。
晚期的浏览器只能通过 cookie 来存储数据,起初逐步减少了 localStorage、sessionStorage 等存储形式,古代浏览器还蕴含 IndexedDB。比照这几种计划的特点,如表 5-2 所示:

表 5-2 缓存计划比照
cookie:因为存储的数据量不能超过 4K,数据量较小,且 cookie 会被带在 http header 中;
sessionStorage:只能够存储 session 内的数据;
IndexedDB:NoSQL 数据库,本地能够存储 250M 以上的数据。数据量很大,然而性能个别,且操作绝对麻烦;
localStorage:能够了解为是一个文件存储,大概存储 5M 的数据(不同浏览器实现不统一),数据量适中。
综合思考后,针对用户行为这样的数据,localStorage 绝对适合。同步的 API 能够确保数据的统一,同时性能好,频繁写入简直感觉不到延时。

批量发送策略

批量发送形式下,当数据产生后会先将数据存储在 localStorage 中(存储的数据有条数限度,最大能存储 200 条),达到发送条件后才会把存储在 localStorage 中的数据合并发送进来。其中,发送条件包含:
工夫距离:每隔肯定工夫(默认 6 秒)发送一次数据;
遇到 $pageview(或应用 quick(‘autoTrack’); 办法)和 $SignUp 也会立刻存储并且发送。
上述两个发送条件满足任意一个即可发送数据。批量发送默认应用跨域 ajax 的形式发送数据,且应用客户端零碎工夫标识数据。如果浏览器不反对跨域 ajax 发送数据,还是默认应用 img 且实时发送数据的形式。
如果数据发送不胜利,会将发送的数据保存起来,满足发送条件后,与之后的数据一起尝试发送。这样能够缩小网络申请、节俭服务器资源,并且无效地升高一些数据发送过程中的失落问题。
须要留神一下 localstorage 遵循浏览器同源策略,即子域名 localstorage 也不能共享,如表 5-3 所示:

表 5-3 浏览器的同源策略
例如:某个用户最初一条数据是在域名 a.test.com 下,而之后几天浏览的页面都是其余域名(如 b.test.com),这个时候是无奈发送 a.test.com 域名下未发送胜利的数据的。

H5 和 App 买通

集成了神策 Web JS SDK 的 H5 页面,在嵌入到 App 后,H5 和 App 的数据想要对立,能够将 H5 内的事件通过 App SDK 进行发送,事件发送前会增加上 App 采集到的预置属性。该性能默认是敞开的,如果须要开启,须要在 App 和 H5 端同时进行配置。
这种状况下 Web JS SDK 调用 App 裸露在 window 上的变量,将数据发送给 App,由 App 端二次加工解决后存入本地缓存,在合乎特定策略之后再进行数据同步。买通能将两端用户行为序列精确串联,补充 H5 数据设施相干的信息,升高 Web 端数据失落率等。

敞开页面发数据

对于 web 数据采集来说,敞开或跳转页面时始终存在上报数据失落的难题。因为浏览器通常会疏忽在 unload 事件处理器中产生的申请,包含异步 ajax 和图片等申请。失常状况下,因为设施、浏览器和网络等因素影响,Web JS SDK 数据会存在肯定的失落率(不超过 5%)。在敞开页面的状况下失落率会减少,尤其在挪动网络环境下失落更重大。置信很多读者都遇到过上面这几个问题:
统计某个链接的点击量,然而这个链接点击后间接跳转了;
统计页面时长问题,unload 的时候发送的统计失落了;
统计登录胜利数量,然而登录胜利后页面跳转了。
针对这个问题,上面简略介绍神策 Web JS SDK 罕用的几种形式:

服务端发送事件

一些要害的点击,须要非常精确的采集。例如:领取、登录胜利等事件是在敞开页面时发送的,倡议在服务端中减少埋点采集,前后端一起统计,进步数据准确性。

应用 setTimeout

提早 500ms 跳转页面,给 SDK 发送数据提供工夫。这样能够升高跳转页面发数据的失落率,但会影响用户体验,并且延迟时间不好估算。代码如下所示:

// 点击链接 function targetLinkIcon(url){// 提早跳转页面 setTimeout(function(){window.location.href = url;},500); // 神策自定义事件的办法 sensors.track(‘demo’,{});}

callback 回调中跳转页面

跳转页面的时候,调用 SDK 的 track 办法触发自定义事件,能够在第三个参数(回调函数)中跳转页面。失常状况下数据申请返回后,就会执行 callback 办法,然而思考到网络卡顿或者死机的状况,设置 callback_timeout(默认值 200ms)的超时来强制执行 callback。
callback_timeout 最好在 500ms 左右。设置的太长(例如 3s),页面可能跳转了还没有执行回调函数。设置的太短(例如 100ms),可能申请还没发胜利,就执行 callback 了。如果 callback 里是页面跳转的操作,那上条数据可能会失落。代码如下所示:

// 点击链接 $(‘a’).on(‘click’,function(e,url){e.preventDefault(); // 阻止默认跳转 sensors.track(‘a_click’, {}, function(){ location.href = url;}); // 把跳转操作加在 callback 里});

trackLink

如果有多个跳转链接须要监控,应用 setTimeout 或者 callback 比拟繁琐。SDK 为了简化操作,对代码做了封装,提供了 trackLink 办法,在理论应用时只须要调用 trackLink 就能够了。代码如下所示:

sa.trackLink = function(link, event_name, event_prop){addEvent(link,’click’,function(e){e.preventDefault(); // 阻止默认跳转 var hasCalled = false; setTimeout(redirectUrl, 1000); // 如果没有回调胜利,设置超时回调 function redirectUrl(){ if (!hasCalled) {hasCalled = true; location.href = link.href; // 把 A 链接的点击跳转, 改成 location 的形式跳转} } sa.track(event_name, event_prop, redirectUrl); // 把跳转操作加在 callback 里 });};

sa.trackLink(document.getElementById(‘index_detail’),’click_index_detail’,{});

sendBeacon

Beacon API 在网页敞开的状况下也能够发送数据,然而兼容性不佳,它有如下特点:
在闲暇的时候异步发送数据,并且是 POST 申请,不影响页面 JS、CSS Animation 等执行;
页面在 unload 状态下,也会发送数据,不影响页面的跳转,且不受同域限度;
可能被客户端优化发送,尤其在 Mobile 环境下,能够将 Beacon 申请合并到其余申请上一起解决。
sendBeacon 办法存在兼容性问题,除 IE 以外的大部分浏览器都曾经反对,兼容状况如图 6-1 所示:

图 6-1 sendBeacon 浏览器兼容性(图片来源于 MDN)

SDK 主动解决好了 sendBeacon 的兼容性问题,在不反对 sendBeacon 状况下还是会默认应用 img 发送数据,代码如下所示:

if (sendType === ‘beacon’ && typeof navigator.sendBeacon !== “function”) {sendType = ‘image’;}

如果想理解更多 sendBeacon 相干的内容,能够参考上面链接:
W3 规范形容:https://dvcs.w3.org/hg/webper…;
MDN 介绍:https://developer.mozilla.org…。

批量发送缓存数据

批量发送默认应用跨域 ajax 的形式发送数据,并且应用客户端零碎工夫标识数据。如果数据发送不胜利,会将发送的数据保存起来,满足发送条件后与后续数据一起尝试发送。如果 localStorage 里曾经存了超过 200 条数据,会导致批量发送性能生效。localStorage 中只保留这 200 条数据,新产生的数据应用 img 且实时发送数据的形式。当进入同域名的新页面时,会主动查看缓存中是否有数据,如果有会持续发送缓存数据。

总结

本文对于 Web JS SDK 数据发送的实现原理进行了解说,介绍了 SDK 解决数据发送的工夫、程序、形式以及升高敞开页面时发送数据失落率的几种形式,如果大家有更好的想法欢送一起探讨。对于数据采集、数据安全和数据处理等内容会在后续的文章中逐渐向大家介绍。

文章起源:神策技术社区

正文完
 0