淘宝的鱼相在 2012 年 8 月份发表了一篇文章,里面讲述了他们通过一个月的数据采集试验,得到的结果是:如果在浏览器的本页面刷新之前发送打点请求,各浏览器都有不同程度的点击丢失情况,具体点击丢失率统计大家请看下图(数据日期为 2012 年 7 月份):

  从图中可以看出,chrome,safari 这类 webkit 内核的浏览器在本页刷新之前发送打点,导致的丢失最为严重,分别为 61%,76%,而 ie8 丢失的情况最少,为7%。 (具体大家可以参看此文:http://ued.taobao.com/blog/?p=116 )

  原因分析:

  基于这种现象的产生,抛开打点服务器的处理性能和网络网速因素,我们从前端的角度来分析一下:

  当点击浏览器的当前页面某个元素后,当前页面创建 image 对象,并把这个 image 对象的 src 设为指向打点服务器的 url,发送打点请求。接着,当前页又向页面服务器发送页面跳转请求。假设页面服务器很快就返回了请求的 html, 则浏览器窗口接收到页面服务器的响应后,开始卸载保存在用户内存中的跟当前页面的有关的所有元素,包括刚才创建的用来向打点服务器发送打点的 image 对象。假设这时打点服务器对这个 image 发送的打点请求还在处理阶段,比如正在向数据库插入数据,还没有做 response 响应。这时,由于页面卸载了,image 对象被销毁了,该对象发送的 src 请求也就被终止了。导致这个打点失败了。类似于垃圾回收机制的影响。如果刚创建的 image 元素立即被内存垃圾回收了,而这个图片的 HTTP 请求尚未建立,那么在被回收时这个请求就会被取消,导致打点并没有真正发出。一个 http 请求的成功,必须是与后端服务器握手的成功。根据 DW 的胡大军检测到的数据,后端服务器监测到 3 次 tcp 协议的请求后,http 请求就被终止了。从这种现象上说明,浏览器在页面跳转之前确实发送了打点请求,由于页面跳转过快,发送打点的 image 对象因页面卸载而销毁了,请求也就被终止了。

  具体示意图如下:

  搜索端应用 searchweb 曾经做过试验,如果 searchweb 端响应延迟 100ms 后返回 html 数据,这种在页面卸载之前发送的请求打点(我们这里称它为 Beforeunload 打点)的回收率就明显上升,差不多得到了 100% 的回收率。页面服务器响应时间越长,Beforeunload 打点丢失率越低。因为页面服务器响应时间变长后,打点服务器有足够的时间处理打点请求, 然后返回给 image 对象做 response 响应。在本页面卸载刷新之前,image 对象的 src 请求已经成功得到了响应。这时再销毁该对象,对打点丢失已经没什么影响了。可以说,只要用来打点的 image 对象在打点成功之前没有被销毁,打点就不会丢失。如果链接跳转的方式是新窗口打开的,也就不会发生打点丢失的情况。因为当前页创建的打点 image 对象被没有被销毁。

  解决方案

  找到了 Beforeunload 打点丢失的原因,我们再来思考一下解决方案:

  因为页面创建的 image 对象在打点请求还没响应之前就被卸载了。如果单纯提升打点服务器性能,确实能加快 image 打点请求的响应返回,减少 Beforeunload 打点丢失情况。问题是,打点服务器要提升多少性能才是合适的?其实是页面服务器(应用服务器)跟打点服务器的一场竞赛。如果页面服务器性能大大提升了,而打点服务器没有提升,那么差距又拉大了。 又或者像 searchweb 做的试验那样,页面服务器(应用服务器)做延迟响应处理,这样 Beforeunload 打点丢失就能得到有效遏制。问题是这其实是对用户体验的极大伤害。现如今大家都在拼命提升应用页面服务器的性能,让页面能加载的更快,用户使用网页感觉更流畅。为了能在服务后端打上点,我们却要自己的应用延迟响应,跟我们提升用户体验的目标背道而驰。要知道,任何软件的应用,良好的用户体验是第一位的。所以,此路不通也。

  那有没有一种更好的办法?比如在页面刷新之前缓存打点数据,页面卸载刷新后,把原来页面刷新之前保存的打点数据取出来,再打点出去?这样,刷新后的页面打点跟点击 pv 基本上就是1:1 了,因为 pv 的计算也是根据跳转后的页面 url 来统计的。答案是:还真有。

  在浏览器页面被卸载的过程中,除了 window.name 属性对象外,其他对象元素都被销毁了。所以我们在页面卸载之前把请求打点的数据保存在 window.name 中。然后,页面刷新后,再取出这个 window.name 对象中保存的打点数据,发送打点请求。

  如果单存要存储打点数据,让该数据在页面刷新之后仍然能够被访问,有 cookie 对象,高级浏览器还支持 localStorage,为什么只推荐 window.name ? 原因如下:

  1. window.name 没有类似于 cookie,localStorage 的跨域问题。如果从 www.taobao.com 跳到 www.tmall.com,天猫的页面仍然能够得到在淘宝页面设置的 window.name 的值。而 cookie 只能跨主域。Localstorage 则不能跨域,连跨主域都不行,所以,在上面的场景中,如果使用 cookie 或者 localstorage,天猫页面无法获取淘宝页面设置的 cookie 或者 localstorage;

  2. Window.name 所有浏览器都支持,无论是 webkit 内核还是 IE 系列,所有浏览器都一概支持,而且表现一致。

  3. 页面刷新打点完毕后,原来被保存的打点数据已经没有用了,数据保存形式应该是临时性质的。cookie,localstorage 对象的设计是用来存储持久数据的,而 window.name 的设计,就是为保存页面临时数据的。如果页面窗口关掉了,内存回收了 widow 对象,window.name 也就不存在了。两者的需求与用途一致。

  如此看来,用 window.name 来存储 Beforeunload 打点请求的数据确实是个不错的主意,但是还有一个问题:如果 window.name 属性对象已经被应用程序使用了,也就是 window.name 已经被应用设为某个值了,这时我们再修改这个 window.name,岂不是对应用程序造成了破坏?

  答案是:没有关系。我们在页面跳转前,拼接完打点串后,可以这样写

window.name=window.name+”|%”+logURL;

  其中,logURL 是要打点的请求串。通过这样设置 window.name,原来应用程序的 window.name 的数据仍然被保存着。在页面跳转后的打点 js 中,可以这样写打点代码:

  var windowUrl=$.trim (window.name);
  if(windowUrl && windowUrl.indexOf ("|%")!==-1){
var urlArray=windowUrl.split ("|%"),orginName=urlArray[0];
windowUrl=urlArray[1];
  //把 window.name 归还给应用程序   window.name=orginName;
//发送打点请求   sendByImg (windowUrl);
}

  上面这段页面曝光打点的代码,放在标签里面,类似于 1688 中文站的 beacon.js,专门用来打点。因为打点代码在 head 标签里,一般在所有应用代码之前执行。(一般应用代码在 domready 的时候执行)所以在该代码执行完后,window.name 已经被设置为原来应用的值了。这样对应用造成的破坏也就不存在了。

  解决方案的示意图如下:

  解决方案数据验证效果

  蓝色为 Beforeunload 打点,红色为用 window.name 先存储打点数据,页面刷新跳转后,再从 window.name 取回打点数据,曝光打出的点。 Chrome 浏览器下的平均数据如下:

  可以看到,通过 window.name 的解决方式,打点效果提升了 13%。

  IE 平台下的总平均回收效果如图(包括 ie6,ie7,ie8,ie9,ie10 总和):

  IE 系列各浏览器的平均回收效果如图:

  (注:目前中文站的用到的 fdev-min.js 依赖的 jQuery1.7.2 的 attr 方法有个 bug,当 IE10 浏览器的 ie7 文档模式下调用 attr 方法时,程序会抛异常,导致了 window.name 不能被赋值。上图中,IE10 有可能因为受此影响,表现不如 Beforeunload 打点。关于这个 jQuery bug 的详细介绍,有兴趣的同学,可以看看这篇 jQuery 官网的 bug 帖 http://bugs.jquery.com/ticket/12577

  方案总结

  总体而言,新方案跟无痕打点的 Beforeunload 打点方式时相比,在 ie 上略有提升,chrome 浏览器上,提升最为明显,相比原来提升了 13%。这种方案还是很有效果的。

Beforeunload打点丢失原因分析及解决方案的更多相关文章

  1. Oracle包被锁定的原因分析及解决方案

    http://blog.csdn.net/jojo52013145/article/details/7470812 在数据库的开发过程中,经常碰到包.存储过程.函数无法编译或编译时会导致PL/SQL ...

  2. CMMI5级——原因分析及解决方案(Causal Analysis and Resolution)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010825142/article/details/15338085 聪明的人在出现问题的时候,除了 ...

  3. SQL查询速度慢的原因分析和解决方案

    SQL查询速度慢的原因分析和解决方案 查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建 ...

  4. IP访问频率限制不能用数组循环插入多个限制条件原因分析及解决方案

    14.IP频率限制不能用数组循环插入多个限制条件原因分析及解决方案: define("RATE_LIMITING_ARR", array('3' => 3, '6' => ...

  5. hive on spark:return code 30041 Failed to create Spark client for Spark session原因分析及解决方案探寻

    最近在Hive中使用Spark引擎进行执行时(set hive.execution.engine=spark),经常遇到return code 30041的报错,为了深入探究其原因,阅读了官方issu ...

  6. 在Android library中不能使用switch-case语句访问资源ID的原因分析及解决方案

    转自:http://www.jianshu.com/p/89687f618837 原因分析   当我们在Android依赖库中使用switch-case语句访问资源ID时会报如下图所示的错误,报的错误 ...

  7. window.open浏览器弹出新窗口被拦截—原因分析和解决方案

    最近在做项目的时候碰到了使用window.open被浏览器拦截的情况,在本机实验没问题,到了服务器就被拦截了,火狐有拦截提示,360浏览器拦截提示都没有,虽然在自己的环境可以对页面进行放行,但是对用户 ...

  8. 关于JVM内存溢出的原因分析及解决方案探讨

    前言:JVM中除了程序计数器,其他的区域都有可能会发生内存溢出. 0.什么是内存溢出 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出. 1. ...

  9. Connection reset原因分析和解决方案

    在使用HttpClient调用后台resetful服务时,“Connection reset”是一个比较常见的问题,有同学跟我私信说被这个问题困扰很久了,今天就来分析下,希望能帮到大家.例如我们线上的 ...

随机推荐

  1. -_-#【Angular】依赖注入

    AngularJS学习笔记 var BoxCtrl = function($scope, $element) { } var str = BoxCtrl.toString().replace(/\s/ ...

  2. -_-#【Canvas】FPS

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  3. //string scriptstrs = "<script>alert('欢迎光临!');</script>";

    //string scriptstrs = "<script>alert('欢迎光临!');</script>"; //if (!Page.ClientSc ...

  4. loadrunner11 录制脚步不成功,在录制概要出现“No Events were detected”,浮动窗口总是显示“0 Events”,解决办法

    打开ie浏览器,菜单栏上的工具----Internet选项---高级选项卡,去掉勾选“启用第三方浏览器扩展”,重启ie即可,重新录制脚本就可以成功. 刚刚开始以为自己解决不了这个问题,还想怎么办呢?一 ...

  5. Word Ladder——LeetCode

    Given two words (start and end), and a dictionary, find the length of shortest transformation sequen ...

  6. linux使用mount挂载iso文件

    mount -t iso9660 -o loop /home/user/XXX.iso /mnt/iso

  7. xml中不能直接添加ViewGroup

    我知道可以直接添加一个<View />的,今天想添加个容器类,然后后台动态添加SurfaceView到ViewGroup容器里,不过提示inflate报错了.难道ViewGroup不能直接 ...

  8. Android批量图片载入经典系列——使用LruCache、AsyncTask缓存并异步载入图片

    一.问题描写叙述 使用LruCache.AsyncTask实现批量图片的载入并达到下列技术要求 1.从缓存中读取图片,若不在缓存中,则开启异步线程(AsyncTask)载入图片,并放入缓存中 2.及时 ...

  9. 机房收费系统——在VB中将MSHFlexGrid控件中的数据导出到Excel

    机房收费系统中,好多查询的窗体都包含同一个功能:将数据库中查询到的数据显示在MSHFlexGrid控件中,然后再把MSHFlexGrid控件中的数据导出到Excel表格中. 虽然之前做过学生信息管理系 ...

  10. 顺序表JAVA代码

        publicclassSeqList{       final int defaultSize =10;     //默认的顺序表的最大长度     int maxSize;          ...