一次排查页面重复请求的经历

前段时间重构一个页面,页面中存在通过第三方JavaScript代码插入的动态广告(正常的产品需求),上线后发现第三方的广告资源存在重复请求的问题。由于控制广告插入的JavaScript代码由第三方提供,我们只负责按照他们要求的方式引入即可,所以对JavaScript代码内容并不了解,在这种情况下开始了艰难的排查过程。虽困难重重,但最终还是找到了原因,在此过程中有些收获,现将排查过程抽象如下:

注:以下过程和截图皆在Chrome浏览器中进行。

一、代码

<div id="container">
    <iframe src="/iframe-1" frameborder="0"></iframe>
    <iframe src="/iframe-2" frameborder="0"></iframe>
    <iframe src="/iframe-3" frameborder="0"></iframe>
</div>

<script>
    document.getElementById(‘container‘).innerHTML += ‘<p>上面是iframe</p>‘;
</script>

代码大意:页面上先渲染3个iframe(目前页面插入广告仍然以iframe作为主要实现形式),然后在最后一个iframe后面追加一个p元素

二、现象

1.页面:渲染正常

2.Network:存在重复的异常请求(Status是canceled)

三、排查过程

1.重复请求从何而来?

既然是解决重复请求的问题,那么重复请求从何而来是我们要解决的第一问题。
由于请求是从第三方的JavaScript代码中发出的,去读第三方压缩后的JavaScript代码更像无头苍蝇。整个过程就像在围城之外徘徊,心急如焚。后来静下心来发现Chrome的devtools中一个很关键的排查助力神器:Network下的Initiator

此列是什么意思呢?通俗地说就是触发请求的位置。
通过对比发现,同一个重复的请求发起的位置并不相同,以/iframe-1为例:
点击第一个请求的Initiator,跳转的位置(标黄位置):

点击第二个请求的Initiator,跳转的位置(标黄位置):

通过观察可以发现,第一个/iframe-1请求是由于正常渲染iframe元素自动触发的,第二个/iframe-1请求是在执行JavaScript代码(作用拼接DOM节点)时触发的,然而对于触发第二个/iframe-1请求的那行JavaScript代码,其真实意图仅仅是拼接一个p元素而已,并不期望其他额外的事情(比如触发新的请求)发生。另外,对于/iframe-2和/iframe-3的第二次请求的触发点都是那段拼接DOM节点的JavaScript代码,至此,产生问题的罪魁祸首已经浮出水面,接下来我们分析下产生重复请求的原因。

2.为什么会重复请求?

产生重复请求的JavaScript代码

document.getElementById(‘container‘).innerHTML += ‘<p>上面是iframe</p>‘;

翻译成:

document.getElementById(‘container‘).innerHTML = document.getElementById(‘container‘).innerHTML + ‘<p>上面是iframe</p>‘;

意思就明了多了:先获取id为container的div元素的所有内部HTML,将其拼接p元素后,再赋值给container的innerHTML。

这个过程会导致iframe元素的重新渲染,也就会引发iframe对应的请求重新触发。

所以,同一个请求会触发两次的原因:页面加载时渲染iframe元素会触发第一次请求,执行JavaScript代码导致iframe重新渲染触发第二次请求。

找到了问题的原因,解决问题的办法也就水到渠成了,将

document.getElementById(‘container‘).innerHTML += ‘<p>上面是iframe</p>‘;

改为:

var div = document.createElement(‘div‘);
var text = document.createTextNode(‘广告‘);
div.appendChild(text);

document.getElementById(‘container‘).appendChild(div);

问题解决了,不过,还有一个疑问:为什么渲染iframe产生的第一个请求的状态是canceled?

3.为什么重复的请求的Status是canceled?

首先Status是canceled代表什么意思呢?
从其字面意思理解,代表此请求被取消了,即此请求在发给服务器端之前就被浏览器取消了,也就是说此请求根本就没有从浏览器发出去,更不可能到达服务器,所以状态是canceled而不是HTTP状态码也就不难理解了。

那第一次的请求为什么会被浏览器取消呢?
用关键词“chrome cancel request”谷歌了一下,在stack overflow上找到了一个比较全面的解答,截图如下:

其中红色标注即为我们要寻找的答案。

根据截图大概梳理一下,Chrome浏览器会取消请求的几种场景:

  • 触发请求的DOM元素被删除了(比如img元素还没有加载完就被删除了)
  • 做了一些不必要的数据加载(比如开始加载iframe后改变其src或重写其内容)
  • 大量的请求指向同一个服务器,并且前面请求的网络问题表明后续的请求也走不通(DNS查询错误,前面的请求报400)

至此,整个过程中的疑问点一一解开了。

四、总结

现在再回顾此bug,产生的原因并不高深,但整个排查过程确实值得总结。小结一下:

1.对于第三方库报错,切莫妄图通过通读并熟悉整个库后解决问题,通读代码只会浪费解决问题的时间,弄明白调用关系才是王道

