傻傻分不清的TCP keepalive和HTTP keepalive

1.TCP keepalive

1.1.概念

A keepalive (KA) is a message sent by one device to another to check that the link between the two is operating, or to prevent the link from being broken.

——From wiki

TCP keepalive是TCP的保活定时器。通俗地说,就是TCP有一个定时任务做倒计时,超时后会触发任务,内容是发送一个探测报文给对端,用来判断对端是否存活。(想到一个桥段:“如果2小时后没等到我的消息,你们就快跑”)

1.2.作用

正如概念中说的,用于探测对端是否存活,从而防止连接处于“半打开”状态。

所谓半打开,就是网络连接的双端中,有一端已经断开,而另一端仍然处于连接状态。

1.3.机制

TCP keepalive流程图.drawio.png

(图一)TCP keepalive 流程图

建立连接的双端在通信的同时,存在一个定时任务A,每当传输完一个报文,都会重置定时任务A。如果在定时任务的时限tcp_keepalive_time内不再有新的报文传输,便会触发定时任务A,向对端发送存活探测报文。根据响应报文的不同情况,有不同的操作分支,如上图所示。

定时任务B会被循环执行,具体逻辑是:定时任务A的探测报文没有得到响应报文,开始执行定时任务B。任务B的内容同样是发送探测报文,但不同的是,B会被执行tcp_keepalive_probes次,时间间隔为tcp_keepalive_intvl。B的探测报文同样也是在收到响应报文后,重置定时任务A,维持连接状态。

[tips1]

上文提到的三个参数存在于系统文件中,具体路径如下:

/proc/sys/net/ipv4/tcp_keepalive_time
/proc/sys/net/ipv4/tcp_keepalive_intvl
/proc/sys/net/ipv4/tcp_keepalive_probes

[tips2]

通信双端都存在一个文件作为数据缓冲区,对端发送给本地当前端口的数据都会缓冲在这个文件中。上文中讲的“断开连接”就是关闭这个文件,关闭后所有发送到当前端口的数据将无法存储到缓冲区,即数据被丢弃了。

通过指令lsof -i :8080,8080改成你的端口号,便能看到这个缓冲区文件。

2.HTTP keepalive

2.1.概念

HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair. The newer HTTP/2 protocol uses the same idea and takes it further to allow multiple concurrent requests/responses to be multiplexed over a single connection.

——From wiki

HTTP keepalive指的是持久连接,强调复用TCP连接。(类似场景:挂电话之前总会问句,没啥事就先挂了,延长通话时长来确认没有新话题)

2.2.作用

延长TCP连接的时长,一次TCP连接从创建到关闭期间能传输更多的数据。

2.3.机制

HTTP keepalive流程图.drawio.png

(图二)HTTP keepalive 流程图

通信连接的双端在通信的同时,存在一个HTTP层面的keepalive定时任务。当客户端发起Request,并且接收到Response之后,触发定时任务。定时任务会开始计时,达到keepalive的时间距离后,关闭连接。如果在计时期间,客户端再次发起Request,并且接收到Response,定时任务会被重置,从头计时。

[tips1]

图二用Python的socket库为示例进行说明,在HTTP的“请求-响应”过程中,HTTP keepalive(或者称为HTTP持久连接)在底层是如何作用于连接释放流程,从而延长连接时长的。

[tips2]

为什么不用Python的requests库来举例说明?requests底层也是socket连接管理,不同的是requests支持HTTP协议,可以解析出HTTP各部分信息;socket仅仅是从文件缓冲区读取二进制流。同样地,各种Web框架中的Request和Response对象的内部仍然是socket连接管理,只提socket可以排除很多干扰信息。

[tips3]

服务端HTTP keepalive超时后的数据丢弃的说明。刚入门的同学可能也会像我一样感到疑惑:服务端keepalive超时后再收到数据就会丢弃,那么服务端后续还怎么接收端口的数据?

这就不得不提到服务端的fork模型了:服务端主进程监听端口,当数据到来时便交给子进程来处理,主进程继续循环监听端口。

