关于请求被挂起页面加载缓慢问题的追查

缘起

最致命的是:偶发!你不能准确知道它抽风的时间点,无法在想要追查问题的时候必现它。这只是一方面,另外,慢的可能实在太多了,那么问题来了,是前端导致的还是后端的问题?

对慢的定义也有待商榷,多久算慢?如果这个页面加载大量数据耗时增加那我认为这是正常的。但这个时限超过了一个合理的自然值,就变得不那么正常了,比如四五十秒,一分多钟。

最奇葩的是,如此久的耗时居然不会报超时错误,而是拿到正确返回后将页面呈现了出来!

可能的原因

初步猜测

初步的猜测可能是后端迟迟未返回造成浏览器处于等待状态。这个猜测是很合乎逻辑的,至少能够很合理地解释ChromeDevTool网络面板中我们看到的状态ping。

但我们不能停留在猜测阶段,要用事实说话,数据才不会骗人。这也正是本文将要展开的。

下面是另外一些被提出来的可能性。

Angular

Angular首当其冲。为什么,因为这个问题出现在后台MIS系统中,且这些系统多用Angular开发。

Angular:怪我咯。

因为问题多出现在基于Angular的MIS系统中,并且Angular的性能一直是被诟病的,所以听到不少的声音将矛头指向Angular。这似乎没什么好庇护的。Angular在整个项目中的前端部分扮演了很重的角色。树大招风,理所当然。

所以这里可以为Angular正身,将其排除。

内部封装的commonResource库

内部对Angular原生的resource进行了封装,做了些数据的转换处理。既然上面Angular都被正身了,那么这里的怀疑也是站不住脚的。

Chrome插件

经查,网上好多呼声有说是Adblock等与网络有关的Chrome插件。可我不使用它已经很多年,那玩意儿太重,后来找到了算法更高级体量更轻便的µBlock。关键是后者也在我使用一段时间后放弃了,因为个人觉悟提高了(此处逼格开始膨胀),免费内容是需要广告支撑的,如果你不希望付费变成强制的话。所以现在一直是处于未开这类插件的状态。那么在未开广告屏蔽插件的情况下重现了问题,可以排除这类插件的影响了。

关于插件,此刻我的Chrome里唯一还会接管Chrome网络的便是代理插件SwitchSharp,升级之后这货叫Switchy哦卖喝(与时俱进的我当然使用的是后者,此处逼格已经爆表)。

Chrome独家?

杀毒软件

后面会看到,在追查错误号ERR_CONNECTION_RESET时引出了杀毒软件可能会导致Chrome工作不正常的情况,但这个可能也在稍后被排除人。

并且,我厂使用Mac的同学并没有安装杀软,依然是可以复现的。

重现

第一件事情便是重现。虽然是偶发,为了尽可能保存现场,还是想要手动将它刷出来。天不灭我,经过良久尝试,该问题被复现。于是各种截图,保存请求数据。这个时候还没有开启chrome://net-internals/events页面来捕获事件日志。看是否有错误或异常发生。

再次经过旷日持久的机械操作,重现了!这次,日志在手,天下我有。感觉Bug不会存活多久了。

ChromeDevTools网络面板截图:

由上面的截图看到,本次出问题的请求总耗时42.74秒。

可以预见,通过捕获的日志完全可以看到Stalled那么久都发生了些什么鬼。

话不多说,切换到事件捕获页面,定位到出问题的请求,查看其详情。同时将该日志导出,永久保存!作为纪念,也方便以后再次导入查看。有兴趣的同学可以访问下方下载后进行导入,就可以清晰地查看到现场了,就好像你亲历了整个犯罪现场一样。

日志还原

下载该日志文件

在Chrome新开一个标签输入chrome://net-internals/#events

切换到Import,选择刚才下载的JSON文件进行导入

切换到Events,定位到

此刻右边出现的便是该问题请求的详细日志。

日志解读

下面不妨把日志文件贴出来先:

