本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net
文章目录
长连接基础:先了解为什么要保持长连接,http 的长连接历史。
长连接实战:接着如何分析如何通过 nginx 设置长连接。如果不恰当的设置长连接,会出现什么问题。
一. http 的长连接历史
HTTP 是属于应用层(七层)的协议,传输层(四层)使用的是 TCP 协议,所以 HTTP 的长连接和短连接,其本质就是 TCP 的长连接和短连接;
HTTP 是一个无状态的面向连接的协议,指的是协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态,但无状态不代表 HTTP 不能保持 TCP 连接;
TCP 建立连接和断开连接是需要三握四挥的,也就说 HTTP 连接的建立与断开是消耗资源的。
接下来正式了解下 http 长短连接模型
1. HTTP 短连接模型
短连接的基本逻辑与特点
HTTP 短连接模型是最早期的模型,也是 HTTP/1.0 的默认模型。在早期,HTTP 使用一个简单的模型来处理这样的连接。这些连接的生命周期是短暂的:每发起一个请求时都先有一次 TCP 握手,然后创建一个新连接,并在收到应答时立即关闭。
持续的短连接是耗时、耗费资源的
实际上,TCP 协议握手本身就是耗费时间的,所以 TCP 可以保持更多的热连接来适应负载。短连接破坏了 TCP 具备的能力,新的冷连接降低了其性能。
长短连接的设置
在 HTTP/1.0 中如果没有指定
Connection协议头,或者是值被设置为close就会启用短连接模型,要在 HTTP/1.0 中启用长连接模型,需要在协议头中指定Connection: Keep-Alive,不过并不建议这样操作。而在 HTTP/1.1 中,默认使用长连接模型,只有当
Connection被设置为close时才会用到这个短连接模型,协议头都不用再去声明它 (但是一般还是会把它加上,以防万一因为某种原因要退回到 HTTP/1.0)。
2. HTTP 长连接模型
长连接的趋势
后来,网页需要请求的资源越来越多,短连接模型显然已经十分吃力了。因为短连接有两个比较大的问题:创建新连接耗费的时间尤为明显(三次握手很耗费时间),另外 TCP 连接的性能只有在该连接被使用一段时间后 (热连接) 才能得到改善。因此在 HTTP/1.1 中引入了长连接模型。
在 HTTP/1.1 之前,长连接也被称为 keep-alive 连接。
一个长连接会保持一段时间,重复用于发送一系列请求,节省了新建 TCP 连接握手的时间,还可以利用 TCP 的性能增强能力(ing)。当然这个连接也不会一直保留着:连接在空闲一段时间后会被关闭 (服务器可以使用 Keep-Alive 协议头来指定一个最小的连接保持时间)。
长连接的缺点:资源占用问题
使用长连接本质上是因为当不断地三次握手建立连接所消耗的资源大于维持连接所需要的资源。
当处于空闲状态时,它还是会消耗服务器资源。如果空闲较长,可以使用非长连接,即尽快关闭那些空闲的连接,也能对性能有所提升。
二. nginx 作为代理时实现 HTTP 长连接
当我们配置 Nginx 作为代理服务器的时候,想要支持 HTTP 长连接,需要 client 到 Nginx 和 Nginx 到 server(nginx 中 upstream 配置的 server)都是长连接,因为此时 Nginx 既是 client 的 server 也是 server 的 client。
多个 HTTP 请求通过复用 TCP 连接,会达到以下效果:
- 减少握手次数
- 通过减少并发连接数减少了服务器资源的消耗
- 降低 TCP 拥塞控制的影响
1. nginx 与 client 的长连接
为了在 client 和 nginx 之间保持长连接,有两个要求:
- client 发送的 HTTP 请求要求 keep alive
2.nginx 设置上支持 keep alive:默认情况下,nginx 已经自动开启了对 client 连接的 keep alive 支持。
1.1. keepalive_timeout 指令
语法:用户一个 HTTP 请求连接完成以后,最多经过 timeout 时间,如果还是没有新的请求,就会关闭连接
Syntax: keepalive_timeout timeout [header_timeout];
Default: keepalive_timeout 75s;
Context: http, server, location
1. 第一个参数设置keep-alive客户端连接在服务器端保持开启的超时值。
值为0会禁用keep-alive客户端连接。
默认75s一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,
适当加大为120s或者300s。
2. (可选的)第二个参数在响应的header域中设置一个值“Keep-Alive: timeout=time”。
第二个参数通常可以不用设置。
1.2. keepalive_requests 指令
使用语法:
Syntax: keepalive_requests number;
Default: keepalive_requests 1000;
Context: http, server, location;
该指令首次出现在版本0.8.0中。在版本1.19.10之前,默认值为100。
1.参数的作用
>一个TCP(keep-alive)连接上最多执行多少个HTTP请求。
>当达到这个参数设置的最大值(默认是1000)时,则nginx会强行关闭这个长连接,
>逼迫客户端不得不重新建立新的长连接。
注意:
定期关闭连接是为了释放每个连接的内存分配。
因此,设置过高的最大请求数可能导致过多的内存使用,这是不推荐的。
* 场景分析
这个参数往往被大多数人忽略,因为大多数情况下当 QPS(每秒请求数) 不是很高时,默认值 100 凑合够用。但是,对于一些 QPS 比较高(比如超过 10 万 QPS,甚至达到 30 万, 50 万甚至更高) 的场景,默认的 1000 就不适用了。因为此时会频繁创建长连接。
出现频繁的关闭、创建连接:
当 QPS=10 万 / s 时,客户端每秒发送 10 万个请求 (通常建立有多个长连接),每个连接只能最多跑 1000 次请求,意味着平均每秒钟就会有 100 个长连接因此被 nginx 关闭。同样意味着为了保持 QPS,客户端不得不每秒中重新新建 100 个连接。
大量的 TIME_WAIT
如果用 netstat 命令看客户端机器,就会发现有大量的 TIME_WAIT 的 socket 连接 (即使此时 keep alive 已经在 client 和 nginx 之间生效)。因此对于 QPS 较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少 TIME_WAIT。
2. 保持和 server 的长连接
2.1. location 设置
为了让 nginx 和 server(upstream 块中的 servers)之间保持长连接,典型设置如下:
http {
upstream BACKEND {
server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300; // 这个很重要!
}
server {
listen 8080 default_server;
server_name "";
location / {
proxy_pass http://BACKEND;
proxy_set_header Host $Host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "";
client_max_body_size 3072k;
client_body_buffer_size 128k;
}
}
}
location 中的两个参数:
proxy_http_version 1.1;
proxy_set_header Connection "";
- HTTP 协议中对长连接的支持是从 1.1 版本之后才有的,因此最好通 proxy_http_version 指令设置为 "1.1";
- 代表来自 client 的请求 Connection header 会被清理。清理 Connection 头可以确保代理层完全控制连接的生命周期。
* 场景分析
假设 client 和 nginx 之间是短连接,nginx 和 upstream 之间可以开启长连接。这种情况下必须清理来自 client 请求中的 Connection header,如果不清理,则会将 client 的 header 传递过来导致不能开启长连接。
2.2. upstream 设置
upstream 设置中,有个参数要特别的小心,就是这个 keepalive。
nginx keepalive 语法:
Syntax: keepalive connections;
Default: —
Context: upstream
keepalive 参数作用
connections 参数设置每个 worker 进程与 upstream server 建立的最多空闲 的 keepalive 连接数量。当这个数量被突破时,最近使用最少的连接将被关闭。
特别提醒:keepalive 指令不会限制一个 nginx worker 进程到 upstream 服务器连接的总数量。connections 参数应该设置为一个足够小的数字来让 upstream 服务器来处理新进来的连接。
* 场景分析
场景描述
有一个 HTTP 服务,作为 upstream server 接收请求,响应时间为 100 毫秒(一秒能处理 10 个请求)。如果要达到 10000 QPS 的性能,就需要在 nginx 和 upstream 服务器之间建立大约 1000 条 HTTP 连接。
nginx 为此建立连接池,然后请求过来时为每个请求分配一个连接,请求结束时回收连接放入连接池中,连接的状态也就更改为 idle。
我们再假设这个 upstream 服务器的 keepalive 参数设置比较小,比如常见的 10.
场景 1
假设请求和响应是均匀而平稳的,那么这 1000 条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的 idle 线程会非常的少,趋进于零。
我们以 100 毫秒为一个单位,来看连接的情况:
- 每 100 毫秒有 100 个新请求,需要 100 个连接;根据前面假设可以知道每 100 毫秒有 100 个请求结束,可以释放 100 个连接
- 如果请求和应答都均匀,则 100 毫秒内释放的连接刚好够用,不需要新建连接,连接池也不空闲。
场景 2
回到现实世界,请求通常不是足够的均匀和平稳,为了简化问题,我们假设应答始终都是平稳的,只是请求不平稳:
- 假设此时 100 毫秒内只有 50 个请求。此时连接池内有 50 个空闲连接。注意看 keepalive=10 的设置,这意味着连接池中最多容许保留有 10 个空闲连接。因此 nginx 不得不将这 50 个空闲连接中的 40 个关闭,只留下 10 个。
- 再下一个 100 个毫秒,有 150 个请求进来,有 100 个请求结束任务释放连接。150 - 100 = 50, 空缺了 50 个连接,减掉前面连接池保留的 10 个空闲连接,nginx 不得不新建 40 个新连接来满足要求。
我们可以看到,在短短的 200 毫秒内,仅仅因为请求不够均匀,就导致 nginx 在前 100 毫秒判断空闲连接过多关闭了 40 个连接,而后 10 毫秒又不得不新建 40 个连接来弥补连接的不足。
场景 3
假设请求是均匀的,而应答不再均匀,前 100 毫秒只有 50 个请求结束,后 100 毫秒有 150 个:
- 前 100 毫秒,进来 100 个请求,只结束了 50 个请求,导致此时连接不够下次请求用,nginx 为此新建 50 个连接,此时有 150 个连接;
- 后 100 毫秒,进来 100 个请求,结束 150 个请求,导致空闲连接过多,ngixn 为此关闭了 150-10=140 个空闲连接。
小结
现实世界中请求往往不均匀,服务器处理请求的时间也不平稳,当 qps 很高时,就会在短时间内导致两种非常矛盾现象: 1. 连接不够用,造成新建连接;2. 连接空闲,造成关闭连接。从而使得总连接数出现反复震荡,不断的创建新连接和关闭连接,使得长连接的效果被大大削弱。
keepalive 参数设置方法:比如前面 10000 QPS 和 100 毫秒响应时间就可以推算出需要的长连接数量大概是 1000. 然后将 keepalive 设置为这个长连接数量的 10% 到 30%。