在http1.1中增加了长连接的特性,在创建连接之后,在设定的时间范围内,客户端可以复用连接进行数据传输。这样就减少了建立连接的时间,对于服务器而言,由于要保持长连接,负载和可用的连接必然会减少。我们用tcpdump工具来分析下这样的行为。
在nginx中,keepalive_timeout参数可以设置长连接时间。值为0说明关闭长连接,为了便于观察,设置了60s为长连接时间。
设置keepalive_timeout为60s
客户端立即关闭连接
在php中,使用curl发送请求,请求完成后,立即调用curl_close进行关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[root@premium.dev152.youku]$ tcpdump -n host 10.10.56.63 and port 80 -v tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 13:34:27.444706 IP (tos 0x0, ttl 62, id 42325, offset 0, flags [DF], proto TCP (6), length 60) 10.10.56.63.45597 > 10.10.72.152.http: Flags [S], cksum 0x519b (correct), seq 523271414, win 14600, options [mss 1460,sackOK,TS val 3922339 ecr 0,nop,wscale 6], length 0 13:34:27.444741 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52) 10.10.72.152.http > 10.10.56.63.45597: Flags [S.], cksum 0x77b1 (correct), seq 1792372720, ack 523271415, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 9], length 0 13:34:27.446835 IP (tos 0x0, ttl 62, id 42326, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45597 > 10.10.72.152.http: Flags [.], cksum 0xf0a8 (correct), ack 1, win 229, length 0 13:34:27.447209 IP (tos 0x0, ttl 62, id 42327, offset 0, flags [DF], proto TCP (6), length 387) 10.10.56.63.45597 > 10.10.72.152.http: Flags [P.], cksum 0x5225 (correct), seq 1:348, ack 1, win 229, length 347 13:34:27.447225 IP (tos 0x0, ttl 64, id 60336, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.56.63.45597: Flags [.], cksum 0xf013 (correct), ack 348, win 31, length 0 13:34:27.471324 IP (tos 0x0, ttl 64, id 60337, offset 0, flags [DF], proto TCP (6), length 297) 10.10.72.152.http > 10.10.56.63.45597: Flags [P.], cksum 0x9606 (incorrect -> 0x9ee5), seq 1:258, ack 348, win 31, length 257 13:34:27.473479 IP (tos 0x0, ttl 62, id 42328, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45597 > 10.10.72.152.http: Flags [.], cksum 0xee3c (correct), ack 258, win 245, length 0 13:34:27.474422 IP (tos 0x0, ttl 62, id 42329, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45597 > 10.10.72.152.http: Flags [F.], cksum 0xee3b (correct), seq 348, ack 258, win 245, length 0 13:34:27.474459 IP (tos 0x0, ttl 64, id 60338, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.56.63.45597: Flags [F.], cksum 0xef10 (correct), seq 258, ack 349, win 31, length 0 13:34:27.476397 IP (tos 0x0, ttl 62, id 42330, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45597 > 10.10.72.152.http: Flags [.], cksum 0xee3a (correct), ack 259, win 245, length 0 |
10.10.56.63请求的客户机ip。重点关注下时间点和flags字段。flags字段说明:S (SYN), F (FIN), P (PUSH), R (RST), U (URG), W (ECN CWR), E (ECN-Echo) or ‘.’ (ACK), or ‘none’。
10.10.56.63首先发起请求握手连接。在13:34:27.474422时间点,由客户机10.10.56.63发起了FIN标记断开连接。然后服务器10.10.72.152进行应答(ACK),并且也发送了FIN标记,最后客户端应答。至此,断开四步骤完成,请求完成。
客户端不立即关闭连接
为了模拟此请求,我们在curl_close之前,调用了sleep(70),模拟客户端延时关闭连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[root@premium.dev152.youku]$ tcpdump -n host 10.10.56.63 and port 80 -v tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 13:22:39.270284 IP (tos 0x0, ttl 62, id 35331, offset 0, flags [DF], proto TCP (6), length 60) 10.10.56.63.45596 > 10.10.72.152.http: Flags [S], cksum 0x6e13 (correct), seq 3435233614, win 14600, options [mss 1460,sackOK,TS val 3214157 ecr 0,nop,wscale 6], length 0 13:22:39.270321 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52) 10.10.72.152.http > 10.10.56.63.45596: Flags [S.], cksum 0x2c0b (correct), seq 164719282, ack 3435233615, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 9], length 0 13:22:39.272475 IP (tos 0x0, ttl 62, id 35332, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45596 > 10.10.72.152.http: Flags [.], cksum 0xa502 (correct), ack 1, win 229, length 0 13:22:39.272883 IP (tos 0x0, ttl 62, id 35333, offset 0, flags [DF], proto TCP (6), length 387) 10.10.56.63.45596 > 10.10.72.152.http: Flags [P.], cksum 0xc6fa (correct), seq 1:348, ack 1, win 229, length 347 13:22:39.272897 IP (tos 0x0, ttl 64, id 57152, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.56.63.45596: Flags [.], cksum 0xa46d (correct), ack 348, win 31, length 0 13:22:39.296408 IP (tos 0x0, ttl 64, id 57153, offset 0, flags [DF], proto TCP (6), length 297) 10.10.72.152.http > 10.10.56.63.45596: Flags [P.], cksum 0x9606 (incorrect -> 0x5240), seq 1:258, ack 348, win 31, length 257 13:22:39.298529 IP (tos 0x0, ttl 62, id 35334, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45596 > 10.10.72.152.http: Flags [.], cksum 0xa296 (correct), ack 258, win 245, length 0 13:23:39.296451 IP (tos 0x0, ttl 64, id 57154, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.56.63.45596: Flags [F.], cksum 0xa36b (correct), seq 258, ack 348, win 31, length 0 13:23:39.448521 IP (tos 0x0, ttl 62, id 35335, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45596 > 10.10.72.152.http: Flags [.], cksum 0xa295 (correct), ack 259, win 245, length 0 13:23:49.301656 IP (tos 0x0, ttl 62, id 35336, offset 0, flags [DF], proto TCP (6), length 40) 10.10.56.63.45596 > 10.10.72.152.http: Flags [F.], cksum 0xa294 (correct), seq 348, ack 259, win 245, length 0 13:23:49.301716 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.56.63.45596: Flags [R], cksum 0xf302 (correct), seq 164719541, win 0, length 0 |
客户机在13:22:39发起了请求,由于没有关闭连接,连接一直保持。在60s后,在13:23:39,服务器10.10.72.152主动发送了FIN。又过了10s,在13:23:49,客户机才发送FIN请求,至此,整个请求完成。
这里需要说明的是,在keepalive_timeout时间内,客户端一直没有请求,服务端才会主动断开。否则,服务器会重置等待时间。在上面的例子中,应该以 13:22:39.296408 这个时间点计算, 60s后,服务器断开连接。
浏览器发起请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
13:37:57.378016 IP (tos 0x0, ttl 62, id 34821, offset 0, flags [DF], proto TCP (6), length 64) 10.10.65.13.62261 > 10.10.72.152.http: Flags [S], cksum 0x3a44 (correct), seq 270331699, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 489639675 ecr 0,sackOK,eol], length 0 13:37:57.378054 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 52) 10.10.72.152.http > 10.10.65.13.62261: Flags [S.], cksum 0x67ab (correct), seq 2064237234, ack 270331700, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 9], length 0 13:37:57.378300 IP (tos 0x0, ttl 62, id 14721, offset 0, flags [DF], proto TCP (6), length 40) 10.10.65.13.62261 > 10.10.72.152.http: Flags [.], cksum 0xc187 (correct), ack 1, win 8192, length 0 13:37:57.378761 IP (tos 0x0, ttl 62, id 3628, offset 0, flags [DF], proto TCP (6), length 1500) 10.10.65.13.62261 > 10.10.72.152.http: Flags [.], cksum 0x105d (correct), seq 1:1461, ack 1, win 8192, length 1460 13:37:57.378776 IP (tos 0x0, ttl 64, id 31486, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.65.13.62261: Flags [.], cksum 0xdbb0 (correct), ack 1461, win 35, length 0 13:37:57.378785 IP (tos 0x0, ttl 62, id 59269, offset 0, flags [DF], proto TCP (6), length 367) 10.10.65.13.62261 > 10.10.72.152.http: Flags [P.], cksum 0x5f8d (correct), seq 1461:1788, ack 1, win 8192, length 327 13:37:57.378788 IP (tos 0x0, ttl 64, id 31487, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.65.13.62261: Flags [.], cksum 0xda64 (correct), ack 1788, win 40, length 0 13:37:57.400895 IP (tos 0x0, ttl 64, id 31488, offset 0, flags [DF], proto TCP (6), length 297) 10.10.72.152.http > 10.10.65.13.62261: Flags [P.], cksum 0x9ed4 (incorrect -> 0x8332), seq 1:258, ack 1788, win 40, length 257 13:37:57.401473 IP (tos 0x0, ttl 62, id 59427, offset 0, flags [DF], proto TCP (6), length 40) 10.10.65.13.62261 > 10.10.72.152.http: Flags [.], cksum 0xb994 (correct), ack 258, win 8183, length 0 13:38:42.748943 IP (tos 0x0, ttl 62, id 29819, offset 0, flags [none], proto TCP (6), length 40) 10.10.65.13.62261 > 10.10.72.152.http: Flags [.], cksum 0xb98c (correct), ack 258, win 8192, length 0 13:38:42.748961 IP (tos 0x0, ttl 64, id 31489, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.65.13.62261: Flags [.], cksum 0xd963 (correct), ack 1788, win 40, length 0 13:38:57.400455 IP (tos 0x0, ttl 64, id 31490, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.65.13.62261: Flags [F.], cksum 0xd962 (correct), seq 258, ack 1788, win 40, length 0 13:38:57.400988 IP (tos 0x0, ttl 62, id 39904, offset 0, flags [DF], proto TCP (6), length 40) 10.10.65.13.62261 > 10.10.72.152.http: Flags [.], cksum 0xb98a (correct), ack 259, win 8192, length 0 13:39:02.489271 IP (tos 0x0, ttl 62, id 50452, offset 0, flags [DF], proto TCP (6), length 40) 10.10.65.13.62261 > 10.10.72.152.http: Flags [F.], cksum 0xb989 (correct), seq 1788, ack 259, win 8192, length 0 13:39:02.489299 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 10.10.72.152.http > 10.10.65.13.62261: Flags [.], cksum 0xd961 (correct), ack 1789, win 40, length 0 |
从浏览器发送请求时,请求头默认会有Connection:keep-alive。如果服务器开启了keep-alive,回复头里也会包含此值。上面的报文中,也是服务端超时60s后,主动断开连接。
关闭keepalive_timeout
如果服务端关闭了keep-alive。在上面的三种情况下,服务端回复完成后会立即发送FIN,主动断开连接。
在这种情况下如果客户端保存了curl连接,多次使用curl_exec发送请求时,每次请求也是以单独的连接存在的。
总结
通过用户访问的请求,合理的设置keep-alive。如果单一用户多次频繁请求,设置合理的keepalive_timeout,是可以提高性能的。如果服务被多方调用,为了保障服务质量,最好关闭keepalive。在开启的情况下,客户端有可能存在bug,没有主动断开连接,或者恶意请求,服务器会因连接数过多,造成服务不可用。
反过来讲,如果某些内部接口,开启了keepalive, 在后端脚本发起请求时,可以不立即调用curl_close,而重用curl,达到长连接效果,提高处理速度。
以上讨论都是http1.1的。如果使用的http1.0,不管设置keepalive_timeout与否,服务器端都会主动断开连接。