具体地说,当数据到来时,主进程先创建新的socket连接句柄(本质就是生成了socket文件描述符落在磁盘上,端口数据会存储在该文件中缓冲),随后fork出子进程;主进程关闭新的socket句柄,子进程则维持socket句柄的连接(当一个socket句柄在所有进程中都被close之后才会开始TCP四次挥手);此后,子进程接管了与客户端的通信。

正如(图三)的例子,主进程会fork出很多子进程,A和B分别对接的是不同客户端发来的请求,socket文件描述符a不会影响b的数据读写。

fork模型.drawio.png

(图三)fork模型下传递socket句柄的过程

结论是,服务端与外界建立的每一个socket连接,都有独立的文件描述符和独立的子进程与客户端通信。服务端断开连接是指关闭了某个文件描述符的读写,并非关闭了整个端口的数据往来,不影响其他的socket连接之间通信。至于丢弃,就是说外界如果还有发往这个socket文件描述符的数据被丢弃,因为这个文件描述符已经禁止写入,自然地数据便无法落地。

3.两者之间的关系

TCP keepalive更像是保障系统有序工作的兜底机制,确保系统最终能收回半打开的socket连接,否则长期运行后无法再接收更多的请求(系统的socket最大连接数限制)。

HTTP keepalive则是应用层的骚操作,使得服务端的应用程序能自主决定socket的释放,因为TCP keepalive的倒计时默认值很长,web服务的某次连接通常不需要等待那么久。说直白点,就是TCP有一个计时器,HTTP也可以自己搞个计时器,如果HTTP的计时器先超时,同样有权利让TCP进入四次挥手流程。

在某个数据包传输后,两个keepalive的定时任务同时存在且一起进入倒计时状态,一个是系统内核TCP相关代码的程序,另一个是高级编程语言(Python/Java/Go等)Web框架代码的程序,他们一起运行并不冲突。

4.念经

HTTP keepalive是应用层的东西,在上生产时对外提供服务的应用程序都会有keepalive参数,例如Gunicorn的keepalive、Nginx的keepalive_timeout。通过这个参数,我们能在更高级的层面控制等待下一个数据的时长。

还有,如果同一台服务器有N个Web服务,TCP keepalive参数是全局生效,众口难调。

如果你的网络结构是类似client-nginx-web server,那么你就要同时考虑nginx和web server的keepalive参数大小搭配的问题,此处引用Gunicorn对keepalive参数的使用建议:

Generally set in the 1-5 seconds range for servers with direct connection to the client (e.g. when you don’t have separate load balancer). When Gunicorn is deployed behind a load balancer, it often makes sense to set this to a higher value.

假设web等待时间比nginx短很多,client-nginx的连接还在,nginx-web就已经断开了,web就会错过一些数据,对于客户来说好端端的我拿不到结果是无法容忍的。因此最好是和nginx的等待时间协调好,不要相差太多(不要太短,也不要长很多)。

关于不要太长,多说一句。如果等待很久,web服务会累积维持非常多的连接,这样子新的请求无法打进来,正在维持的连接不见得利用率很高(可能客户端的代码在打断点、可能客户端早就close)。结果就是服务端netstat显示一堆连接,新的请求全都被挂起甚至丢弃。

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

推荐阅读更多精彩内容

  • 什么是心跳机制? 心跳说的是在客户端和服务端在互相建立ESTABLISH状态的时候,如何通过发送一个最简单的包来保...
    tracy_668阅读 4,885评论 1 5
  • 很久没有分析一个生产问题超过3天了,是TCP的一个血案,C语言编写。我是一个Java人员,这次只能依靠对T...
    stamp1238阅读 3,212评论 8 28
  • Netty权威指南2-读书笔记 UNIX网络编程5种I/O模型 I/O 复用模型(最大的优势是多路复用)Linux...
    landon30阅读 514评论 0 0
  • 长连接与短连接 TCP 本身并没有长短连接的区别 ,长短与否,完全取决于我们怎么用它。 短连接:每次通信时,创建 ...
    tracy_668阅读 4,544评论 1 3
  • 16宿命:用概率思维提高你的胜算 以前的我是风险厌恶者,不喜欢去冒险,但是人生放弃了冒险,也就放弃了无数的可能。 ...
    yichen大刀阅读 6,049评论 0 4