Stalled:一次请求超时异常


简述

  最近项目中出现一个问题,前端每隔1秒向同一个url发起请求,第一次请求响应时间2秒左右,此后每次请求耗时会增加大约1秒,直到超时。

定位和验证

  • 后台

    增加日志观察后台服务耗时情况,发现每次均耗时2秒左右,和前端第一次请求耗时差不多,后续也没有明显增长,基本可以排除后台服务的问题

  • 前端

    首先是在项目指定的浏览器chrome上发现的问题,之后分别测试了edeg/firefox/ie,只在edeg上复现了问题,而edeg又是使用的Chromium内核,因此猜测是Chrome的某种机制导致的问题。

    打开chrome控制台,查看请求耗时的详情,如下:

    

    观察多次请求的耗时明细,发现真正发起请求到响应的时间依然稳定在2秒左右,这和后台观察到的情况是一样的。

    真正导致请求超时的是Connection Start:Stalled,这一项每次稳定增长1秒左右,最终导致超时。

    那么这个Stalled是何方神圣呢,chrome文档(https://developer.chrome.com/docs/devtools/network/reference/)如下:

    Here's more information about each of the phases you may see in the Timing tab:

    •   Queueing. The browser queues requests when:
      •   There are higher priority requests.    有更高优先级的请求
      •   There are already six TCP connections open for this origin, which is the limit. Applies to HTTP/1.0 and HTTP/1.1 only.  针对每个源,最多打开6个TCP连接
      •   The browser is briefly allocating space in the disk cache    浏览器正在准备缓存
    •   Stalled. The request could be stalled for any of the reasons described in Queueing.

    Stalled:在满足Queueing的任意一种条件时,请求将会停滞(阻塞)。

    更高优先级的请求此时并不存在,排除;TCP连接数量限制此时也未达上限,而且同网站的其他请求并未受影响,排除(对于TCP连接和数量限制后续可以再研究一下);

    同时在StackOverFlow上我找到一个类似的问题:https://stackoverflow.com/questions/27513994/chrome-stalls-when-making-multiple-requests-to-same-resource,高赞回答如下:

      This behavior is due to Chrome locking the cache and waiting to see the result of one request before requesting the same resource again. The answer is to find a way to make the requests unique.

    I added a random number to the query string, and everything is working now.

    完美契合文档中的缓存阻塞,马上验证

    

  验证1:在请求url上添加随机值

    将url由http://localhost:8080/master/timed修改为http://localhost:8080/master/timed?HxkNkz4Wwe

    结果:问题解决,所有请求并发进行,不再阻塞!    

 

     那么能不能通过后端的响应来解决问题呢?于是又做了一些验证

  验证2:修改响应头,试图控制浏览器的缓存行为

    分别修改Cache-Control响应头的值为no-store/no-cache/no-store,no-cache/max-age=3, must-revalidate,观察

    结果:前面3种毫无反应;对于第4种,结果如下:

    

    没有缓存时,请求从后台获取数据,然后缓存到本地;有缓存时,请求直接从磁盘缓存获取数据(有时候还会获取到空值),对于接口类的请求来说这有可能获取到过期的数据,显然是不可接受的,事实上,一般之后资源类的数据(js/css/图片等)才会通过缓存获取。

 

  另外,针对文档中的第二点,TCP连接数量限制也做了一次验证:

  验证3:最大TCP连接数

  将前端请求频率缩小为10ms一次并且带上随机值,并发数量超过6个以后的确会发生阻塞现象:      

 

 

 

  如图,即使url不同也只能同时发出6个请求,前面的请求完成之后后面的才能进行。

 

 结论

在拿到响应之前,chrome会将资源(以url表示)相关的缓存锁住,后续所有相同的url请求都必须在队列中等待,直到前面的请求及缓存处理完之后才能依次进行。

解决这个问题有两种方法:

1. 每次请求时在链接上加个随机值

2. 对于完全相同的请求不并发进行,而是等上一个完成之后才进行下一个

Tips

这次解决这个问题花的时间稍微有点长,原因是一开始的方向有误,直接去翻后台代码和脚本浪费了不少时间。

正确的做法是首先确定问题发生在哪里:前端还是后端,如果是后端的话再看是服务器还是服务本身(老二分法了),确定是服务本身之后再去看细节,不花无谓的时间。

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM