请求刚一发出,timing中显示是这样的
有时候过了几十秒之后才响应返回,页面加载完成。
已下内容纯粹是转载,只为做个记录。方便自己后续再次遇到问题时找到解决方案。
本文前戏较多,务实的同学可以直接跳到文末的结论。
由「钢的琴」网友脑洞大开延伸出了吉的他二的胡琵的琶,以及后来许嵩的「苏格拉没有底」,是否可以再拓展一下,得到哥本不爱吃哈根,哈根爱达斯等剧情乱入的关系。
上面跟本文要讨论的主题有什么关系?
没关系。
缘起
有用户反馈内部MIS系统慢,页面加载耗时长。前端同学们开组会提及此事,如何解决慢的问题。
最致命的是:偶发!你不能准确知道它抽风的时间点,无法在想要追查问题的时候必现它。 这只是一方面,另外,慢的可能实在太多了,那么问题来了,是前端导致的还是后端的问题?
对慢的定义也有待商榷,多久算慢?如果这个页面加载大量数据耗时增加那我认为这是正常的。但这个时限超过了一个合理的自然值,就变得不那么正常了,比如四五十秒,一分多钟。
最奇葩的是,如此久的耗时居然不会报超时错误,而是拿到正确返回后将页面呈现了出来!
可能的原因
初步猜测
初步的猜测可能是后端迟迟未返回造成浏览器处于等待状态。这个猜测是很合乎逻辑的,至少能够很合理地解释Chrome Dev Tool 网络面板中我们看到的状态pending
。
但我们不能停留在猜测阶段,要用事实说话,数据才不会骗人。这也正是本文将要展开的。
下面是另外一些被提出来的可能性。
Angular
Angular首当其冲。为什么,因为这个问题出现在后台MIS系统中,且这些系统多用Angular开发。
Angular :怪我咯。
因为问题多出现在基于Angular的MIS系统中,并且Angular的性能一直是被诟病的,所以听到不少的声音将矛头指向Angular。这似乎没什么好庇护的。Angular在整个项目中的前端部分扮演了很重的角色。树大招风,理所当然。
这让我想起初次接触到这个问题时,那是在七月份,芙蓉的爱马仕平台用户反馈了慢的问题,报到前端,顺便看了下,一看Pending
状态,觉得是后端未返回。只是情深缘浅当时也没有深入,同时洪堂大神负责去追查了。当时那个系统,很负责地说,没有用Angular。
所以这里可以为Angular正身,将其排除。
内部封装的commonResource
库
内部对Angular原生的resource
进行了封装,做了些数据的转换处理。既然上面Angular都被正身了,那么这里的怀疑也是站不住脚的。
Chrome插件
经查,网上好多呼声有说是Adblock等与网络有关的Chrome插件。可我不使用它已经很多年,那玩意儿太重,后来找到了算法更高级体量更轻便的µBlock。关键是后者也在我使用一段时间后放弃了,因为个人觉悟提高了(此处逼格开始膨胀),免费内容是需要广告支撑的,如果你不希望付费变成强制的话。所以现在一直是处于未开这类插件的状态。那么在未开广告屏蔽插件的情况下重现了问题,可以排除这类插件的影响了。
关于插件,此刻我的Chrome里唯一还会接管Chrome网络的便是代理插件SwitchSharp, 升级之后这货叫Switchy哦卖喝(与时俱进的我当然使用的是后者,此处逼格已经爆表)。
Chrome独家?
因为内部MIS只兼容了Chrome开发,所以不会有在除了Chrome之外的浏览器上使用的场景,并且其他浏览器上面追查问题也是很痛苦的事情。这里仅在火狐里进行了少量尝试,未复现。同时接到反馈,Safari里也未复现。但也不能肯定就只有Chrome存在问题。似乎这个对于问题的解决还不那么重要,所以先不管。
杀毒软件
后面会看到,在追查错误号ERR_CONNECTION_RESET
时引出了杀毒软件可能会导致Chrome工作不正常的情况,但这个可能也在稍后被排除人。
并且,我厂使用Mac的同学并没有安装杀软,依然是可以复现的。
重现
第一件事情便是重现。虽然是偶发,为了尽可能保存现场,还是想要手动将它刷出来。天不灭我,经过良久尝试,该问题被复现。于是各种截图,保存请求数据。这个时候还没有开启chrome://net-internals/#events
页面来捕获事件日志。
为以后引用方便,这里留下版本信息:
OS: Windows 7 Ultimate
Chrome:Version 39.0.2171.95 m
这是请求Pending
时的请求信息:
这是请求成功返回后:
可以看到Stalled
了1分多钟。神奇的是竟然不报超时错误而是成功返回了。
同时保存了请求头,响应头,还将本次问题请求保存成了CURL等。现场已经留下,感觉Bug不会存活太久了。
接下来就是对比正常请求跟这次异常请求的不同,一轮比较下来,未发现多少异常。
常态与变态的对比
请求头对比:
请求头的对比已丢失,但除了时间外,其余无差别。
响应头对比:
返回结果对比:
上面的对比意义不大,但还是要做的,万一发现有价值的情报了呢。
一次失败的尝试
解决问题时,习惯性地看有没有人已经碰过到类似问题,这样做的好处很明显: 如果有,站在巨人的肩上轻松地牛逼着; 如果没有,这是个机会。
于是信心满满地出发了,因为根据一条互联网准则,70%的问题已经有人解决过了,那些没有被解决的要么是现有技术达不到,要么是未被人发现。所以能够搜索出问题答案的概率还是蛮大的。
经过旷日持久的搜索,有价值的参考寥寥无几。可能是问题本身太过奇葩,遇到的人太少;也有可能问题过于晦涩,无法表述;抑或我搜索的关键词不够精准。 倒也不是说一个都没找到,但一般涉及网络日志的情况就无人问津了,无人问津了!
比如这个,一年多前被人问的,现在还没有一个回答。
还比如这个 > Chrome stalls when making multiple requests to same resource?
是后来作为参考的,也是无人问津了……
甚至自己也去问了一个,依然无人问津问津……
神秘的CACHE LOCK
上面提到,Stackoverflow上找到一个问题,跟现在需要解决一有些类似点:
- 偶发,并不是必然出现的。这里我们的问题也是偶发,很难复现,需要反复刷。
- 也是请求被
Pending
了很久,从请求的时间线来看,体现在Stalled
上。
这一刻,有一种感觉大概是这样的:
伟大的意大利的左后卫!他继承了意大利的光荣的传统。法切蒂、卡布里尼、马尔蒂尼在这一刻灵魂附体!格罗索一个人他代表了意大利足球悠久的历史和传统,在这一刻他不是一个人在战斗,他不是一个人!
突然看到了希望。该提问到没有给出什么建设性的意见,但它后面的追加编辑却给出了答案。过程是查看Chrome的网络日志,在事件里面发现有一个超时错误:
t=33627 [st= 5] HTTP_CACHE_ADD_TO_ENTRY [dt=20001] –> net_error = -409 (ERR_CACHE_LOCK_TIMEOUT)
耗时20秒之久!而且写得非常明显是ERR_CACHE_LOCK_TIMEOUT
。根据提问者贴出来的链接,了解到Chrome有一个缓存锁的机制。
具体源于一个今年6月分实现的一个补丁,加入了这么个机制,而这个机制的引入又源于2010年的一个issue。具体信息可以通过这个这里查看,下面引用如下。
Basically here is the situation:
The site author has a long-lived XHR being used to stream a slow response from the server. This XHR response is cachable (it is just really slow). They kick off the XHR asynchronously, and as data slowly arrives on it, update the progressive load of the webpage. Cool.
Now what happens if you try to load this page in multiple tabs of Chrome is: The first page starts to load just fine, but the second one does nothing. What has happened, is the background XHR of the first page load has acquired an exclusive lock to the cache entry, and the background XHR of the second page is stalled at “Waiting for cache…” trying to get a reader acc