http阻塞(http请求被挂起,长时间处于pending 状态)
不知道大家有没有遇到这种情况,就是当你再进行开发的时候,有时候突然会出现http请求一直卡在pending这个状态,甚至有时候卡了一两分钟,然后这个接口竟然没有报错,请求的数据被成功的返回了。当时我遇到这种情况的时候感觉很神奇,一个接口竟然可以等几分钟还不报错,但是这个东西因为是偶发的,所以后面也就没有去深入研究,直到前几天突然想到了这个问题,今天就想去深入了解一下。
分析
要想搞明白为什么会出现这种情况,你必须对http协议有所了解,其中http1.1是支持长连接的,什么是长连接,就是一个tcp连接可以处理多个http请求。但是有一个缺点就是,必须等上一个http请求返回结果之后才能继续处理下一个请求,这有可能会造成时间上的浪费。而问题正是出现在长连接的请求上。因为我们发现出现问题的时候总是有多个http请求复用同一个tcp连接的情况出现,因此可以断定是长连接引起的问题。
猜想
根据上文分析,我这边有两个猜想:
1.由于http1.1的长连接的缺点,可能是因为等待上一个请求返回结果的时间过长,导致请求一直处于pending状态,但是经过测试可以证明这个想法是错误的。因为我发现复用同一个tcp连接的http请求,当上一个请求已经返回结果之后,下一个请求依然长时间处于pending状态,所以说这个猜想是不成立的。
2.既然不是因为等待时间的问题,我们就大胆的猜测这个长连接(TCP连接)因为某种原因异常的断开了,所谓异常的断开就是单方面的断开,也就是服务器知道断开了,浏览器却不知道该连接已经断开了。因此,浏览器仍然会通过该tcp连接去发送请求,但是此时服务器已经发现该连接断开了,所以会拒绝该次请求,从而引起一系列的问题。但是它会引起什么问题呢,就是http请求会被长时间挂起,这有一篇博客在这方面写的非常好,大家可以去看一下。关于请求被挂起页面加载缓慢问题的追查_知识库_博客园
解决办法
1.调整系统内核参数
我们已经知道出现该问题是因为tcp连接是异常断开的,只要解决连接异常断开的问题就好了。当然这涉及到tcp的异常连接问题,通过查阅各种资料发现,原因如下:服务器关闭连接的时候,调用的是Socket.close()方法,该方法会返回一个RST(RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。),然后服务器就单方面的断开连接了,因为RST这个标志位浏览器不能识别,浏览器只能识别正常断开连接的FIN标志位,只有返回这个标志位,浏览器才知道这个连接被断开了(这个地方看不懂的大家可以去仔细的看一下TCP相关的知识)。所以说,解决这个问题办法就是不让它调用Socket.close方法,也就不会返回RST标志位,解决这个问题涉及到系统内核的问题了,查阅相关资料,给出解决方案:
2.短连接
这是最简单的解决办法,因为这种情况只出现在长连接的情况下,所以我们采用最简单最粗暴的方式来解决它,就是服务器和客户端同时采用短连接,这样就算再怎么异常断开也没有关系,因为我们采用的短连接根本不会出现复用tcp连接的情况,想想就很爽。但是呢,这种解决办法也有缺点,每次请求都要通过三次握手建立tcp连接,就会浪费大量的三次握手的时间,这对于一般的应用来说,这点时间算不了什么。但是对于那些亿级以上的应用,节省的时间是很可观的。
至于要采用哪种方式去解决这个问题呢,就要根据具体情况来定了。