HTTP keep-alive及Tomcat配置分析

5 篇文章 0 订阅

1. HTTP协议关于keep-alive的说明

参考“Hypertext Transfer Protocol – HTTP/1.1-8.1 Persistent Connections”( https://tools.ietf.org/html/rfc2616#section-8.1 )。

在持久化连接出现之前,每次访问URL都需要建立一个独立的TCP连接,会增加HTTP服务器的负载,并导致网络拥塞。

持久的HTTP连接有一些优势,例如打开与关闭的TCP连接会减少,可以减少中间经过节点的CPU时间及内存等。

HTTP的各种实现程序,必须实现持久化连接。

从HTTP/1.1版本开始,持久化连接是默认开启的。即比1.1低的版本默认不开启持久化连接。

当客户端或服务器在HTTP头中指定了“Connection: close”时,表示当前请求是对应连接的最后一个请求(即对端下一次请求需要建立新的连接)。

为了保持持久化,连接对应的所有消息必须指定自定义的消息长度(即不是通过连接关闭来定义)。

1.1. 兼容HTTP/1.0协议持久化连接

参考“Hypertext Transfer Protocol – HTTP/1.1-19.7.1 Compatibility with HTTP/1.0 Persistent Connections”( https://tools.ietf.org/html/rfc2068#section-19.7.1 )。

当HTTP/1.1客户端访问HTTP/1.0服务器,或HTTP/1.0客户端访问HTTP/1.1服务器时,可以在HTTP头中指定“Connection: Keep-Alive”来使用持久化连接。

HTTP/1.0客户端无法使用分块传输编码(chunked transfer-coding,即消息体被分为一系列的块进行传输,每个块都有对应的大小标识),因此必须使用Content-Length标记每个消息的结束边界。

1.2. HTTP头中Keep-Alive字段说明

参考“Hypertext Transfer Protocol (HTTP) Keep-Alive Header”( https://tools.ietf.org/id/draft-thomson-hybi-http-timeout-01.html#rfc.section.2 )。

Keep-Alive是一个用于提供持久化连接信息的HTTP头,客户端与服务器都可以独立地提供该信息。

Keep-Alive头的格式如下所示:

Keep-Alive           = "Keep-Alive" ":" 1#keep-alive-info
keep-alive-info      =   "timeout" "=" delta-seconds
                       / "max" "=" 1*DIGIT
                       / keep-alive-extension
keep-alive-extension = token [ "=" ( token / quoted-string ) ]
  • timeout参数

主机可以将Keep-Alive头中的timeout参数设置为一个空闲的连接在被关闭前保持为打开状态的时间。空闲的连接指没有数据发送或接收的连接。

timeout参数值为一个单独的整型,以秒为单位。

设置timeout参数的主机可以保持空闲连接的时间超过timeout参数值,但不得小于timeout参数值。

  • max参数

max参数值代表一个持久化连接允许发起的连接数。当一个连接发起的请求或响应数量达到max参数值时,设置该参数的主机应该关闭该连接。

Keep-Alive头的timeout、max参数示例如下:

Keep-Alive: timeout=5, max=100

2. Tomcat的keep-alive相关属性

以下分析的Tomcat版本为8.5.63。

参考 http://tomcat.apache.org/tomcat-8.5-doc/config/http.html#Standard_Implementation 。

Tomcat的标准HTTP Connector(NIO, NIO2,APR/native)都支持以下属性。

2.1. maxKeepAliveRequests

maxKeepAliveRequests属性表示当一个连接传输的HTTP请求数达到该数值后,Tomcat服务器会关闭该连接。

将该属性值设置为1代表禁用HTTP/1.0及HTTP/1.1的keep-alive(即禁用持久化连接)。

设置为-1代表不限制一个连接允许发起的HTTP请求数量。

若未指定时,默认值为100。

2.2. keepAliveTimeout

keepAliveTimeout属性指定当前Connector在关闭连接前,等待另一个HTTP请求来到的时间毫秒数。

默认值与connectionTimeout属性值相同。-1代表没有超时时间。

2.3. 在Tomcat的access日志中记录HTTP头相关信息

参考 https://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/valves/AbstractAccessLogValve.html 。

可通过server.xml配置在access日志中记录请求或返回的HTTP头,可用于记录keep-alive相关的字段。

“%{xxx}i”可记录请求HTTP头中的指定字段;“%{xxx}o”可记录返回HTTP头中的指定字段。

例如以下可指定记录请求或返回HTTP头中的Connection、Keep-Alive字段。

%{Connection}i
%{Connection}o
%{Keep-Alive}i
%{Keep-Alive}o

3. 验证Tomcat keep-alive生效情况

curl命令支持keep-alive,可以用来验证Tomcat keep-alive生效情况。

以下使用的curl版本为7.29.0。

3.1. curl命令

curl命令格式如下所示:

curl [options] [URL...]

在执行一次curl命令时,可以指定多个url,curl会按照指定的顺序,串行依次执行对应的url。

对于多个文件传输,curl会尝试复用连接,因此从相同的服务器获取多个文件不会执行多次连接或握手。仅当在一次curl命令中访问多个文件时才能够复用连接;在多次单独执行curl命令时,无法复用连接。

可以使用curl来验证Tomcat的keep-alive机制。

3.1.1. curl命令与keep-alive相关参数

  • –keepalive-time

–keepalive-time参数用于指定,当使用某个连接发起两次keep-alive请求时,连接可以保持空闲不被关闭的时间秒数。默认值为60秒。

  • –no-keepalive

根据man curl的文档,–no-keepalive参数用于禁止TCP连接使用keep-alive消息,默认情况下curl会使用该消息。

使用curl命令进行验证,发现“–no-keepalive”参数不能禁止curl进行连接复用。

参考 https://curl.se/mail/archive-2013-04/0037.html ,有以下描述。

What may be less clear is that the "keepalive messages" it talks about is TCP-level "chatter" that makes the connection get used instead of remaining completely idle when nothing is being sent over it.
The curl tool actually has no option that can forcible make a connection not being tried for re-use afterward. 
  • -H

-H参数用于在发送的HTTP请求头中添加指定的数据。

  • -0

-0参数可以强制curl发起请求时使用HTTP 1.0,代替默认的HTTP 1.1。

使用-0参数时,使用HTTP 1.0,默认不会使用keep-alive。

使用-0参数时,指定“-H “Connection: keep-alive””,也可以使用keep-alive。

3.1.2. curl使用keep-alive发送HTTP请求

使用以下命令,使curl连续访问两次相同服务器

curl -v http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8080/test/b.txt

输出结果如下:

* About to connect() to 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /test/a.txt HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8080
> Accept: */*
>
< HTTP/1.1 200
< Accept-Ranges: bytes
< ETag: W/"2-1614674850000"
< Last-Modified: Tue, 02 Mar 2021 08:47:30 GMT
< Content-Type: text/plain
< Content-Length: 2
< Date: Wed, 03 Mar 2021 06:24:02 GMT
<
a
* Connection #0 to host 127.0.0.1 left intact

* Found bundle for host 127.0.0.1: 0xd40ec0
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /test/b.txt HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8080
> Accept: */*
>
< HTTP/1.1 200
< Accept-Ranges: bytes
< ETag: W/"2-1614752620000"
< Last-Modified: Wed, 03 Mar 2021 06:23:40 GMT
< Content-Type: text/plain
< Content-Length: 2
< Date: Wed, 03 Mar 2021 06:24:02 GMT
<
b
* Connection #0 to host 127.0.0.1 left intact

可以看到第一次请求时显示“About to connect() to 127.0.0.1 port 8080 (#0) Trying 127.0.0.1…”,即第一次发送请求需要进行TCP连接。

第二次请求时显示“Found bundle for host 127.0.0.1: 0xd40ec0 Re-using existing connection! (#0) with host 127.0.0.1”,即第二次发送请求时复用了之前建立的连接#0,不需要重新建立连接。

每次请求结束后均显示“Connection #0 to host 127.0.0.1 left intact”,表示当前连接被保留,可以复用。

3.1.3. curl不使用keep-alive发送HTTP请求

如上所述,“–no-keepalive”参数不能使curl不复用连接,为了使curl不复用连接,可以使用-H参数在请求HTTP头指定“Connection: close”,或指定-0参数使用HTTP 1.0:

curl -v -H "Connection: close" http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8080/test/b.txt
curl -v -0 http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8080/test/b.txt

部分输出结果如下所示:

* About to connect() to 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Closing connection 0

* About to connect() to 127.0.0.1 port 8080 (#1)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#1)
* Closing connection 1

可以看到,第一次使用连接#0,第二次使用连接#1,即第二次请求也需要重新建立连接。每次发送请求后显示“Closing connection”,即连接被关闭。

3.2. 使用curl验证Tomcat keep-alive机制

3.2.1. 验证Tomcat maxKeepAliveRequests属性

将Tomcat Connector的maxKeepAliveRequests属性值设置为2(keepAliveTimeout设置为足够大的值),代表一个连接最多发起两次请求,之后需要重新建立连接。

使用以下命令,三次访问相同的服务器:

curl -v http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8080/test/a.txt 

部分输出结果如下所示:

* About to connect() to 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Connection #0 to host 127.0.0.1 left intact

* Found bundle for host 127.0.0.1: 0x10c7f20
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Closing connection 0

* About to connect() to 127.0.0.1 port 8080 (#1)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#1)
* Connection #1 to host 127.0.0.1 left intact

可以看到,第一次请求建立了连接#0;第二次请求复用连接#0,达到maxKeepAliveRequests属性值,请求结束后关闭了连接#0;第三次请求建立新连接#1。

3.2.2. 验证Tomcat keepAliveTimeout属性

为了验证Tomcat keepAliveTimeout属性,需要使curl在一个命令中发起多个请求访问同一个服务器,每次请求结束后需要等待一段时间,使之前建立的连接空闲指定的时间后,再发起下一个请求。

3.2.2.1. 使curl发送多次请求前等待的方法

curl支持在一个命令中发起多个请求,每个请求会依次串行发起,但没有提供设置每个请求发起前等待时间的功能。

为了使curl支持在一个命令中发起多个请求,并在发送前等待指定的时间,可以按照以下方法实现。

在Tomcat被访问的Web工程目录中增加以下jsp文件(保存为sleep_rsp.jsp),当客户端访问后,会等待指定的时间再返回:

<%@ page import="java.lang.*"%>

<%
	String sleepSeconds = request.getParameter("sleep_seconds");
	System.out.println("begin to sleep: " + Thread.currentThread().getName() + " : " + sleepSeconds);
	
	long startTime = System.currentTimeMillis();
	Thread.sleep(Integer.parseInt(sleepSeconds) * 1000L);
	System.out.println("after sleep: " + Thread.currentThread().getName() + " : " + sleepSeconds);
	out.print("start: " + startTime + " end: " + System.currentTimeMillis());
%>

在Tomcat的conf/server.xml文件中配置两个Connector,一个用于验证keep-alive机制(8080端口);一个用于访问上述jsp文件(8081端口),用于起到等待指定时间的作用。

以上需要分为两个端口,对于curl是两个不同的服务器(若以上使用同一个端口,则curl会认为是同一个服务器,上一次请求结束后会立即发起对同一个服务器的下一次访问,无法起到访问前等待的效果)。

3.2.2.2. 使用curl验证Tomcat keepAliveTimeout属性

将Tomcat Connector的keepAliveTimeout属性值设置为3(maxKeepAliveRequests设置为足够大的值),代表一个连接空闲超过3秒后会被关闭,之后需要重新建立连接。

  • 在keepAliveTimeout超时时间内访问

使用以下命令,使curl在访问Tomcat的8080端口后,等待2秒(通过8081访问上述jsp文件实现等待),再访问Tomcat的8080端口。

curl -v http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8081/test/sleep_seconds.jsp?sleep_seconds=2 http://127.0.0.1:8080/test/a.txt

部分输出结果如下所示:

* About to connect() to 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Connection #0 to host 127.0.0.1 left intact

通过8081端口访问jsp文件,等待2秒

* Found bundle for host 127.0.0.1: 0x2530f40
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Connection #0 to host 127.0.0.1 left intact

可以看到,第一次请求8080端口建立了连接#0;第二次请求8080端口,此时连接已空闲2秒,未被Tomcat关闭,复用连接#0。

  • 超过keepAliveTimeout超时时间后访问

使用以下命令,使curl在访问Tomcat的8080端口后,等待4秒,再访问Tomcat的8080端口。

curl -v http://127.0.0.1:8080/test/a.txt http://127.0.0.1:8081/test/sleep_seconds.jsp?sleep_seconds=4 http://127.0.0.1:8080/test/a.txt

部分输出结果如下所示:

* About to connect() to 127.0.0.1 port 8080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Connection #0 to host 127.0.0.1 left intact

通过8081端口访问jsp文件,等待4秒

* Found bundle for host 127.0.0.1: 0x891f40
* Connection 0 seems to be dead!
* Closing connection 0
* About to connect() to 127.0.0.1 port 8080 (#2)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#2)
* Connection #2 to host 127.0.0.1 left intact

可以看到,第一次请求8080端口建立了连接#0;第二次请求8080端口,此时连接已空闲4秒,连接被Tomcat关闭,重新创建连接#2(连接#1用于访问8081端口)。

当curl命令发现准备复用的连接被服务器关闭时,会提示“Connection x seems to be dead!”。

3.2.3. 验证Tomcat返回HTTP头keep-alive相关字段

当curl命令不在HTTP头指定“Connection”参数时,Tomcat返回HTTP头不包含Connection参数。

当curl命令指定HTTP头参数“Connection: keep-alive”时,Tomcat返回HTTP头包含“Connection: keep-alive”。

当curl命令指定HTTP头参数“Connection: close”时,Tomcat返回HTTP头包含“Connection: close”。

当curl命令指定HTTP头参数“Connection: keep-alive”,且Tomcat有配置maxKeepAliveRequests属性(未指定关闭keep-alive)时,Tomcat返回HTTP头包含“Connection: keep-alive”,以及“Keep-Alive: timeout=maxKeepAliveRequests属性值对应秒数”,如“Keep-Alive: timeout=3”。

当客户端复用某个连接请求次数达到maxKeepAliveRequests属性值,或连接空闲时间超过keepAliveTimeout属性值时,Tomcat返回HTTP头包含“Connection: close”。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值