| 编辑推荐: |
本文主要介绍如何通过JMeter性能测试工具结合浏览器调试与Wireshark抓包分析,以八大实战实例深入浅出地讲解HTTP/1.1协议的核心原理(如持久连接、缓存机制、请求/响应报文结构、编码格式等)及其在性能测试脚本开发中的实际应用,希望对你的学习有帮助。
本文来自于7dgroup-ai,由火龙果软件Alice编辑、推荐。 |
|
1、引言
概要图 在网络时代的IT人员对HTTP并不陌生,HTTP是使用最多的应用层超文本传输协议。写此篇的主要目的也就是借用一些常用的工具(本人是测试人员,所以使用的工具基本上是测试工具),以理论结合工具实践的方式来更好的解释HTTP协议及在网络中的应用。
2、HTTP
2.1、HTTP发展史
HTTP 是一种应用于分布式、协作、超媒体信息的应用系统的应用层协议。从1990年开始http协议被广泛地应用在万维网中,经历了0.9、1.0(RFC 1945)、1.1(RFC 2616)、及还未大规模推广使用的2.0与3.0。尽管http当前已经发展到了3.0,但由于自身的缺点与限制还有浏览器的兼容等种种原因,致使HTTP1.1从1997年的发版(RFC 2068)到1999年的完善改进(启用RFC 2616,弃用RFC 2068)仍是当前最流行使用规模最大的,所以在本文中的说明及相关实例都是基于HTTP1.1协议进行说明与实践的。想了解更多RFC与HTTP相关的信息,可以去RFC官方网站搜索“http”查看详情。
2.2、HTTP概述
The HTTP protocol is a request/response protocol. A client sends a request to the server in the form of a request method, URI, and protocol version, followed by a MIME-like message containing request modifiers, client information, and possible body content over a connection with a server. The server responds with a status line, including the message’s protocol version and a success or error code, followed by a MIME-like message containing server information, entity metainformation, and possible entity-body content. 【来自RFC2616官网】
HTTP协议是一种请求/响应协议。当客户端与服务器建立连接后,客户端向服务器端发送请求,请求的格式:请求方法,URI,协议版本(即HTTP版本),以及后面的类似MIME的消息修饰符,客户端消息,可能有的正文内容(也可以没有正文内容)。服务器返回客户端请求对应的响应信息,响应信息包括:状态行,消息协议版本、成功或错误码,还有类似MIME的消息,实体元信息和可能有的实体信息内容(实体信息内容可有可无)。
再者就是客户端与源服务器之间的通讯,最简单的形式就是单一连接(a路连接)客户端与服务器(源服务器资源)直接交流,即客户端发起请求直接到服务器端,服务器接到请求返回响应信息直接到客户端;但现实中为了数据安全等种种原因会存在很多客户端发出的请求,会经过很多连接(客户端->1->2->3->4->5->服务器)最终转发到服务器端。返回信息也会根据缓存等机制与技术(代理(缓存的响应信息)->11->客户端)或交换机、防火墙等多个的连接(服务器->7->8->9->10->11->客户端)到达客户端。
2.3、HTTP报文
2.3.1、报文类型
HTTP报文分请求报文与响应报文。请求报文结构见“HTTP请求报文”图解,其主要结构有:请求起始行、请求头域(Request-Header-Fields)、实体(Entity,可有可无)。响应报文结构见“HTTP响应报文”图解
具体的报文信息说明网上已经有很多详细的说明,在此不再做过多的阐述。
2.3.2、HTTP请求
每一个请求报文中"起始行"与"Header"头域是必不可少,但实体不一定会有。上一节中已经对请求结构做了图解可做参考,在此只对其中的几项进行说明。
请求方法(9种):OPTIONS | GET | HEAD | POST | PUT | PATCH | DELETE | TRACE | CONNECT。我们最常见的也就是POST与GET。
- POST: 此方法的响应是不可缓存的。除非响应里有合适的Cache-Control或者Expires。
- GET:请求的响应在满足HTTP缓存的要求时是可缓存的。
请求URL:如"http://www.baidu.com" 与 "http://www.BAIDU.com "是相同的,但 "http://www .baidu.com/ZHIHU I"与" http://www .baidu.com/zhihu i"是不一样的。请求的url是必不可少的,区分大小写(除域名部分外,请求相对源服务器资源的部分是区分大小写的)。URL本身没有大小限制,但不同的浏览器对于URL是有限制的,比如IE浏览器对于URL的限制为2KB,而Chrome,FireFox浏览器理论上对于URL是没有限制的,它真正的限制取决于操作系统本身。
请求头域:Accept | Accept-Charset | Accept-Language | Authorization | Expect | From | Host | If-Match | If-Modified-Since | If-None-Match | If-Range | If-Unmodified-Since | Max-Forwards | Proxy-Authorization | Range | Referer | TE | User-Agent
2.3.3、HTTP响应
正常情况下服务器在接收和解析一个客户端请求消息后,会给客户端一个回应,即为HTTP响应消息。上一节中已经对响应报文结构做了图解可做参考,在此只对其中的几项进行说明。
状态码及原因简介:
1XX : 报告的 - 请求被接收到,继续处理
2XX : 成功 - 被成功地接收(received),理解(understood),接受(accepted)的动作
3XX : 重发 - 为了完成请求必须采取进一步的动作
4XX : 客户端出错 - 请求包括错的语法或不能被满足
5XX : 服务器出错 - 服务器无法完成显然有效的请求
常见的有:
* 200--OK
* 302--发现
* 401--未授权的
* 403--禁用
* 404--没有找到
* 504--网关超时
实体头域:Allow | Content-Encoding | Content-Language | Content-Location | Content-MD5 | Content-Range | Content-Type | Expires | Last-Modified
常用的有:
* Content-Encoding--实体消息编码
* Content-Type--实体消息类型
* Content-Length--实体消息长度
很多代理都支持HTTP协议,本文使用了比较常见的测试工具Jmeter中的HTTP Request的使用场景实例来介绍HTTP1.1协议。
2.4、HTTPS简介
HTTPS(Hyper Text Transfer Protocol over SecureSocket Layer)即HTTP的安全版,在HTTP的基础上加入了TLS/SSL加密层,是目前互联网上最主流的安全传输方案。
2.4.1、HTTPS与HTTP的关系
HTTPS本质上就是HTTP + TLS(Transport Layer Security),两者的核心区别:
| 对比项 | HTTP | HTTPS |
| 协议标识 |
http:// |
https:// |
| 默认端口 |
80 |
443 |
| 传输方式 |
明文传输 |
TLS加密传输 |
| 证书要求 |
不需要 |
需要CA颁发的SSL证书 |
| 安全性 |
数据可被截获和篡改 |
数据加密、防篡改、身份认证 |
2.4.2、TLS握手简述
HTTPS在TCP三次握手建立连接之后,还会进行一次TLS握手来协商加密参数,过程如下:
1. Client Hello:客户端发送支持的TLS版本、加密套件列表和一个随机数。
2. Server Hello:服务端从中选择一个加密套件,返回选定的TLS版本、加密套件和另一个随机数。
3. 证书验证:服务端发送SSL证书,客户端验证证书的有效性(是否过期、是否由可信CA签发、域名是否匹配)。
4. 密钥交换:客户端和服务端利用前面的随机数和证书中的公钥,协商出一个对称加密的会话密钥。
5. 加密通信:TLS握手完成后,后续所有HTTP数据都通过该会话密钥进行对称加密传输。
2.4.3、JMeter测试HTTPS注意事项
在JMeter中测试HTTPS接口时,需要注意以下几点:
协议设置:Protocol填 https,Port Number填 443。
证书信任:如果目标服务器使用的是受信任CA签发的证书(如Let's Encrypt),JMeter默认即可正常访问。如果是自签名证书或内部CA证书,需要将证书导入JMeter的truststore文件,或在jmeter.properties中配置 truststore.file 和 truststore.password。
常见报错:
javax.net.ssl.SSLHandshakeException:证书不被信任,需导入证书。
javax.net.ssl.SSLProtocolException:TLS版本不兼容,可在JMeter中指定协议版本。
性能测试建议:TLS握手会消耗额外时间,首次连接比普通HTTP慢。在高并发场景下,建议开启连接复用(KeepAlive)以减少握手次数。
很多代理都支持HTTP协议,本文使用了比较常见的测试工具Jmeter中的HTTP Request的使用场景实例来介绍HTTP1.1协议。
3、工具说明
3.1、JMeter中HTTP Request说明
我们先来对Jmeter的HTTP Request在后面的实例中使用到的设置项进行简单的说明。
Basic(基本设置项):
Web Server:Protocol[http]:协议类型【http/https】,默认值是http;Server Name or IP:服务器域名或IP地址,即host;Port Number:端口号,HTTP默认是80。
HTTP Request:Method:请求方法GET, POST, HEAD, OPTIONS, PUT, DELETE and TRACE;Path:服务资源的相对路径,默认是"/";Content encoding:请求编码格式,通常设置为"UTF-8";消息体的几种传递形式:Parameters(以表单形式传递)、Body Data(Json串形式)、Files Upload(文件上传)。
Advanced(高级):Client implementation(HttpClient4/Java)、Timeouts(Connect/Response超时毫秒)、Embedded Resources form HTML Files(Retrieve All Embedded Resources、Parallel downloads、URLs must match)、Source address、Proxy Server、Optional Tasks。
3.2、浏览器自带的调试工具(快捷键F12)
浏览器自带的调试工具一直以来都受开发人员或测试人或运维人员的青睐。第一简单方便,无需安装,并且所有的浏览器基本都通用。在此不做过多说明,只简洁说一下chrome自带的调试工具,下文中使用到的一些功能项。
Network面板底部项说明:
Finish:页面上所有http请求发送到响应完成的时间。HTTP1.1协议限定,单个域名的请求并发量是6个。
DOMContentLoaded:DOM树构建完成,即HTML页面由上向下解析HTML结构到末尾封闭标签。
Load:页面加载完毕,DOM树构建完成后继续加载外部资源(图片等)。
DOMContentLoaded一般比Load时间小,两者差大致等于外部加载的时间。Finish时间与DOMContentLoaded和Load并无直接关系。
4、实例说明
针对Jmeter的HTTP Request中的各项设置及执行结果进行http对应的理论说明。
4.1、脚本实例一(连接不复用)
http1.1的特性持久化连接,所以我们来模拟一下这个场景。一个get请求,10个并发用户,持续120秒。
请求中把用户持久连接即"UseKeepAlive"给去掉,就是连接请求完毕立即断开。
执行后请求信息中看到"Connection: close"。
摘要报告显示平均用时28784毫秒,Throughput为20.0/min。
服务器端8015端口不断有TIME_WAIT断开,并建立新的TCP连接,直到脚本执行完毕。
4.2、脚本实例二(连接复用)
此并发场景与上一个场景一样,只是设置了"Use KeepAlive"即连接复用。
执行脚本实际请求可以看到"Connection: keep-alive"。
执行结果报告摘要显示平均时间27570毫秒,Throughput为20.9/min。
再看服务器端请求端口详情,可以看到在执行过程中连接被复用都处于"ESTABLISHED"没有断开,直到所有线程执行完毕。
由此与上一个实例对比,可以看出当使用复用时,因每个线程的请求不需要断开重新再与服务器连接,所以节省了一个再重连的时间,因而总体时间会比没有使用复用连接时要短。
4.3、脚本实例三(缓存)
对于HTTP 1.1来说另一个特性就是缓存,说到缓存常用的方法也就GET了。这里直接用浏览器自带的开发者调试工具(F12),打开调试,在浏览器中输入"www.baidu.com"回车进行查询。
第一次查询查看Network结果:
可以看到一共有 47个请求,完成这些请求共用564ms,DOMContentLoaded用时353ms,加载447ms。请求最大用时79ms,最小用时6ms。
刷新页面第二次请求"www.baidu.com":
仍有47个请求,但总用时453ms,DOMContentLoaded用时264ms,加载366ms。请求最大用时39ms,最小用时0ms。Size列时间为0的显示都为(memory cache),说明数据从缓存中获取,并未走网络。
这也就符合了正常的逻辑,我们平时在浏览一些网站时第一次比较慢,而后再次打开就比较快了,就是因为第一次浏览时图片和js等资源都得从服务器获取并加载,再次浏览时部分资源已缓存到本地。
4.4、脚本实例四(协议的必须组成部分)
我们这里拿"www.baidu.com"做一个简单的实例说明。
1) 首先在终端用 ping 命令
找到其对应的外网IP地址:
2) 打开wireshark并设置过滤条件:
3) JMeter工具发送HTTP请求包
只填写了Server Name or IP:www.baidu.com,其他用默认值。
4) 查看执行结果。
从JMeter可以看到请求信息
再用wireshark查看请求包详情:
这里的“\r\n”也就是前面“\回车(CR)\换行(LF)”,HTTP1.1协议请求都是用完整的请求,即“Full request URI:http://www.baidu.com/”
如果请求主体里有信息时,则实体头域里必须有“Content-Length”实体消息长度,“Content-Type”实体消息编码,还会有"Line-base text data"实体消息内容。
再看一下对应的响应信息
也能简单地看到一些信息,可能还不是很清晰,那我们还是来看一下wireshark的详情信息。我们可以选中要查看的HTTP请求,“在选中的请求右键菜单Follow->HTTP Stream ”查看完整的http数据流信息
从上面我们可看到响应信息中有乱码,这是因为HTTP1.1协议里的默认的编码是"ISO-8859-1"(在上面的请求信息图中可以查看到),而结果实体消息里需要的是UTF-8.
现在知道原因了,可以手动修改一下请求客户端代理即Jmeter的配置文件“jmeter.properties”里的“sampleresult.default.encoding”
修改为:
保存,重启jmeter执行结果中文显示应该不会有乱码了。
总之,http协议的组成必须部分:请求起始行主要是URI,请求头域,如果有实体消息体的话需要有消息体的头域消息体长度与编码 。
4.5、脚本实例五(http常用方法的post、get说明)
其实POST与GET最大的区别:
1)POST响应不可以使用缓存,GET为了降低网络压力可以使用缓存。
2)POST请求主要信息放在消息体里,所以在发送请求时,消息头域里会有Content-Length,即使消息体长度为0;而GET请求参数放在URL里,没有消息体。
4.6、脚本实例六(结合报文对请求头报文体说明,内容形式及编码格式)
请求中常用的编码格式与消息体参数形式
编码格式:
URL
UTF-8
报文体参数形式:
parameters
Body Data
Files Upload
请求报文可以是UTF-8,也可以是URL,一般来说post报文的参数体以Body Data形式或Files Upload形式,而get方法使用的Parameters多一些。具体来看看每一种形式实际是如何。
因我们在上一节中修改过Jmeter的配置文件,已经把默认编码改为"UTF-8",所以当不设置时默认的编码是“UTF-8”
看一下执行结果,能直接看到请求信息“角色”
而如果在请求设置里选择了“URL Encode”
再看一下执行结果,看到的请求信息“%E8%A7%92%E8%89%B2”,用URL在线转码工具,可以转换成“角色”
4.7、脚本实例七(以jmeter高级使用-Implementation)
Jmeter实现客户端编译默认的是"Apache-HttpClient",但在实际中有些请求需要特殊处理。当Implementation没有设置时,User-Agent为Apache-HttpClient/4.5.12,服务器可能解析失败。
上面的请求就是上传图片到服务器。
当请求的高级设置里的“Implementation”没有设置时,请求结果:
头信息里User-Agent: Apache-HttpClient/4.5.12 (Java/1.8.0_231)
此时服务器处理返回的结果,报错,请求信息的元消息体服务器解析不了:
如果请求的高级设置里的“Implementation”设置为"Java"时请求结果:
由此可以看出默认的实现方式Apache-HttpClient/4.5.12不能正确地传递信息。 另外当请求消息里出现“Content-Transfer-Encoding: binary”时,媒体类型必须通过Content-Type 头域指明,如实例的“Content-Type: multipart/form-data; boundary=---------------------------7d159c1302d0y0”
这样在传递消息到服务器时,服务器才能认为传递的是有效的消息主体,否则会丢弃此请求并返回自定义的code码为500,并给出提示信息“请求出错了”。 4.8、脚本实例八(以jmeter高级使用-Embedded Resources from HTML Files)
http协议是应用层的通信协议,作用是传输数据。HTML是一种标记语言,用于展示数据。可以这么理解为http是传输工具,工具承载的主要消息内容体是HTML。所以对HTML也简单的了解一下。
这里主要是对html文件中的嵌入资源进行过滤。HTML会嵌入很多资源,如请求一个“www.baidu.com”,返回信息:
<!DOCTYPE html> <!--STATUS OK--> <html> <head> <meta http-equiv=content-type content=text/html;charset=utf-8> <meta http-equiv=X-UA-Compatible content=IE=Edge> <meta content=always name=referrer> <link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css> <title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg><img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129></div> <form id=form name=f action=//www.baidu.com/s class=fm><input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span></form> </div> </div> <div id=u1><a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript><a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a></noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=' + encodeURIComponent(window.location.href +
(window.location.search === "" ? "?" : "&") + "bdorz_come=1") + '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a></div> </div> </div> <div id=ftCon> <div id=ftConw><p id=lh><a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a></p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif></p></div> </div> </div> </body> </html>
|

