HTTP keepalive详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP keepalive详解相关的知识,希望对你有一定的参考价值。

参考技术A

在http早期,每个http请求都要求打开一个tpc socket连接,并且使用一次之后就断开这个tcp连接。

使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,也意味着可以减少TIME_WAIT状态连接,以此提高性能和提高httpd服务器的吞吐率(更少的tcp连接意味着更少的系统内核调用,socket的accept()和close()调用)。

但是, keep-alive 并不是免费的午餐,长时间的tcp连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。
Httpd守护进程,一般都提供了keep-alive timeout时间设置参数。比如nginx的keepalive_timeout。这个keepalive_timout时间值意味着:一个http产生的tcp连接在传送完最后一个响应后,还需要hold住keepalive_timeout秒后,才开始关闭这个连接。

当httpd守护进程发送完一个响应后,理应马上主动关闭相应的tcp连接,设置 keepalive_timeout后,httpd守护进程会想说:”再等等吧,看看浏览器还有没有请求过来”,这一等,便是keepalive_timeout时间。如果守护进程在这个等待的时间里,一直没有收到浏览发过来http请求,则关闭这个http连接。

假设我们的httpd服务器是nginx, nginx的keepalive_timeout默认是60,即默认开启了http keepalive模式,而且http的长连接会保持60秒,60秒内没有数据来往即关闭连接。

这里我们设置业务sleep 60 秒

可以看到上面是经典的http过程,首先是3次握手建立连接,然后客户端发送请求,服务器处理60秒后返回数据,http响应一旦发送完毕,nginx马上关闭这个tcp连接
关闭 keepalive 情况下,一个socket资源从建立到真正释放需要经过的时间是:建立tcp连接 + 传送http请求 + 业务执行 + 传送http响应 + 关闭tcp连接 + 2MSL

可以看到这个http过程跟上面的keepalive=0情况是比较类似的,只不过当服务器响应完数据后,没有立刻发送FIN信号关闭连接,而是从38分等到43分,nginx才发送FIN信号关闭连接。说明了nginx的keepalive=300配置生效了。

这次我们开启了keepalive=180,并且连续发送了几个http请求,可以看到,都复用了同一个连接(因为没有再次的三次握手建立连接),而nginx再最后一次数据发送后,在过了180秒后,发送FIN信号关闭了连接。 这说明nginx的keepalive倒计时是从最后一个数据包开始计算的!

nginx代理与上游服务器 upstream 之间的连接默认是关闭了长连接,我们可以通过抓包来看到,通过浏览器提交上来的HTTP/1.1的请求,经过代理服务器以后改为HTTP/1.0, 所以想要代理与服务器之间使用长连接,需要修改nginx的相关配置。

upstream里面的有几个配置是跟keepalive相关的:

对于location里面的配置,主要是修改http版本为1.1, 并且把nginx默认传递给upsteam connection:close的行为去掉
http_version需要指定用1.1 ,以及原有的Connection头部要被擦掉

这样就完成了nginx转发给upstream的长连接配置了,但是,我们的upstream也要配置keepalive才行啊,否则它无视nginx发来的connection:keepalive 头部也不行。
我们用的是flask 框架,本地调试的话要加上下面的配置,才能使用上http 1.1

思考一下,用uwsgi的花需要另外配置keepalive吗?
回答:uwsgi支持keep-alive 如果nginx通过http pass 给uwsgi, --http-keepalive

但是nginx 一般都是通过uwsgi_pass 传递请求给uwsgi,而我们说的是http协议的keepalive ,所以对于其他协议诸如,uwsgi协议,fastcgi协议都是不能生效的
参考
nginx proxy 的规则
nginx keepalive doc
那么至此,nginx跟上游服务器的长连接配置就完成了

curl后面跟随两个连接,让curl同时请求两个url
这时抓包

可以看到,两次的请求,都是使用同一个端口,请求完毕了后,curl主动关闭socket, 毕竟程序也关闭了
所以客户端->nginx的连接是实现了http keep-alive
而此时,如果注释掉了nginx配置,

那么此时再次请求,客户端->nginx连接还是可以keep-alive的,但是nginx->upstream就不行了,因为那是针对ngxin->upstream的配置,客户端跟nginx的配置默认就是能keep-alive

为什么这样请求过去nginx,跟nginx的连接没有复用,每次循环,客户端完了都会发送F.信号,而不会复用连接呢?
这个问题跟一开始我想在终端curl http://xxx 一样想看到第N次curl的时候,都会复用同一个连接,但是上网查了下,使用curl -v curl默认就是http 1.1, connection: keep-alive的,但是curl这个程序结束以后,就会直接关闭socket,这也很正常,程序都关闭了,socket自然就会关闭。

但是这里的我自己写个程序,循环5次,程序没有关闭,它也会自动关闭socket,没有复用连接,看来是requests的问题。
上网查了下,是requests的问题,它默认不支持keepalive,需要使用requests.session 对象来请求,才能使用上keepalive

浏览器打开 http://app-store-server.webapp.163.com:8000/api/recommend/index?a 确实可以复用连接,因为http1.1默认开启了connection: keepalive
当connection:keep-alive的头跟随请求到达nginx,nginx就会keepalive,nginx 是否开启keep-alive的配置就是keepalive-timout, 设置成0就是不开启keepalive。
为了校验效果我我把nginx的keepalive_timeout 设置成了10
连续请求3次,tcp抓包看到

等待10秒后,nginx发送F.信号了,符合我们预期,然后浏览器也立马返回ack信号了。
但是浏览器居然没有发送F.给nginx,这时后的80端端口处于 FIN_WAIT2状态

为什么浏览器没有及时发送F.信号呢?而是在两分钟后,浏览器发送R.信号给nginx,这时的连接才被完全关闭了。

在nginx还处于FIN_WAIT2的时候,也就是说client没想过要发送Fin信号,是因为它认为自己还有数据要发,所以不关闭吗?然后设置了2分钟超时时间,发送R信号,重置连接。 那么这样服务器不是一直得占用了一个端口处于FIN_WAIT2状态吗?这不是很浪费吗?
在nginx还处于FIN_WAIT2的时候,我让浏览器继续发送同一个请求过去nginx,看看会怎样的,抓包看到

上面的流程:

测试过程中还碰到一种情况是,nginx keepalive 超时后,发送FIN给客户端, 这时客户端再次请求,就走正常流程了,向nginx发送FIN.信号,nginx也回应ack信号。

TCP keepalive的探究 (2) : 浏览器的Keepalive机制
TCP保活(TCP keepalive)
tcpdump flags
HTTP Keep-Alive是什么?如何工作?
超时与重试机制(一)

FIN_WAIT2 状态 https://my.oschina.net/airship/blog/2875176
curl keep-alive https://serverfault.com/questions/199434/how-do-i-make-curl-use-keepalive-from-the-command-line
requests的keep-alive http://xiaorui.cc/2017/04/03/%E6%9E%84%E5%BB%BA%E9%AB%98%E6%95%88%E7%9A%84python-requests%E9%95%BF%E8%BF%9E%E6%8E%A5%E6%B1%A0/
rst包攻击 https://blog.csdn.net/russell_tao/article/details/7228923
TCP的ack机制 http://xstarcd.github.io/wiki/shell/TCP_ACK.html
为什么基于TCP的应用需要心跳包 http://hengyunabc.github.io/why-we-need-heartbeat/
https://www.cnblogs.com/hukey/p/5481173.html

Centos使用LVS+keepalive 搭建集群原理详解

负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群。常用的负载均衡开源软件有nginx、lvs、haproxy,商业的硬件负载均衡设备F5、Netscale。这里主要是学习Linux下 LVS 并对其进行了详细的总结记录。

一、负载均衡LVS基本介绍
LB集群的架构和原理很简单,就是当用户的请求过来时,会直接分发到Director Server上,然后它把用户的请求根据设置好的调度算法,智能均衡地分发到后端真正服务器(real server)上。为了避免不同机器上用户请求得到的数据不一样,需要用到了共享存储,这样保证所有用户请求的数据是一样的。

LVS是 Linux Virtual Server 的简称,也就是Linux虚拟服务器。这是一个由章文嵩博士发起的一个开源项目,它的官方网站是 http://www.linuxvirtualserver.org 现在 LVS 已经是 Linux 内核标准的一部分。使用 LVS 可以达到的技术目标是:通过 LVS 达到的负载均衡技术和 Linux 操作系统实现一个高性能高可用的 Linux 服务器集群,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的性能。LVS 是一个实现负载均衡集群的开源软件项目,LVS架构从逻辑上可分为调度层、Server集群层和共享存储。


二、LVS的基本工作原理

