关于http请求串行问题的研究分析

一.问题描述

周五 同事在学习tomcat的时候写了一个简单的ServletDemo1继承了HttpServlet,在doGet方法中处理逻辑,同时在方法中设置线程sleep 8s的时间。

主体代码:


但是发现了一个奇怪的现象,在浏览器的多个窗口打开同一个路由到此Servlet的url,如http://localhost:8080/name,却发现这几个窗口的页面是串行打开的,请求耗时都是在前面请求的基础上+8s(因为并不是同一个时刻打开的,所以请求时间略小于8s)

请求时间如图:

1.


2.


3.


 

二. 问题结论

chrome对于相同的url采用串行请求的方式,

相同的url如果在多个窗口打开chrome必须等前面的请求处理完成后才发起其他相同url的请求。

浏览器处理逻辑

Certain browsers will serialize requests to the same URL if accessed from different windows. For example if you have a CGI script that does:

for (1..100) {
print “$$: $_\n”;
warn “$$: $_\n”;
sleep 1;
}
And two concurrent requests are issued from different windows of the same browser (for those browsers that have this bug/feature), the browser will actually issue only one request and won’t run the second request till the first one is finished. The debug printing to the error_log file helps to understand the serialization issue.

Solution? Find a UA that doesn’t have this feature, especially if a command line UA will do (LWP comes to mind). As of this writing, opera 6, mozilla 1.0 on linux have this problem, whereas konqueror 3 and lynx don’t.

重要的是这句话:

And two concurrent requests are issued from different windows of the same browser, the browser will actually issue only one request and won’t run the second request till the first one is finished.

翻译过来就是:从同一个浏览器的不同窗口发出的两个并发请求,浏览器实际上只发出一个请求,并且在第一个请求完成之后才会运行第二个请求。

 

三. 排查过程

思考一:服务器端有共享变量,某一个线程进去后加锁了,执行完成后才释放锁,其他线程才能进入。

分析:doGet方法存在全局共享变量ServletConfig和ServletContext,而ServletContext的setAttribute方法是synchronized方法,在进入这个方法的时候会对线程加锁,所以是这里的原因造成的吗?

  


答案是不是这样的,setAttribute方法加锁并没有涉及到sleep的过程,setAttribute方法很快就执行完了,其他线程可以进入。

验证:

直接通过httpclient调用,在10个线程中发起10个http请求


结果如下:


可以看到同时发出的10个请求并没有串行执行,而是同时执行返回的。因此,验证了并不是共享变量导致的串行执行问题。

所以串行执行的问题不是这个原因造成的。

 

思考二:浏览器的keep-alive机制,tcp没有关闭连接,浏览器复用了一个连接,导致服务器是依次取数据的。

分析:看着很合理的想法,其实很有问题,从两方面来分析。

先看一下keep-alive的作用:Keep-Alive是通知服务器,在这个HTTP Request/Responset结束后,不要立即断开TCP连接(注意是TCP连接,和HTTP没有关系),后面的HTTP Request仍然可以通过这个TCP连接继续传送。

从客户端来说,Keep-Alive是在整个http请求过程结束之后,不用立即断开tcp连接,后面的http请求仍然可以通过这个tcp连接继续传输,减少的是tcp握手的时间,而不是同时发送请求的时候复用一个tcp连接,也就是tcp连接在同一个时刻只为一个http请求,只有请求完成后才会支持其他http请求。

从服务端分析,即使多个请求同时复用一个tcp连接,服务器也不会依次拉取数据的。tcp接收到传输过来的数据后,会放在tcp缓冲区,给应用层read,应用层取了数据后怎么操作对tcp是透明的,所以即使是同一个tcp连接也不影响请求的串行执行。而多个tcp连接更不可能串行执行。

验证:

同时打开的三个相同url页面的端口号分别是51149,51183,55106,tcp连接的四元组是[源ip,源端口号,目的ip,目的端口号],所以看出这三个请求并不是复用一个tcp连接,而主页面完成后,资源页面的请求是复用的相同的端口号,证明keep-alive的作用是后面的请求确实是复用之前的tcp连接发送请求的。

 

因此,keep-alive机制不是造成请求串行执行的原因。

 

思考三:浏览器是串行发送请求的。

既不是共享变量加锁的原因,也不是keep-alive机制的原因,那么还有什么可能性呢?

另外有一个现象,同一个浏览器不同的窗口同时打开不同的链接,请求都是在8s左右返回,没有串行的现象。

不是服务器端的原因,那么就是客户端的原因。上面的分析我们知道了每个请求都使用的是新的tcp连接,那么就没有竞争资源的问题,这样就只有一种情况,浏览器针对相同的url是串行发出请求的!

验证:


上图是wireshark网络抓包得到的数据,我们是几乎同时打开页面(在浏览器的多个窗口打开相同的url),可以明显的看到,浏览器是在上一个请求完成之后才发出真正的请求。

某一个请求tcp头:


网上查询资料验证了这个想法

And two concurrent requests are issued from different windows of the same browser (for those browsers that have this bug/feature), the browser will actually issue only one request and won’t run the second request till the first one is finished。

最终,问题得到了解决,浏览器的多个窗口打开url串行接收请求的原因是因为浏览器针对相同的url是串行发出请求的。

 

四. 解决问题经验

1.多思考

2.多动手

3.善用工具

欢迎大家有任何网络相关的问题和想法与我交流~~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值