HTTP首部Connection实践

引子

在HTTP/1.0 时代,HTTP与TCP默认传输的关系如图1所示,每次HTTP请求和响应都发生一次TCP三次握手和四次挥手。以HTTP/1.0 时代的网络通信量来看,都是些容量很小的文本传输,所以图1的传输方式性能没有较大问题。


图1 HTTP/1.0默认传输过程

可是随着互联网普及,Web网页的图片逐渐多起来。比如,使用浏览器浏览一个包含多张图片的HTML页面时,在发送HTTP请求访问HTML页面资源的同时,也会请求该HTML页面里包含的其他资源,比如图片等静态文件。因此,每次的请求都会造成无谓的TCP连接建立和断开,增加网络通信量的开销,如图2所示。


图2 一个Web页面的多次HTTP请求

为了解决上述问题,HTTP/1.1和一部分的HTTP/1.0想出了持久连接的办法,即只要任意一端没有明确提出断开连接,则保持TCP连接状态,如图3所示。
图3 HTTP持久连接
HTTP持久连接不是绝对高性能

HTTP持久连接允许在事务处理结束之后将TCP连接保持在打开状态,以便为未来的HTTP请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的TCP连接被称为持久连接。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。
优点:重用已对目标服务器打开的空闲持久连接,可以避开缓慢的连接建立阶段,更快速地进行数据的传输。
缺点:管理不当可能会积累出大量的空闲连接,耗费本地客户端以及远程服务器上的资源。
非持久连接会在每个事务处理结束之后关闭。

HTTP持久连接实现手段是HTTP首部添加Connection字段
  • Connection: keep-alive , 开启HTTP持久连接,HTTP 1.1默认值
  • Connection: close , 关闭HTTP持久连接,HTTP 1.0默认值
HTTP keep-alive与TCP keep-alive区别
  • HTTP keep-alive参数为了减少TCP连接和断开而提出的一种解决方案,HTTP持久连接即TCP长连接。
  • TCP keep-alive参数主要为探测长连接的存活状况,即TCP保活功能。

本文将对HTTP首部Connction实践,对比keep-alive/close两个值在HTTP和TCP的表现情况。后端使用Spring boot+Java,前端使用HTML+CSS。

HTTP Request首部Connection

如果Client希望HTTP使用持久连接,在Request首部指定Connection: keep-alive,否则指定Connection: close
后端Java代码如下:

package com.demo.web.http;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("http")
public class ConnectionController {
    @RequestMapping("/connection")
    public String connection(@RequestHeader(value="Connection") String connection) {
        System.out.println("Connection: " + connection);
        return "http/connection";
    }
}

前端HTML代码:

<!DOCTYPE HTML>
<html>
<head>
    <title>HTTP Connection Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h3>实践HTTP Connection</h3>
<a href="connection">刷新页面</a>
</body>
</html>

后端服务启动端口8080,执行nc 127.0.0.1 8080,输入如下两行命令,两次回车,HTTP Request首部Connection: keep-alive,执行情况如图4第一个红色方框,紧接着返回Web页面,后端日志打印Connection: keep-alive。从图5抓包红色方框看,TCP没有发起四次挥手释放连接,HTTP请求保持TCP长连接。

GET http://127.0.0.1:8080/http/connection HTTP/1.1
Connection: keep-alive
图4 HTTP持久连接请求

图5 HTTP请求tcpdump

再次输入如下两行命令,两次回车,HTTP Request首部Connection: keep-alive,执行情况如图4第二个红色方框,紧接着返回Web页面,后端日志打印Connection: keep-alive。从图5绿色方框看,TCP没有发起四次挥手释放连接,HTTP请求保持TCP长连接。

GET http://127.0.0.1:8080/http/connection HTTP/1.1
Connection: keep-alive

再次输入如下两行命令,两次回车,HTTP Request首部Connection: close,执行情况如图6绿色方框,紧接着返回Web页面,后端日志打印Connection: close。从图5蓝色色方框看,TCP发起四次挥手释放连接,HTTP请求的TCP连接断开。

GET http://127.0.0.1:8080/http/connection HTTP/1.1
Connection: close
图5 HTTP请求

