赶知识网

Stalled:一次请求超时异常处理

2022-06-19 / 699次点击 php/mysql/apache

 最近项目中出现一个问题,前端每隔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,其中一个回答如下:


      Yes, 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


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


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


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


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


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


    


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


 结论

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


要解决这个问题,只要每次请求时在链接上加个随机值就好了。


Tips

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


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


有用 没用

Top10

沪ICP备09053415号 © 赶知识网