关于http请求串行问题的研究分析_串行请求-程序员宅基地

技术标签: 网络  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.善用工具

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/ningdunquan/article/details/79910745

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签