可以看出里面有很多嵌入资源如“http://www.baidu.com/bdorz/login.gif?”会有css,gif等。我们可以根据自己的需求来过滤出想要的资源信息。如我们不想要“www.baidu.com”中的"png",那么就可以这么设置:
Retrieve All Embedded Resources选中,
URLs must match : http://www.baidu.com/.*
URLs must match : .*.(png|svg)
执行后的结果
下面是没有打开“Retrieve All Embedded Resources”执行结果
5、注意事项
1、HTTP协议不对URI的长度作事先的限制,服务器必须能够处理任何他们提供资源的URI,并且应该能够处理无限长度的URIs。如果服务器不能处理太长的URI的时候,服务器应该返回414状态码(Request-URI太长)。
2、当比较两个URI是否匹配时,客户端应该对整个URI区分大小写,但主机(host)名和模式(scheme)名的比较必须不区分大小写。例如以下三个URI是等同的:
http://abc.com:80/~smith/home.html
http://ABC.com/~smith/home.html
http://ABC.com:/%7esmith/home.html
3、对于响应消息,消息里是否包含消息主体依赖相应的请求方法和响应状态码。所有HEAD请求方法的响应消息不能包含消息主体。所有1XX(信息的)、204(无内容的)和304(没有修改)的响应都不能包括消息主体。所有其他的响应必须包括消息主体,即使它长度可能为零。
4、状态码总结:
- 1xx:报告的 - 请求被接收到,继续处理
- 2xx:成功 - 被成功地接收、理解、接受
- 3xx:重发 - 为了完成请求必须采取进一步的动作
- 4xx:客户端出错 - 请求包括错的语法或不能被满足
- 5xx:服务器出错 - 服务器无法完成显然有效的请求
4、状态码
-1xx :报告的 -请求被接收到,继续处理 -2xx :成功 - 被成功地接收(received),理解(understood),接受 (accepted)的动作 。 -3xx :重发 - 为了完成请求必须采取进一步的动作。 -4xx :客户端出错 - 请求包括错的语法或不能被满足。 -5xx :服务器出错 - 服务器无法完成显然有效的请求。
5、任一包含了实体主体的 HTTP/1.1 消息都应包括 Content-Type 头域以定义实体主体的媒体类型。 如果只有媒体类型没有被 Content-Type 头域指定时,接收者可能会尝试猜测媒体类型,这通 过观察实体主体的内容并且/或者通过观察 URI 指定资源的扩展名。如果媒体类型仍然不知道, 接收者应该把类型看作”application/octet-stream”。
6、支持持久连接(persistent connection)的客户端可以以管线的方式发送请求(即无须等待响 应而发送多个请求)。服务器必须按接收请求的顺序发送响应。
tcp建议一次连接,最多发送6~8个http请求,但都是等待排队,每次只能处理一个请求,当上一个请求有响应结果后下一个请求才会有响应结果,处理是按请求的先后顺序执行的。
所以,当出现短连接时,使用jmeter出现“jmeter org.apache.http.NoHttpResponseException: 172.16.20.25:18030 failed to respond”时,此时解决方法就是需要把jmeter中的“Use keepAlive”取消掉,即可。
7、使用持久连接的客户端应限制与某一服务器同时连接的个数。单用户客户端不应与任一服务器 或代理保持两个以上的连接。代理应该与其它服务器或代理之间维护 2*N 个连接,其中 N 是同 时在线的用户数。这些准则是为了改善响应时间和避免阻塞。
8、method(方法)
OPTIONS:此方法的响应是不能缓存的。
POST 方法的响应是不可缓存的。除非响应里有合适的 Cache-Control 或者 Expires 头域。然而, 303(见其他)响应能被用户代理利用去获得可缓存的响应。
6、总结
本文通过JMeter HTTP Request结合浏览器调试工具和Wireshark抓包分析,从实践角度深入介绍了HTTP 1.1协议的核心原理,主要涵盖以下内容:
- HTTP基础原理 :介绍了HTTP的发展历程、请求/响应模型、报文结构(请求报文与响应报文)及状态码体系。
- HTTPS简介 :介绍了HTTPS与HTTP的关系、TLS握手流程,以及JMeter测试HTTPS时的证书配置和常见问题。
- 工具使用 :详细说明了JMeter HTTP Request的各项参数配置,以及浏览器F12调试工具和Wireshark抓包分析的使用方法。
- 连接复用与缓存 :通过实例对比了KeepAlive开启/关闭对TCP连接和性能的影响,验证了HTTP缓存机制的实际效果。
- 编码与实现方式 :分析了URL Encode与UTF-8编码的区别,以及不同Implementation(HttpClient vs Java)对请求解析的影响。
- 嵌入资源过滤 :介绍了如何通过JMeter过滤HTML中的嵌入资源,实现对特定资源的精确测试。
在实际性能测试中,合理配置JMeter的HTTP Request参数(如KeepAlive、Implementation、编码格式等),结合对HTTP协议原理的理解,可以更准确地模拟真实场景,定位性能瓶颈。
|