HTTP Response首部Connection

如果Server希望HTTP使用长连接,在Response首部指定Connection: keep-alive,否则指定Connection: close
后端Java代码如下:

package com.demo.web.http;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("http")
public class ConnectionController {
    @RequestMapping("/connection")
    public String connection(HttpServletRequest request, HttpServletResponse response,
                             @RequestHeader(value="Connection") String connection) {
        System.out.println("Connection: " + connection);
        response.addHeader("Connection", "keep-alive");
        return "http/connection";
    }
}

前端HTML代码:

<!DOCTYPE HTML>
<html>
<head>
    <title>HTTP Connection Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h3>实践HTTP Connection</h3>
<a href="connection">刷新页面</a>
</body>
</html>

HTTP/1.1的Request首部Connection默认为keep-alive,当后端返回Response首部Connection: keep-alive,访问http://127.0.0.1:8080/http/connection,如图6所示,点击按钮刷新页面。从图7绿色方框和红色方框看,TCP没有发起四次挥手释放连接,HTTP请求保持TCP长连接。超时后TCP连接会自动断开,从四次挥手的开始时间21:31:40.000676与Web页面请求结束时间21:30:39.945438看,TCP长连接60s超时。

图6 HTTP页面访问

图7 HTTP页面访问tcpdump

HTTP/1.0与HTTP/1.1 首部Connection默认值对比

后端Java代码:

package com.demo.web.http;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("http")
public class ConnectionController {
    @RequestMapping("/connection")
    public String connection() {
        return "http/connection";
    }
}

后端服务启动端口8080,执行nc 127.0.0.1 8080,输入如下命令,两次回车,重复执行如下命令,如图8所示,TCP没有发起四次挥手释放连接,HTTP请求保持TCP长连接,所以HTTP/1.1 首部Connection默认值为keep-alive。

GET http://127.0.0.1:8080/http/connection HTTP/1.1
图8 HTTP/1.1持久连接

后端服务启动端口8080,执行nc 127.0.0.1 8080,输入如下命令,两次回车,如图9所示,TCP释放连接,HTTP请求也结束,所以HTTP/1.0 首部Connection默认值为close。

GET http://127.0.0.1:8080/http/connection HTTP/1.0
图9 HTTP 1.0

再进一步,后端服务启动端口8080,执行nc 127.0.0.1 8080,输入如下命令,两次回车,如图9所示,TCP释放连接,HTTP请求也结束,所以HTTP默认使用1.0版本。

GET http://127.0.0.1:8080/http/connection
图10 HTTP默认版本

HTTP持久连接的数据传输完成识别

HTTP首部定义Connection: keep-alive后,客户端、服务端怎么知道本次传输结束呢?两部分:

  • 静态页面通过Content-Length提前告知对方数据传输大小,具体可以参考拙作HTTP Content-Length深入实践
  • 动态页面不能通过Content-Length提前告知对方数据传输大小,它是分块传输(chunked),这时候就要根据chunked编码来判断,chunked编码的数据在最后有一个空chunked块,表明本次传输数据结束,HTTP头部使用Transfer-Encoding: chunked来代替Content-Length。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • 连接管理 重点 http是如何使用tcp连接的; tcp连接的时延、瓶颈以及存在的障碍 http的优化包括并行连接...
    shenyifu阅读 792评论 0 3
  • 本篇文章篇幅比较长,先来个思维导图预览一下。 一、概述 1.计算机网络体系结构分层 2.TCP/IP 通信传输流 ...
    涤生_Woo阅读 55,001评论 24 557
  • 这里讲的请求是后端DevOps可以控制的范围内,不包括DNS解析,层层的路由等等,一切都从请求到达我们自己架设的服...
    MMoooooon阅读 2,153评论 0 3
  • 当我们在浏览器的地址栏输入 www.linux178.com ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?...
    Ddaidai阅读 1,216评论 0 12
  • 每个人都有 想要将开心喜悦或烦忧 与人诉说分享的需求 曾经只有一个人的时候 就自己默默回味 直到遇见另一个人 以为...
    瑾瑜菇凉阅读 331评论 0 0