1. 当用户向负载均衡调度器(Director Server)发起请求,调度器将请求发往至内核空间
2. PREROUTING链首先会接收到用户请求,判断目标IP确定是本机IP,将数据包发往INPUT链
3. IPVS是工作在INPUT链上的,当用户请求到达INPUT时,IPVS会将用户请求和自己已定义好的集群服务进行比对,如果用户请求的就是定义的集群服务,那么此时IPVS会强行修改数据包里的目标IP地址及端口,并将新的数据包发往POSTROUTING链
4. POSTROUTING链接收数据包后发现目标IP地址刚好是自己的后端服务器,那么此时通过选路,将数据包最终发送给后端的服务器


三、LVS的组成
LVS 由2部分程序组成,包括 ipvs 和 ipvsadm。

1. ipvs(ip virtual server):一段代码工作在内核空间,叫ipvs,是真正生效实现调度的代码。
2. ipvsadm:另外一段是工作在用户空间,叫ipvsadm,负责为ipvs内核框架编写规则,定义谁是集群服务,而谁是后端真实的服务器(Real Server)


四、LVS相关术语
1. DS:Director Server。指的是前端负载均衡器节点。
2. RS:Real Server。后端真实的工作服务器。
3. VIP:向外部直接面向用户请求,作为用户请求的目标的IP地址。
4. DIP:Director Server IP,主要用于和内部主机通讯的IP地址。
5. RIP:Real Server IP,后端服务器的IP地址。
6. CIP:Client IP,访问客户端的IP地址。

下边是三种工作模式的原理和特点总结。


五、LVS/NAT原理和特点
1. 重点理解NAT方式的实现原理和数据包的改变。


(a). 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 
(b). PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
(c). IPVS比对数据包请求的服务是否为集群服务,若是,修改数据包的目标IP地址为后端服务器IP,然后将数据包发至POSTROUTING链。 此时报文的源IP为CIP,目标IP为RIP 
(d). POSTROUTING链通过选路,将数据包发送给Real Server
(e). Real Server比对发现目标为自己的IP,开始构建响应报文发回给Director Server。 此时报文的源IP为RIP,目标IP为CIP 
(f). Director Server在响应客户端前,此时会将源IP地址修改为自己的VIP地址,然后响应给客户端。 此时报文的源IP为VIP,目标IP为CIP

2. LVS-NAT模型的特性

RS应该使用私有地址,RS的网关必须指向DIP

DIP和RIP必须在同一个网段内

请求和响应报文都需要经过Director Server,高负载场景中,Director Server易成为性能瓶颈

支持端口映射

RS可以使用任意操作系统

缺陷:对Director Server压力会比较大,请求和响应都需经过director server


六、LVS/DR原理和特点
1. 重将请求报文的目标MAC地址设定为挑选出的RS的MAC地址


(a) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP
(b) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
(c) IPVS比对数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址修改为DIP的MAC地址,将目标MAC地址修改RIP的MAC地址,然后将数据包发至POSTROUTING链。 此时的源IP和目的IP均未修改,仅修改了源MAC地址为DIP的MAC地址,目标MAC地址为RIP的MAC地址 
(d) 由于DS和RS在同一个网络中,所以是通过二层来传输。POSTROUTING链检查目标MAC地址为RIP的MAC地址,那么此时数据包将会发至Real Server。
(e) RS发现请求报文的MAC地址是自己的MAC地址,就接收此报文。处理完成之后,将响应报文通过lo接口传送给eth0网卡然后向外发出。 此时的源IP地址为VIP,目标IP为CIP 
(f) 响应报文最终送达至客户端

2. LVS-DR模型的特性

特点1:保证前端路由将目标地址为VIP报文统统发给Director Server,而不是RS

RS可以使用私有地址;也可以是公网地址,如果使用公网地址,此时可以通过互联网对RIP进行直接访问

RS跟Director Server必须在同一个物理网络中

所有的请求报文经由Director Server,但响应报文必须不能进过Director Server

不支持地址转换,也不支持端口映射

RS可以是大多数常见的操作系统

RS的网关绝不允许指向DIP(因为我们不允许他经过director)

RS上的lo接口配置VIP的IP地址

缺陷:RS和DS必须在同一机房中

3. 特点1的解决方案:

在前端路由器做静态地址路由绑定,将对于VIP的地址仅路由到Director Server

存在问题:用户未必有路由操作权限,因为有可能是运营商提供的,所以这个方法未必实用

arptables:在arp的层次上实现在ARP解析时做防火墙规则,过滤RS响应ARP请求。这是由iptables提供的