193486:URL_REQUEST[st=0]+REQUEST_ALIVE[dt=42741]t=1[st=0]URL_REQUEST_DELEGATE[dt=0]t=1[st=0]+URL_REQUEST_START_JOB[dt=42740]--load_flags=339804160(BYPASS_DATA_REDUCTION_PROXY|MAYBE_USER_GESTURE|REPORT_RAW_HEADERS|VERIFY_EV_CERT)--method="GET"--priority="LOW"--url=""t=2[st=1]URL_REQUEST_DELEGATE[dt=0]t=2[st=1]HTTP_CACHE_GET_BACKEND[dt=0]t=2[st=1]HTTP_CACHE_OPEN_ENTRY[dt=0]t=2[st=1]HTTP_CACHE_ADD_TO_ENTRY[dt=0]t=2[st=1]HTTP_CACHE_READ_INFO[dt=0]t=2[st=1]URL_REQUEST_DELEGATE[dt=0]t=2[st=1]+HTTP_STREAM_REQUEST[dt=2]t=4[st=3]HTTP_STREAM_REQUEST_BOUND_TO_JOB--source_depency=193488(HTTP_STREAM_JOB)t=4[st=3]-HTTP_STREAM_REQUESTt=4[st=3]+HTTP_TRANSACTION_SEND_REQUEST[dt=0]t=4[st=3]HTTP_TRANSACTION_SEND_REQUEST_HEADERS--GET/release/getReleaseHistory?projectId=/1.1Host::keep-aliveAccept:application/json,text/plain,*/*User-Agent:Mozilla/5.0(;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/39.0.2171.95Safari/537.36Referer:;q=0.8Cookie:[268byteswerestripped]t=4[st=3]-HTTP_TRANSACTION_SEND_REQUESTt=4[st=3]+HTTP_TRANSACTION_READ_HEADERS[dt=21301]t=4[st=3]HTTP_STREAM_PARSER_READ_HEADERS[dt=21301]--net_error=-101(ERR_CONNECTION_RESET)t=21305[st=21304]HTTP_TRANSACTION_RESTART_AFTER_ERROR--net_error=-101(ERR_CONNECTION_RESET)t=21305[st=21304]-HTTP_TRANSACTION_READ_HEADERSt=21305[st=21304]+HTTP_STREAM_REQUEST[dt=3]t=21307[st=21306]HTTP_STREAM_REQUEST_BOUND_TO_JOB--source_depency=193494(HTTP_STREAM_JOB)t=21308[st=21307]-HTTP_STREAM_REQUESTt=21308[st=21307]+HTTP_TRANSACTION_SEND_REQUEST[dt=3]t=21308[st=21307]HTTP_TRANSACTION_SEND_REQUEST_HEADERS--GET/release/getReleaseHistory?projectId=/1.1Host::keep-aliveAccept:application/json,text/plain,*/*User-Agent:Mozilla/5.0(;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/39.0.2171.95Safari/537.36Referer:;q=0.8Cookie:[268byteswerestripped]t=21311[st=21310]-HTTP_TRANSACTION_SEND_REQUESTt=21311[st=21310]+HTTP_TRANSACTION_READ_HEADERS[dt=21304]t=21311[st=21310]HTTP_STREAM_PARSER_READ_HEADERS[dt=21304]--net_error=-101(ERR_CONNECTION_RESET)t=42615[st=42614]HTTP_TRANSACTION_RESTART_AFTER_ERROR--net_error=-101(ERR_CONNECTION_RESET)t=42615[st=42614]-HTTP_TRANSACTION_READ_HEADERSt=42615[st=42614]+HTTP_STREAM_REQUEST[dt=12]t=42627[st=42626]HTTP_STREAM_REQUEST_BOUND_TO_JOB--source_depency=193498(HTTP_STREAM_JOB)t=42627[st=42626]-HTTP_STREAM_REQUESTt=42627[st=42626]+HTTP_TRANSACTION_SEND_REQUEST[dt=2]t=42627[st=42626]HTTP_TRANSACTION_SEND_REQUEST_HEADERS--GET/release/getReleaseHistory?projectId=/1.1Host::keep-aliveAccept:application/json,text/plain,*/*User-Agent:Mozilla/5.0(;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/39.0.2171.95Safari/537.36Referer:;q=0.8Cookie:[268byteswerestripped]t=42629[st=42628]-HTTP_TRANSACTION_SEND_REQUESTt=42629[st=42628]+HTTP_TRANSACTION_READ_HEADERS[dt=112]t=42629[st=42628]HTTP_STREAM_PARSER_READ_HEADERS[dt=112]t=42741[st=42740]HTTP_TRANSACTION_READ_RESPONSE_HEADERS--HTTP/1.1200OKDate:Fri,02Jan201509:51:48GMTContent-Type:application/json;charset=UTF-8Transfer-Encoding:chunkedConnection:keep-aliveCache-Control:no-cachetracecode:31079600320335034634010217tracecode:31079600320537995786010217Server:Apachet=42741[st=42740]-HTTP_TRANSACTION_READ_HEADERSt=42741[st=42740]HTTP_CACHE_WRITE_INFO[dt=0]t=42741[st=42740]HTTP_CACHE_WRITE_DATA[dt=0]t=42741[st=42740]HTTP_CACHE_WRITE_INFO[dt=0]t=42741[st=42740]URL_REQUEST_DELEGATE[dt=0]t=42741[st=42740]-URL_REQUEST_START_JOBt=42741[st=42740]URL_REQUEST_DELEGATE[dt=0]t=42741[st=42740]HTTP_TRANSACTION_READ_BODY[dt=0]t=42741[st=42740]HTTP_CACHE_WRITE_DATA[dt=0]t=42741[st=42740]HTTP_TRANSACTION_READ_BODY[dt=0]t=42741[st=42740]HTTP_CACHE_WRITE_DATA[dt=0]t=42742[st=42741]-REQUEST_ALIVE

首先,日志显示的总耗时与上面网络面板截图的总耗时是吻合的,都是42.74秒,说明我们定位正确。

日志第一列为时间线,自请求发起时算。第二列为每步操作所逝去的时间,时间差的概念,与第三列里面的dt不同,它会积累前面的耗时。第三列为具体的事件,以及相应事件的耗时dt,此耗时为绝对耗时。

+号对应事件开始,-号对应事件结束,也就是说他们必然成对出现。住里是展开后更加详细的子事件。直到不能再细分。

如果说一开始接触到这个日志时手足无措的话,我们来看一下正常情况下的日志是怎样的,有对比才有发现。

以下随便摘取一次正常请求的日志,如下:

384462:URL_REQUEST[st=0]+REQUEST_ALIVE[dt=172]t=1556[st=0]URL_REQUEST_DELEGATE[dt=0]t=1556[st=0]+URL_REQUEST_START_JOB[dt=171]--load_flags=335609856(BYPASS_DATA_REDUCTION_PROXY|MAYBE_USER_GESTURE|VERIFY_EV_CERT)--method="GET"--priority="LOW"--url=""t=1557[st=1]+URL_REQUEST_DELEGATE[dt=4]t=1557[st=1]DELEGATE_INFO[dt=4]--delegate_info="extensionTampermonkey"t=1561[st=5]-URL_REQUEST_DELEGATEt=1561[st=5]HTTP_CACHE_GET_BACKEND[dt=0]t=1561[st=5]HTTP_CACHE_OPEN_ENTRY[dt=1]--net_error=-2(ERR_FAILED)t=1562[st=6]HTTP_CACHE_CREATE_ENTRY[dt=0]t=1562[st=6]HTTP_CACHE_ADD_TO_ENTRY[dt=0]t=1562[st=6]URL_REQUEST_DELEGATE[dt=0]t=1562[st=6]+HTTP_STREAM_REQUEST[dt=2]t=1564[st=8]HTTP_STREAM_REQUEST_BOUND_TO_JOB--source_depency=384467(HTTP_STREAM_JOB)t=1564[st=8]-HTTP_STREAM_REQUESTt=1564[st=8]+HTTP_TRANSACTION_SEND_REQUEST[dt=1]t=1564[st=8]HTTP_TRANSACTION_SEND_REQUEST_HEADERS--GET/release/getReleaseHistory?projectId=/1.1Host::keep-aliveAccept:application/json,text/plain,*/*User-Agent:Mozilla/5.0(;WOW64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/39.0.2171.95Safari/537.36Referer:;q=0.8Cookie:[2642byteswerestripped]t=1565[st=9]-HTTP_TRANSACTION_SEND_REQUESTt=1565[st=9]+HTTP_TRANSACTION_READ_HEADERS[dt=161]t=1565[st=9]HTTP_STREAM_PARSER_READ_HEADERS[dt=160]t=1725[st=169]HTTP_TRANSACTION_READ_RESPONSE_HEADERS--HTTP/1.1200OKDate:Sat,03Jan201512:23:54GMTContent-Type:application/json;charset=UTF-8Transfer-Encoding:chunkedConnection:keep-aliveCache-Control:no-cachetracecode:34020tracecode:253893130010320Server:Apachet=1726[st=170]-HTTP_TRANSACTION_READ_HEADERSt=1726[st=170]HTTP_CACHE_WRITE_INFO[dt=0]t=1726[st=170]HTTP_CACHE_WRITE_DATA[dt=0]t=1726[st=170]HTTP_CACHE_WRITE_INFO[dt=0]t=1726[st=170]+URL_REQUEST_DELEGATE[dt=1]t=1726[st=170]DELEGATE_INFO[dt=1]--delegate_info="extensionTampermonkey"t=1727[st=171]-URL_REQUEST_DELEGATEt=1727[st=171]-URL_REQUEST_START_JOBt=1727[st=171]URL_REQUEST_DELEGATE[dt=0]t=1727[st=171]HTTP_TRANSACTION_READ_BODY[dt=0]t=1727[st=171]HTTP_CACHE_WRITE_DATA[dt=1]t=1728[st=172]HTTP_TRANSACTION_READ_BODY[dt=0]t=1728[st=172]HTTP_CACHE_WRITE_DATA[dt=0]t=1728[st=172]-REQUEST_ALIVE

发送请求头`+HTTP_TRANSACTION_SEND_REQUEST[dt=1]`

读取响应头`+HTTP_TRANSACTION_READ_HEADERS[dt=161]`

这是正常的情况下,没有什么问题。并且日志里可以清晰地看到发送的请求头是什么,然后解析出来的响应头是什么。这跟在网络面板看到的是一致的。

与正常相比,最后一次发送请求和读取响应头无异常,时间就多在了前面还有再次发送和请求的过程,细看时间都花在了以下两个事件中:

HTTP_STREAM_PARSER_READ_HEADERS[dt=21301]

HTTP_STREAM_PARSER_READ_HEADERS[dt=21304]

该事件的名称已经自我解读,意思是解析读取的响应头。但问题是紧接着下面报错了,

--net_error=-101(ERR_CONNECTION_RESET)

读取响应头时发生了链接重置的错误,有理由认为本次链接是不成功的,没拿到正确的响应头,于是解析不成功。时间都花在了这里,足足21秒之久,两个21秒造就了上面看到的Stalled了42秒之久。

问题似乎已经很明朗了。链接被重置。

在第三次尝试的时候正常了,于是正确返回,我们才看到了被解析的响应头被展示在了下面。也就是说在出问题的时候要么响应头未拿到,要么响应头非法导致解析不成功。而原因就是链接被重置。

那么接下来的工作就是对ERR_CONNECTION_RESET这个错误的追查了。

官方关于ERR_CONNECTION_RESET错误的解释

未找到官方相应的资料,Chrome官网上唯一关于此错误的描述是在安装Chrome时出现Error101。我估计文档的撰写人员没想到谁会这么蛋疼想要看这些生涩的东西,除了开发者。既然你都是开发者了,那为什么不去看Chromium的源码。

好吧,唯一的途径似乎只能从源码中寻找了。作为只精JS的前端人员,现在要从C,C++代码中找答案了。估计追完这个问题,我会尝试为Chromium贡献代码。

慢着,在这之前,还是搜到一些关于这个错误的信息的。但似乎都不怎么靠谱。

比如这里提到,是因为ISP网络问题,实在无太可能。还有这是神马居然一个硬件网站但提到了这个错误,并且怀疑是杀软导致Chrome出问题,但杀软已经在上文被我们排除了。

Chromium源码

那么这个错误究竟是什么。能不能找到点靠谱的解释。当然能,让我们进入到Chromium的源码中去。

ERR_CONNECTION_RESET被唤起的地方

在Chromium的源码中搜索该常量名,确实出现很多结果。联系到我们查看日志发现问题的上下文,是在解析响应头报的。所以我们定位到http_stream_文件,同时注意到有一个文件叫net_errors_,所以猜测他是定义所有错误常量用的,也顺便打开之。

经过观察src/net/base/net_errors_其路径和代码得知其中多为系统级别的错误,似乎跟我们的问题不是很关联,忽略该文件。

http_stream_文件中,ERR_CONNECTION_RESET仅出现一次。这给我们定位带来了极大的便利。

[chromium]//src/net/base/net_errors_:

//Returnstrueif|error_code|isanerrorforwhichwegivetheservera//chancetosabodycontainingerrorinformation,iftheerrorwasreceived//(interror_code){return(error_code==ERR_CONNECTION_RESET);}

这里定义了一个ShouldTryReadingOnUploadError的方法,注释耐人寻味,这个时候,这样的情景,能否正确解读注释成为了比读懂代码更重要(这是我在看JS代码时永远无法体味到的感觉),下面尽可能对它进行理解:

我承认被上面的复杂从句打败!

那么我们来看这个方法被调用的场景。

459行DoSHeadersComplete方法里进行了调用:

intHttpStreamParser::DoSHeadersComplete(intresult){if(result0){//Intheunlikelycasethattheheadersandbodyweremerged,allthe//theheadersweresent,butnotallofthebodyway,and|result|is//anerrorthatthisshouldtryreadingafter,stashtheerrorfornowand//(request_headers_-BytesConsumed()=request_headers_length_ShouldTryReadingOnUploadError(result)){upload_error_=result;returnOK;}returnresult;}

516行另一个DoSBodyComplete方法里进行了调用:

intHttpStreamParser::DoSBodyComplete(intresult){if(result0){//If|result|isanerrorthatthisshouldtryreadingafter,stashthe//(ShouldTryReadingOnUploadError(result)){upload_error_=result;returnOK;}returnresult;}

这也与我们在日志中看到的情况相符,在前面再次错误后,这次请求并没有终止结束,而是尝试到了第三次并且以成功结束的。

但不管怎样,从这两个方法,一个DoSHeadersComplete,另一个DoSBodyComplete,身上能体现出请求确实已经发出去。

TCPRST

另外,在net_error_这个文件的109行,可以准确找到我们在日志中得到的101号错误。它的定义如下:

//Aconnectionwasreset(correspondingtoaTCPRST).NET_ERROR(CONNECTION_RESET,-101)

从括号中的进一步解释可以知道,它代表TCP连接重置。

TCP

那么问题来了,什么是TCP连接重置?什么会引发TCP连接重置。从这篇文章中有比较详细的解答。

想要完全解释,本文似乎是不可能的了。但根据上面的文章,这里可以简单转述一下。

什么是TCP连接

它是一种协议。当网络上一个节点想与另一个节点通信时,双方需要选建立连接。而这个连接过程需要大家都懂的一种约定,TCP就是事先定好的一种约定,于是我们采用它吧,于是其中一个节点按照这个约定发起一建立连接的请求,另一节点收到后,根据该约定,便能读懂这个请求里各字段的意思:哦,丫这是想约我呢。

三次握手

继续上面的例子。A想与B通信,并且使用TCP。

首先A发起一个报文,其中包含自己的地址,想要连接的目标地址,自己用来连接的端口及目标机器的端口,etc.

B收到邀约,并且愿意付约。此刻B需要回传一个报文,告诉A我愿意跟你连接。

A收到B的肯定应答,到此A与B经历了三次通信或者说是握手,双方都没有异议,连接建立。

而连接断开的过程也颇为类似。双方中的一方比如说A先发起一个断开连接的报文FIN,B收到并确认,然后回传一个可以断开的报文FIN给A。此刻A收到并确认。此刻双方都确认后,连接可以安全断开,但还会保持一个等待断开的状态,大概持续4分钟,用于之前连接通路上未传输完成的数据进行善后。

什么是重置

上面提到了4分钟的等待时间,而重置RESET便是立即断开连接的手段。

发生重置的情况

到此重置的作用已然明了。也就是说,重置甚至算不上一个错误,它是TCP连接中的一种正常情况。但什么时候会发生重置,如何引起的。

上文列出了三种情况。

SMBReset

简单举例来说,服务器提供了两个端口445,139进行服务,客户端同时去请求与这两个端口连接,服务器返回了两个端口可以被连接,此刻客户端择优选择一个进行连接,而重置另一个。

Ack,Reset

报文重置发生主要有以下情况:

服务器没有监听被请求的端口,无法建立连接

服务器此刻无法比如没有充裕的资源用来连接连接

TCPResetduetonoresponse

由于没有响应而被重置。当发起连接的一方连续发送6次请求未得到回应,此刻默认他们之间已经通过三次握手建立了连接并且通信有问题,发起的一方将连接重置。

ApplicationReset

除了上面的情况,找不到TCP内部自己发送的重置,则归为了这一类。程序内将连接重置。此种情况包含了所有你想得到想不到将连接断开的情况。有可能是程序内部逻辑重置的,所以不能完全认为此时发生了错误。

值得注意的是,上面列出的情况服务器的不确定性导致连接重置的可能性要合理些。Chrome主动发起URL请求不太可能自己又重置掉,并且没有理由重置掉后又去重连。

进一步解读日志文件

上面Chromium源码部分的求证多少带有猜测成分。不妥。

因为没找到关于Chromenet-internal日志的官方文档什么的,自身去解读始终是有局限的。不如提个ISSUE让Chromium开发人员来搭一把手吧。遂向Chromium提交ISSUE,请戳此查看,虽然我不认为现在遇到的这个问题跟Chrome有关并且属于Chrome的Bug,目的仅仅是看他们能否帮忙给出合理的日志解读来定位问题。

三天后(有点热泪盈眶),有同学回复,将日志所体现的问题诊断得似乎很有道理,可信。

「之前有过很多成功的连接」,确实,因为出现加载缓慢的情况是偶发的,这之前有过很多正常的不卡的请求存在过。这里没有异议。

「他们都以未知的原因被断掉了」,因为不是正常地断开连接,所以客户端也就是浏览器不知道当前与服务器的TCP连接已经断开,傻傻地保留着与服务器连接的socket,注意,此时已经发生信息的不对等了,这是问题的根源。至于什么原因,给出了可能的原因:路由器认为连接超时将其断掉,同时不排除ISP(互联网服务提供商)的原因,服务器暂时的停运抽风等。不管怎样,客户端浏览器没有收到连接断开的信息。

在上面的基础上,我们去发起一次新的请求。此时浏览器希望重用之前的连接以节省资源,用之前的一个socket去发起连接。21秒后收到服务器返回的重置信息(意思是服务器告诉浏览器:我和你之间没有连接),没关系,上面提到,我们有很多可以重用的连接,于是浏览器重新从可用的连接里面又选择了一个去进行连接,不幸的是,同样的情况再次发生,21秒后收到服务器的重置信息。这体现在日志上就是第二次重试失败。到第三次,因为前面浏览器认为可以重用的连接现在都被正确地标为断开了,没有新的可用,于是这次浏览器发起了全新的请求,成功了!

总结出来,两个问题:

为什么之前成功的连接不正常的断开了?服务器配置或者网络原因?

是什么让浏览器21秒后才收到重置信息?服务器作出反应过慢还是网络原因?

ChromeDevTool中时间线各阶段代表的意义

另附注一下ChromeDevTool中请求的时间线各阶段代表的意义。以下内容扒自Chrome开发者文档页,然后我将它本地化了一下下。

Stalled/Blocking

在请求能够被发出去前的等等时间。包含了用于处理代理的时间。另外,如果有已经建立好的连接,那么这个时间还包括等待已建立连接被复用的时间,这个遵循Chrome对同一源最大6个TCP连接的规则。

「拿我们的情况来说,上面出错所有的耗时也是算在了这部分里面。网络面板中显示的其余时间比如DNS查找,连接建立等都是属于最后那次成功请求的了」

ProxyNegotiation

处理代理的时间。

DNSLookup

查找DNS的时间。页面上每个新的域都需要一次完整的寻路来完成DNS查找。

InitialConnection/Connecting

用于建立链接的时间,包括TCP握手及多次尝试握手,还有处理SSL。

SSL

完成SSL握手的时间。

RequestSent/Sing

发起请求的时间,通常小到可以忽略。

Waiting(TTFB)

等待响应的时间,具体来说是等待返回首个字节的时间。包含了与服务器之间一个来回响应的时间和等待首个字节被返回的时间。

ContentDownload/Downloading

用于下载响应的时间

结论

我相信很多同学是直接跳到这里来了的。事实上我给不出什么解决方案,但能排除前端代码引起问题的可能性。

具体来说,能够得到的结论有以下几点:

请求成功构造,失败情况下也可以看到正常的请求头被打印出来了的

可以肯定的是在与服务器建立连接时被Shutdown了,参考上面关于连接重置的部分会更有意义一些

参考上面「进一步解读日志文件」部分,来自Chromium开发者的回复中对日志文件的解读更加合理些,浏览器与服务器的连接不正常断开是导致问题的根源,以至于影响了后面对连接的重用

21秒的等待仍然是个未知数,不知道有何不可抗拒的外力促使浏览器21秒后才收到服务器的重置信息。此处浏览器与服务器的失联原因有待查证。

希望本文能帮助到您!

点赞+转发,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)

每周重点攻克一个前端技术难点。更多精彩前端内容私信我回复“教程”

作者:wayou

免责声明:本文章如果文章侵权,请联系我们处理,本站仅提供信息存储空间服务如因作品内容、版权和其他问题请于本站联系