从性能角度帮你理解HTTP协议
因为做性能测试分析的人来说,HTTP 协议可能是绕不过去的一个槛。在讲 HTTP 之前,我们得先知道一些基本的信息。
HTTP(HyperText Transfer Protocol,超文本传输协议),显然是规定了传输的规则,但是它并没有规定内容的规则。
HTML(HyperText Marked Language,超文本标记语言),规定的是内容的规则。浏览器之所以能认识传输过来的数据,都是因为浏览器具有相同的解析规则。
我们首先关注一下 HTTP 交互的大体内容。想了很久,画了这么一张图,我觉得它展示了我对 HTTP 协议在交互过程上的理解。
在这张图中,可以看到这些信息:
1、在交互过程中,数据经过了 Frame、Ethernet、IP、TCP、HTTP 这些层面。不管是发送和接收端,都必须经过这些层。这就意味着,任何每一层出现问题,都会影响 HTTP 传输。
2、在每次传输中,每一层都会加上自己的头信息。这一点要说重要也重要,说不重要也不重要。重要是因为如果这些头出了问题,非常难定位(在我之前的一个项目中,就曾经出现过 TCP 包头的一个 option 因为 BUG 产生了变化,查了两个星期,一层层抓包,最后才找到原因)。不重要是因为它们基本上不会出什么问题。
3、HTTP 是请求 - 应答的模式。就是说,有请求,就要有应答。没有应答就是有问题。
4、客户端接收到所有的内容之后,还要展示。而这个展示的动作,也就是前端的动作。在当前主流的性能测试工具中,都是不模拟前端时间的,比如说 JMeter。我们在运行结束后只能看到结果,但是不会有响应的信息。你也可以选择保存响应信息,但这会导致压力机工作负载高,压力基本上也上不去。也正是因为不存这些内容,才让一台机器模拟成千上百的客户端有了可能。
如果你希望能理解这些层的头都是什么,可以直接抓包来看,比如如下示图:
从这个图中,我们就能看到各层的内容都是什么。
在我看来,只有实践的操作和理论的结合,才能真正的融会贯通。只讲压力工具而不讲原理,是不可能学会处理复杂问题的;空有理论没有动手能力是不可能解决实际问题的。由于压力工具并不处理客户端页面解析、渲染等动作,所以,以下内容都是从协议层出发的,不包括前端页面层的部分。
JMeter 脚本
在这里只解释几个重要信息:
第一个就是 Protocol。
这个当然重要。从“HTTP”这几个字符中,我们就能知道这个协议有什么特点。 HTTP 的特点是建立在 TCP 之上、无连接(TCP 就是它的连接)、无状态(后来加了 Cookies、Session 技术,用 KeepAlive 来维持,也算是有状态吧)、请求 - 响应模式等。
第二个是 Method 的选项 GET。
HTTP 中有多少个 Method 呢?我在这里做个说明。在 RFC 中的 HTTP 相关的定义中(比如 RFC2616、2068),定义了 HTTP 的方法,如下:GET、POST、PUT、PATCH、DELETE、COPY、HEAD、OPTIONS、LINK、UNLINK、PURGE。
GET 方法是怎么工作的呢?
GET 可以得到由 URI 请求(定义)的各种信息。同样的,其他方法也有清楚的规定。我们要注意的是,HTTP 只规定了你要如何交互。它是交互的协议,就是两个人对话,如何能传递过去?小时候一个人手上拿个纸杯子,中间有根线,相互说话能听到,这就是协议。
第三个是 Path,也就是请求的路径。
这个路径是在哪里规定的呢?在我这个 Spring Boot 的示例中。
@RequestMapping(value = "pabcd")
public class PABCDController {
@Autowired
private PABCDService pabcdService;
@Autowired
private PABCDRedisService pabcdRedisService;
@Autowired
private PABCDRedisMqService pabcdRedisMqService;
@GetMapping("/redis_mq/query/{id}")
public ResultVO<User> getRedisMqById(@PathVariable("id") String id) {
User user = pabcdRedisMqService.getById(id);
return ResultVO.<User>builder().success(user).build();
}
看到了吧。因为我们定义了 request 的路径,所以,我们必须在 Path 中写上/pabcd/redis_mq/query这样的路径。
第四个是 Redirect,重定向。HTTP 3XX 的代码都和重定向有关,从示意上来看,如下所示。
用户发了个 URL A 到服务 A 上,服务 A 返回了 HTTP 代码 302 和 URL B。 这时用户看到了接着访问 URL B,得到了服务 B 的响应。对于 JMeter 来说,它可以处理这种重定向。
第五个是 Content-Encoding,内容编码。
它是在 HTTP 的标准中对服务端和客户端之间处理内容做的一种约定。当大家都用相同的编码时,相互都认识,或者有一端可以根据对端的编码进行适配解释,否则就会出现乱码的情况。
默认是 UTF8。但是我们经常会碰到这种情况。当我们发送中文字符的时候。比如下面的名字。
当我们发送出去时,会看到它变成了这种编码。如下图所示:
如果服务端不去处理,显然交互就错了。如下图所示:
这时,只能把配置改为如下:
我们这里用 GBK 来处理中文。就会得到正确的结果。
你就会发现现在用了正常的中文字符。在这个例子,有人选择用 URL 编码来去处理,会发现处理不了。这是需要注意的地方。
第六个是超时设置。在 HTTP 协议中,规定了几种超时时间,分别是连接超时、网关超时、响应超时等。
如下所示,JMeter 中可以设置连接和响应超时:
在工具中,我们可以定义连接和响应的超时时间。但通常情况下,我们不用做这样的规定,只要跟着服务端的超时走就行了。但在有些场景中,不止是应用服务器有超时时间,网络也会有延迟,这些会影响我们的响应时间。如果 HTTP 默认的 120s 超时时间不够,我们可以将这里放大。
在这里为了演示,我将它设置为 100ms。我们来看一下执行的结果是什么样。
从栈的信息上就可以看到,在读数据的时候,超时了。
超时的设置是为了保证数据可以正常地发送到客户端。做性能分析的时候,经常有人听到“超时”这个词就觉得是系统慢导致的,其实有时也是因为配置。
通常,我们会对系统的超时做梳理,每个服务应该是什么样的超时设置,我们要有全局的考量。比如说:
超时应该是逐渐放大的(不管你后面用的是什么协议,超时都应该是这个样子)。而我们现在的系统,经常是所有的服务超时都设置得一样大,或者都是跟着协议的默认超时来。
在压力小的时候,还没有什么问题,但是在压力大的时候,就会发现系统因为超时设置不合理而导致业务错误。
如果倒过来的话,你可以想像,用户都返回超时报错了,后端还在处理着呢,那就更有问题了。
而我们性能测试人员,都是在压力工具中看到的超时错误。如果后端的系统链路比较长,就需要一层层地往后端去查找,看具体是哪个服务有问题。所以在架构层级来分析超时是非常有必要的。
第七个,在上图中,还有一个参数是客户端实现(Client Implementation)。其中有三个选项:空值、HTTPClient4、Java。
官方给出如下的解释。
JAVA: 使用 JVM 提供的 HTTP 实现,相比 HttpClient 实现来说,这个实现有一些限制,这个限制我会在后面提到。
HTTPClient4:使用 Apache 的 HTTP 组件 HttpClient 4.x 实现。
空值:如果为空,则依赖 HTTP Request 默认方法,或在jmeter.properties文件中的jmeter.httpsample定义的值。
用 JAVA 实现可能会有如下限制。
- 在连接复用上没有任何控制。就是当一个连接已经释放之后,同一个线程有可能复用这个已经释放掉的连接。
- API 最适用于单线程,但是很多设置都是依赖系统属性值的,所以都应用到所有连接上了。
- 不支持 Kerberos Authentication(这是一种计算机网络授权协议,用在非安全网络中,对个人通信以安全的手段进行身份认证)。
- 不支持通过 keystore 配置的客户端证书。
- 更容易控制重试机制。
- 不支持 Virtual hosts。
- 只支持这些方法: GET、POST、HEAD、OPTIONS、PUT、DELETE 和 TRACE。
- 使用 DNS Cache Manager 更容易控制 DNS 缓存。
第八个就是 HTTP 层的压缩。
我们经常会听到在性能测试过程中,因为没有压缩,导致网络带宽不够的事情。当我们截获一个 HTTP 请求时,你会看到如下内容。
这就是有压缩的情况。在我们常用的 Nginx 中,会用如下常见配置来支持压缩:
gzip on; #打开gzip
gzip_min_length 2k; #低于2kb的资源不用压缩
gzip_comp_level 4; #压缩级别【1-9】值越大,压缩率就越高,但是CPU消耗也越多,根据我们在网上看到建议,大部分都是建议设置为中间4、5之类的,这里我建议大家根据自己的项目实际情况,在压力测试之后给出适合的值。
gzip_types text/plain application/javascript; #设置压缩类型
gzip_disable "MSIE [1-6]\."; # 禁用gzip的条件,支持正则
在 RFC2616 中,Content Codings 部分定义了压缩的格式 gzip 和 Deflate,不过我们现在看到的大部分都是 gzip。不过在压缩这件事情上,我们在压力工具中并不需要做什么太多的动作,最多也就是加个头。
第九个就是并发。
在 RFC2616 中的 8.1.1 节明确说明了为什么要限制浏览器的并发。大概翻译如下,有兴趣的去读下原文:
- 少开 TCP 链接,可以节省路由和主机(客户端、服务端、代理、网关、通道、缓存)的 CPU 资源和内存资源。
- HTTP 请求和响应可以通过 Pipelining 在一个连接上发送。Pipelining 允许客户端发出多个请求而不用等待每个返回,一个 TCP 连接更为高效。
- 通过减少打开的 TCP 来减少网络拥堵,也让 TCP 有充足的时间解决拥堵。
- 后续请求不用在 TCP 三次握手上再花时间,延迟降低。
- 因为报告错误时,没有关闭 TCP 连接的惩罚,而使 HTTP 可以升级得更为优雅(原文使用 gracefully)。
- 如果不限制的话,一个客户端发出很多个链接到服务器,服务器的资源可以同时服务的客户端就会减少。
HTTPS 只是加了一个 S,就在访问中加了一层。
因为证书是个非常标准的产品,加在中间,就是加密算法和位数也会对性能产生影响。如果执行场景时报:javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake,就应该把证书也加载进来。
其实对我们做性能测试的人来说,无需关心 HTTP 的内容,我们只要关心数据的流向和处理的逻辑就可以了。
从性能测试的角度来看,如果你要模拟页面请求,最多也就是正常实现 HTTP 的方法 GET、POST 之类的。
它发送和接收的内容,只要符合业务系统的正常流程就可以,这样业务才能正常运行。
比如说,前面提到的 POST 请求。如果我们发送了一段 JSON。内容如下:
{
"userNumber": "${Counter}",
"userName": "Zee_${Counter}",
"orgId": null,
"email": "test${Counter}@dunshan.com",
"mobile": "18611865555"
}
代码中的 Service 负责接收 User 对象,同时转换它的是如下代码:
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", userNumber='" + userNumber + '\'' +
", userName='" + userName + '\'' +
", orgId='" + orgId + '\'' +
", email='" + email + '\'' +
", mobile='" + mobile + '\'' +
", createTime=" + createTime +
'}';
}
然后通过 Service 的 add 方法 insert 到数据库中,这里后面使用的 MyBatis:
Boolean result = paRedisService.add(user);
而这些,都属于业务逻辑处理的部分,我们分析时把这个链路都想清楚才可以一层层剥离。
总结
对于 HTTP 协议来说,我们在性能分析中,主要关心的部分就是传输字节的大小、超时的设置以及压缩等内容。
在编写脚本的时候,要注意 HTTP 头部,至于 Body 的内容,只要能让业务跑起来即可。
从性能角度帮你理解HTTP协议的更多相关文章
- 转载和积累系列 - 深入理解HTTP协议
深入理解HTTP协议 1. 基础概念篇 1.1 介绍 HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(World Wide Web C ...
- 深入理解AMQP协议
深入理解AMQP协议 2018年10月22日 12:32:16 一剑何风情 阅读数:1941 文章目录 一.AMQP 是什么 二.AMQP模型 工作过程 深入理解 三.Exchange交换机 默认 ...
- (转存 作者未知)深入理解HTML协议
深入理解HTML协议 http协议学 习系列 1. 基础概念篇 1.1 介绍 HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(Worl ...
- 深入理解 web 协议(一)- http 包体传输
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/WlT8070LlrnSODFRDwZsUQ作者:吴越 开坑这个系列的原因,主要是在大前端学习的 ...
- 转战物联网·基础篇05-通俗理解MQTT协议的实现原理和异步方式
网络上搜索MQTT协议,会出现太多的解释,这里就不做官方标准释义的复制了.这一节我们从实战理解角度,通俗的将MQTT协议的作用及实现原理说一下,旨在可以快速理解MQTT协议.所以可能会出现很多看似 ...
- (原创) 巩固理解I2C协议(MCU,经验)
题外话:这几天天气突然转冷了.今天已是11月23日了,查查黄历,昨天(11月22日)刚好是小雪,一夜温度骤降,果然老祖先的经验有灵验!冬天来了,还是多加加衣服,注意保暖! 1.Abstract ...
- 网络编程懒人入门(六):深入浅出,全面理解HTTP协议
本文引用了自简书作者“涤生_Woo”的文章,内容有删减,感谢原作者的分享. 1.前言 HTTP(全称超文本传输协议,英文全称HyperText Transfer Protocol)是互联网上应用最为广 ...
- 【学习】014 深入理解Http协议
Http协议入门 什么是http协议 http协议: 对浏览器客户端 和 服务器端 之间数据传输的格式规范 查看http协议的工具 1)使用火狐的firebug插件(右键->firebug-& ...
- 深入理解 Web 协议 (三):HTTP 2
本篇将详细介绍 HTTP 2 协议的方方面面,知识点如下: HTTP 2 连接的建立 HTTP 2 中帧和流的关系 HTTP 2 中流量节省的奥秘:HPACK 算法 HTTP 2 协议中 Server ...
随机推荐
- 使用numba加速python科学计算
技术背景 python作为一门编程语言,有非常大的生态优势,但是其执行效率一直被人诟病.纯粹的python代码跑起来速度会非常的缓慢,因此很多对性能要求比较高的python库,需要用C++或者Fort ...
- Weekly Contest 139
1071. Greatest Common Divisor of Strings For strings S and T, we say "T divides S" if and ...
- Python 极速入门指南
前言 转载于本人博客. 面向有编程经验者的极速入门指南. 大部分内容简化于 W3School,翻译不一定准确,因此标注了英文. 包括代码一共两万字符左右,预计阅读时间一小时. 目前我的博客长文显示效果 ...
- hdu2830 可交换行的最大子矩阵
题意: 求最大子矩阵,但是相邻的列之间可以相互交换... 思路: 回想下固定的情况,记得那种情况是开俩个数组 L[i] ,R[i],记录小于等于i的最左边和最右边在哪个位置,对 ...
- PowerShell-4.API调用以及DLL调用
PowerShell可以直接调用API,So...这东西完全和cmd不是一回事了... 调用API的时候几乎和C#一样(注意堆栈平衡): 调用MessageBox: $iii = Add-Type - ...
- 缓冲流以及JAVA路径相关问题
缓冲流 缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO 次数,从而提高读写的效率. 字节缓冲流 按字节处理 字符缓冲流 按字符处理 实例练习:文 ...
- OOP第四章博客
OOP第四章博客作业 (1)本单元作业架构设计 1)针对于第一次作业,我是将所给类进行了自己的封装,在MyUmlInteraction类里面进行关系的建立,这里把所给的UmlClass建立好,同时有i ...
- 如何更好理解Peterson算法?
如何更好理解Peterson算法? 1 Peterson算法提出的背景 在我们讲述Peterson算法之间,我们先了解一下Peterson算法提出前的背景(即:在这个算法提出之前,前人们都做了哪些工作 ...
- makefile的函数集合
strip函数:$(strip text) 函数功能:去除字符串空格函数 示例: STR = a b c LOSTR = $(strip $(STR)) #结果是&quo ...
- 『动善时』JMeter基础 — 21、HTTP Cookie管理器的使用
目录 1.在HTTP信息头管理器组件中添加Cookie信息 (1)测试计划内包含的元件 (2)请求取样器内容 (3)HTTP信息头管理器内容 (4)查看结果 2.使用HTTP Cookie管理器组件来 ...