修改RS上内核参数(arp_ignore和arp_announce)将RS上的VIP配置在lo接口的别名上,并限制其不能响应对VIP地址解析请求。


七、LVS/Tun原理和特点
在原有的IP报文外再次封装多一层IP首部,内部IP首部(源地址为CIP,目标IIP为VIP),外层IP首部(源地址为DIP,目标IP为RIP)


(a) 当用户请求到达Director Server,此时请求的数据报文会先到内核空间的PREROUTING链。 此时报文的源IP为CIP,目标IP为VIP 。
(b) PREROUTING检查发现数据包的目标IP是本机,将数据包送至INPUT链
(c) IPVS比对数据包请求的服务是否为集群服务,若是,在请求报文的首部再次封装一层IP报文,封装源IP为为DIP,目标IP为RIP。然后发至POSTROUTING链。 此时源IP为DIP,目标IP为RIP 
(d) POSTROUTING链根据最新封装的IP报文,将数据包发至RS(因为在外层封装多了一层IP首部,所以可以理解为此时通过隧道传输)。 此时源IP为DIP,目标IP为RIP
(e) RS接收到报文后发现是自己的IP地址,就将报文接收下来,拆除掉最外层的IP后,会发现里面还有一层IP首部,而且目标是自己的lo接口VIP,那么此时RS开始处理此请求,处理完成之后,通过lo接口送给eth0网卡,然后向外传递。 此时的源IP地址为VIP,目标IP为CIP
(f) 响应报文最终送达至客户端

LVS-Tun模型特性

RIP、VIP、DIP全是公网地址

RS的网关不会也不可能指向DIP

所有的请求报文经由Director Server,但响应报文必须不能进过Director Server

不支持端口映射

RS的系统必须支持隧道

其实企业中最常用的是 DR 实现方式,而 NAT 配置上比较简单和方便,后边实践中会总结 DR 和 NAT 具体使用配置过程。


八、LVS的八种调度算法
1. 轮叫调度 rr
这种算法是最简单的,就是按依次循环的方式将请求调度到不同的服务器上,该算法最大的特点就是简单。轮询算法假设所有的服务器处理请求的能力都是一样的,调度器会将所有的请求平均分配给每个真实服务器,不管后端 RS 配置和处理能力,非常均衡地分发下去。

2. 加权轮叫 wrr
这种算法比 rr 的算法多了一个权重的概念,可以给 RS 设置权重,权重越高,那么分发的请求数越多,权重的取值范围 0 – 100。主要是对rr算法的一种优化和补充, LVS 会考虑每台服务器的性能,并给每台服务器添加要给权值,如果服务器A的权值为1,服务器B的权值为2,则调度到服务器B的请求会是服务器A的2倍。权值越高的服务器,处理的请求越多。

3. 最少链接 lc
这个算法会根据后端 RS 的连接数来决定把请求分发给谁,比如 RS1 连接数比 RS2 连接数少,那么请求就优先发给 RS1 

4. 加权最少链接 wlc
这个算法比 lc 多了一个权重的概念。

5. 基于局部性的最少连接调度算法 lblc
这个算法是请求数据包的目标 IP 地址的一种调度算法,该算法先根据请求的目标 IP 地址寻找最近的该目标 IP 地址所有使用的服务器,如果这台服务器依然可用,并且有能力处理该请求,调度器会尽量选择相同的服务器,否则会继续选择其它可行的服务器

6. 复杂的基于局部性最少的连接算法 lblcr
记录的不是要给目标 IP 与一台服务器之间的连接记录,它会维护一个目标 IP 到一组服务器之间的映射关系,防止单点服务器负载过高。

7. 目标地址散列调度算法 dh
该算法是根据目标 IP 地址通过散列函数将目标 IP 与服务器建立映射关系,出现服务器不可用或负载过高的情况下,发往该目标 IP 的请求会固定发给该服务器。

8. 源地址散列调度算法 sh
与目标地址散列调度算法类似,但它是根据源地址散列算法进行静态分配固定的服务器资源。

 


转载自:http://www.thinkphp.cn/topic/51067.html

























































































































































以上是关于HTTP keepalive详解的主要内容,如果未能解决你的问题,请参考以下文章

keepalive配置文件详解

TCP的KeepAlive探测详解

keepalive配置文件详解

HTTP 与 TCP 的 KeepAlive 是一个东西吗?

傻傻分不清的TCP keepalive和HTTP keepalive

Centos使用LVS+keepalive 搭建集群原理详解