不可不知的KeepAlive科普
前言
近日,我的朋友广叔来和我讨论keepalive的相关知识,由于他最近项目上要用,但是他呀又不太了解,所以我就给他讲解了一番,keepalive其实用的也挺多的,在此记录下,希望朋友们用到的话,也能大致明白,这到底是个啥~
一、什么是keepAlive?
广叔:你能给我说说keepAlive是什么吗?最近项目上要用,但是我又不懂,领导还居然让我负责。。。
令狐冲:你们领导挺大胆的呀,居然让你负责,那公司离破产不远了~
广叔:少哔哔,你就直接说吧~我真的要用。。
令狐冲:其实你这问的就很不专业~,你首先要明确你想了解的是哪一种,是TCP的 KeepAlive 还是HTTP的 Keep-Alive。这俩可是完全不同的概念,绝对不能混淆的。
广叔:哦哦,看出来了,写法都不同,HTTP的KeepAlive写法是Keep-Alive,TCP的是KeepAlive。
令狐冲:TCP是传输层的一种协议,而HTTP是应用层的一种协议。不要混淆!
广叔:那你要不先讲讲TCP的keepalive?
令狐冲:TCP的keepalive是侧重在保持客户端和服务端的连接,一方会不定期发送心跳包给另一方,当一方端掉的时候,没有断掉的定时发送几次心跳包,如果间隔发送几次,对方都返回的是RST,而不是ACK,那么就释放当前链接。
广叔:那这个有啥用吗?没想出来呀。。
令狐冲:那你设想一下,如果tcp层没有keepalive的机制,一旦一方断开连接却没有发送FIN给另外一方的话,那么另外一方会一直以为这个连接还是存活的,几天,几月。那么这对服务器资源的影响是很大的。
广叔:哎呦,还真是的,之前没想到啊,没有keepalive的话,TCP的中可能存在大量的无效连接却占着很多资源。
广叔:那HTTP中的keep-alive呢?http连接不就是客户端连接上服务端,然后结束请求后,由客户端或者服务端进行http连接的关闭。下次再发送请求的时候,客户端再发起一个连接,传送数据,关闭连接。这么个流程反复吗?不会有问题的呀?
令狐冲:正常的情况确实是你说的那样子的,但是一旦客户端发送connection:keep-alive头给服务端,且服务端也接受这个keep-alive的话,两边对上暗号,这个连接就可以复用了,一个http处理完之后,另外一个http数据直接从这个连接走了。减少新建和断开TCP连接的消耗。
广叔:wow!,这个操作也太骚了吧?太省资源了。
广叔:我简单概括下:HTTP协议的Keep-Alive意图在于短时间内连接复用,希望可以短时间内在同一个连接上进行多次请求/响应。
广叔:TCP的KeepAlive机制意图在于保活、心跳,检测连接错误。当一个TCP连接两端长时间没有数据传输时(通常默认配置是2小时),发送keepalive探针,探测链接是否存活。
令狐冲:是的,你要记住HTTP的Keep-Alive和TCP的KeepAlive不是一回事,tcp的keepalive是在ESTABLISH状态的时候,双方如何检测连接的可用行。而http的keep-alive说的是如何避免进行重复的TCP三次握手和四次挥手的环节。
二、TCP之KeepAlive详解
2.1 为什么需要KeepAlive?
广叔:你前面说的有点浅,忽悠小白还可以,想忽悠我们那个大胖子领导,还是不行呀,我先说说我对TCP知识的理解吧,你看看下边这个图,就是在TCP层发出一个请求,然后。。(打断)
令狐冲:停!首先要明确的是在TCP层是没有“请求”一说的,TCP是一种通信的方式,“请求”一词是事务上的概念,HTTP协议是一种事务协议,如果说发送一个HTTP请求,这种说法就没有问题。
广叔:我去,之前都记得是错的呀,这次真的记住了。
令狐冲:TCP的三次握手,你还记得吧?看看下边:
广叔:哎呀,看了你的图,我回忆起来了,之前在学校的时候学过的~
令狐冲:那下面我通过wireshark抓取一个TCP建立握手的过程,您仔细看下。(命令行基本上用TCPdump,后面我们还会用这张图说明问题):
令狐冲:现在你只要看前3行,这就是TCP三次握手的完整建立过程,第一个报文SYN从发起方发出,第二个报文SYN,ACK是从被连接方发出,第三个报文ACK确认对方的SYN,ACK已经收到,是不是和上面的图我是完全对应的?
广叔:是的,但是我看数据实际上并没有传输呀,第四个报文才是数据传输开始的过程。
令狐冲:是的,wireshark把第四个报文解析成HTTP协议,HTTP协议的GET方法和URI也解析出来,所以说TCP层是没有请求的概念,HTTP协议是事务性协议才有请求的概念,TCP报文承载HTTP协议的请求(Request)和响应(Response)。
广叔:说了半天,还没说为啥需要keepAlive呢?
令狐冲:别急嘛,这就说。链接建立之后,如果应用程序或者上层协议一直不发送数据,或者隔很长时间才发送一次数据,当链接很久没有数据报文传输时如何去确定对方还在线,到底是掉线了还是确实没有数据传输,链接还需不需要保持,这种情况在TCP协议设计中是需要考虑到的。
广叔:那咋解决的呀?
令狐冲:TCP协议通过一种巧妙的方式去解决这个问题,当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。
广叔:这就是keepAlive?这也不难嘛~~
2.2 如何开启KeepAlive?
广叔:那KeepAlive到底是咋开启的?默认就开启了吗?
令狐冲:并不是默认开启的,在Linux系统上没有一个全局的选项去开启TCP的KeepAlive。需要开启KeepAlive的应用必须在TCP的socket中单独开启。
令狐冲:在设置keepAlive之前,我们可以看看它都支持哪些选项:
1. KeepAlive默认情况下是关闭的,可以被上层应用开启和关闭
2. tcp_keepalive_time: KeepAlive的空闲时长,或者说每次正常发送心跳的周期,默认值为7200s(2小时)
3. tcp_keepalive_intvl: KeepAlive探测包的发送间隔,默认值为75s
4.tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)
令狐冲:在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
令狐冲:添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置,当然,如果应用中已经设置SO_KEEPALIVE,程序不用重启,内核直接生效。
广叔:这么简单呀,我回头要试试~~
令狐冲:是的呀,还可以使用Java、C语言以及在Nginx设置,这里我就不一一展开了。
2.3 keepAlive的局限性
广叔:那你说这个技术这么好,为啥不是默认开启的吗?
令狐冲:哎呦,终于感觉到你的秃头可能真的有点属于聪明绝顶了,没错,tcp自带的keepalive还是有些不足之处的。
令狐冲:keepalive只能检测连接是否存活,不能检测连接是否可用。例如,某一方发生了死锁,无法在连接上进行任何读写操作,但是操作系统仍然可以响应网络层keepalive包。
广叔:这确实是个问题,我之前没有想到这一点。还有吗?
令狐冲:TCP keepalive机制依赖于操作系统的实现,灵活性不够,默认关闭,且默认的 keepalive 心跳时间是 两个小时, 时间较长。并且,代理(如socks proxy)、或者负载均衡器,会让tcp keepalive失效。
广叔:那就没有解决办法了吗?
令狐冲:办法肯定是有的呀,我们往往需要加上应用层的心跳,这个需要自己去实现,就不展开了。。
三、HTTP之Keep-Alive详解
3.1 http为什么需要Keep-Alive?
广叔:那你再说说为啥http也需要keep-Alive呗~~
令狐冲:通常一个网页可能会有很多组成部分,除了文本内容,还会有诸如:js、css、图片等静态资源,有时还会异步发起AJAX请求。只有所有的资源都加载完毕后,我们看到网页完整的内容。然而,一个网页中,可能引入了几十个js、css文件,上百张图片,如果每请求一个资源,就创建一个连接,然后关闭,代价是不是太大了?
广叔:嗯,确实很大,如果这些连接能够复用就好了~
令狐冲:是的,大牛们也是这样想的,他们也希望连接能够在短时间内得到复用,在加载同一个网页中的内容时,尽量的复用连接,这就是HTTP协议中keep-alive属性的作用。
广叔:那这个也是默认关闭的吗?
令狐冲:这个可不是,要看版本,HTTP的Keep-Alive是HTTP1.1中默认开启的功能。通过headers设置”Connection: close “关闭;在HTTP1.0中是默认关闭的。通过headers设置”Connection: Keep-Alive”开启。
广叔:那这个保持连接的时间是怎么确定的呢?
令狐冲:Keep-Alive属性保持连接的时间长短是由服务端决定的,通常配置都是在几十秒左右。
令狐冲:TCP连接建立之后,HTTP协议使用TCP传输HTTP协议的请求(Request)和响应(Response)数据,一次完整的HTTP事务如下图:
令狐冲:这张图我简化了HTTP(Req)和HTTP(Resp),实际上的请求和响应需要多个TCP报文,广叔,你从这张图中看出了啥呀?说说
广叔:从图中可以发现一个完整的HTTP事务,有链接的建立,请求的发送,响应接收,断开链接这四个过程。
令狐冲:是的,早期通过HTTP协议传输的数据以文本为主,一个请求可能就把所有要返回的数据取到,但是,现在要展现一张完整的页面需要很多个请求才能完成,如图片.JS.CSS等,如果每一个HTTP请求都需要新建并断开一个TCP,这个开销是完全没有必要的。
广叔:是的,没有必要,资源都浪费了~
令狐冲:开启HTTP Keep-Alive之后,能复用已有的TCP链接,当前一个请求已经响应完毕,服务器端没有立即关闭TCP链接,而是等待一段时间接收浏览器端可能发送过来的第二个请求,通常浏览器在第一个请求返回之后会立即发送第二个请求,如果某一时刻只能有一个链接,同一个TCP链接处理的请求越多,开启KeepAlive能节省的TCP建立和关闭的消耗就越多。
广叔:那直接使用一个链接不就更省资源了?
令狐冲:这。。。那和电脑只用一个CPU核调度有啥区别?所有的进程都等着这个cpu去调度吗?你分手的时候,不是还说,不能在一棵树上吊死吗?要拥抱森林来着?
令狐冲:通常会启用多个链接去从服务器器上请求资源,但是开启了Keep-Alive之后,仍然能加快资源的加载速度。HTTP/1.1之后默认开启Keep-Alive, 在HTTP的头域中增加Connection选项。当设置为Connection:keep-alive表示开启,设置为Connection:close表示关闭。
广叔:ok,我是彻底明白了。
===================================================
字节内推码:B1RHWFK
官网校招简历投递通道:https://jobs.toutiao.com/campus/m/position?referral_code=B1RHWFK