2.Chrome开发者工具中的Network > Initiator代表请求是从哪里触发的,对于定位请求非常有用,尤其是对于一些第三方库中发出的请求

3.请求状态为canceled,表示请求被浏览器取消了,并没有从浏览器发出去,更不可能进到服务器

4.Chrome浏览器取消请求的几种情景,见上图

5.element.innerHTML += HTMLStr 表示将原有的子节点和新的节点拼接后再重新赋值,会导致节点元素重新渲染,节点内容中含有iframe时慎用

原文地址:https://www.cnblogs.com/jlfw/p/11827185.html

时间: 2024-08-29 06:51:31

一次排查页面重复请求的经历的相关文章

JS解决页面多次,重复请求 的2个方法

最近的一个case,在web页下,鼠标向下滚动翻页动态请求数据的时候.一般都是判断当前页面距离顶部的高度与整个html的长度之差来决定是否请求下一页的数据. 如果不进行相应处理,滑动事件会触发多次,导致请求多次或者重复请求(取决于代码设计) 思路1: 滑动到下方第一次触发请求后,移除滑动监听,等待请求返回,继续监听滑动事件. 思路2: 对于一次滑动有产生多次不重复的请求的情况.可以让这些请求都进入队列,滑动时判断标志位,为真则取出队列第一个请求并执行,为假则不执行.待请求返回时设置标志位true

微信页面注册时重复请求的问题

以下转载于同事空间日志,做个记录: 最近在微信端的开发当中发现微信浏览器会重复请求后台,搞的我是寝食难安,业务逻辑就是一用户注册逻辑,本地测试没问题,放生产上有时就会报"用户已存在"异常,开始以为是代码逻辑里有重复插入的逻辑,在我来来回回反反复复的看了n遍依然没有发现有重复插入的逻辑时,我又把注意力移到了jquery的ajax上,以为是jquery里ajax重复提交的,于是我改变ajax提交为普通的href连接,结果错误依然.这时我感觉很可能是尼玛微信的套路,最后用了一很勉强的办法暂时

JAVA–利用Filter和session防止页面重复提交

JAVA–利用Filter和session防止页面重复提交解决思路:1 用户访问表单页面,先经过过滤器,过滤器设置一个随机id作为token令牌, 并将该token放入表单隐藏域中.2 表单响应到浏览器,用户填充数据后提交请求;3 请求经过过滤器,过滤器获取表单中的令牌进行验证,如果和之前生成的令牌一致,则将请求放行,并且清空令牌;4 如果用户重复提交表单,请求经过过滤器,过滤器进行验证.因为第一次放行后令牌已经清空失效,令牌不一致,不放行.跳转到提醒界面. 需用知识:1 过滤器基础知识2 se

juqery 实现 防止当前页面重复点击,以减轻服务器压力

<script> //防止当前页面重复点击,以减轻服务器压力 $(document).ready(function () { var current_url = location.pathname; $("a").each(function () { if (current_url == $(this).attr("href")) { $(this).click(function () { return false; }); $(this).dblcli

php利用session防止页面重复刷新

如何防止页面重复刷新,在php环境下可以利用session来轻松实现. b.php的代码 <?php //只能通过post方式访问 if ($_SERVER['REQUEST_METHOD'] == 'GET') {header('HTTP/1.1 404 Not Found'); die('亲,页面不存在');} session_start(); $fs1=$_POST['a']; $fs2=$_POST['b']; //防刷新时间,单位为秒 $allowTime = 30; //读取访客ip

Struts2框架action路径问题心得----》页面url请求怎么找action

Struts2 页面url请求怎么找action Struts2 页面url请求如何找action 1.我们使用最原始的方法去查找action,不同注解. struts.xml文件先配置 <!-- 新闻信息action --><action name="newsInfoAction" class="com.xxx.NewsInfoAction"><result name="add">news/addNewsIn

使用Filter防止浏览器缓存页面或请求结果

只需要两步: 1.定义一个Filter: public class NoCacheFilter implements Filter{ @Override public void destroy(){ } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException{ HttpServletRespons

Ajax打开三种页面的请求

xmlhttprequest对象可以打开两种方式的页面请求 1,.asmx格式的webservice页面. 2,.aspx格式的web窗体 其中web窗体可以是调用一新建的web窗体,同时调用和被调用页面可以是同一个页面,表达的可能不够清楚,还是看示例吧. 一.调用.asmx格式的webservice页面 1,新建一个ASP.NET应用程序,在该应用程序中添加一web服务页面(webservice页面),此时项目中有三个页面,web窗体页面(Default.aspx).webservice页面(

通过LoadRunner - Analyze详细分析页面元素请求

众所周知LoadRunner录制某个链接,包括动态请求与js.css.jpg等静态请求. web_custom_request("动态请求", "URL=http://www.baidu.com/", "Method=GET", "Resource=0", "RecContentType=text/html", "Referer=", "Snapshot=t1.inf"