深入浅出学习透析 Nginx 服务器的基本原理和配置指南「Keepalive 性能分析实战篇」
Linux 系统:Centos 7 x64
Nginx 版本:1.11.5
Nginx 是一款面向性能设计的 HTTP 服务器,能反向代理 HTTP,HTTPS 和邮件相关(SMTP,POP3,IMAP)的协议链接。并且提供了负载均衡以及 HTTP 缓存。它的设计充分使用异步事件模型,削减上下文调度的开销,提高服务器并发能力。采用了模块化设计,提供了丰富模块的第三方模块。所以关于 Nginx,有这些标签:「异步」「事件」「模块化」「高性能」「高并发」「反向代理」「负载均衡」「长连接」。本章内容主要就是针对于长连接请求模块。
为什么要单独讲解 keepalive 指令?
upstream 设置中,有个参数要特别的小心,就是这个 keepalive。
大多数未仔细研读过 nginx 的同学通常都会误解这个参数,有些人理解为这里的 keepalive 是设置是否打开长连接,以为应该设置为 on/off。有些人会被前面的 keepalive_timeout 误导,以为这里也是设置 keepalive 的 timeout。
但是实际上这个 keepalive 参数的含义非常的奇特,请小心看 nginx 文档中的说明,因为 Keepalive 长连接非常重要而且容易理解错误,所以专门做连一期专门讲解 keepalive 的文章。
本文介绍如何使用 Nginx 进行配置和实现长连接 Keepalive,并介绍如何设置 nginx 以提供静态内容服务,如何配置 nginx 作为代理服务器。
keepalive 指令
支持 keepalive 长连接,当使用 nginx 作为反向代理时,为了支持长连接,需要做到两点:
从 client 到 nginx 的连接是长连接
从 nginx 到 server 的连接是长连接
从 HTTP 协议的角度看,Nginx 在这个过程中,对于客户端它扮演着 HTTP 服务器端的角色。而对于真正的服务器端(在 nginx 的术语中称为 upstream)Nginx 又扮演着 HTTP 客户端的角色,keepalive 指令出现在版本 1.1.4。
keepalive 指令格式
keepalive 不是 on/off 之类的开关
keepalive 不是 timeout,不是用来设置超时值
connections 的取值代表着连接到 upstream 服务器的持续连接(即长连接)的数量。很多人都会有一个误解:认为这个参数是设置到 upstream 服务器的长连接的数量,分歧在于是最大连接数还是最小连接数。
官方文档的介绍
The connections parameter sets the maximum number of idle keepalive connections to upstream servers
connections 参数设置到 upstream 服务器的空闲 keepalive 连接的最大数量,这个”idle”的概念,何为 idle。大多数人之所以误解为是到 upstream 服务器的最大长连接数,一般都是因为看到了文档中的这句话,而漏看了这个”idle”一词。
When this number is exceeded, the least recently used connections are closed.
当这个数量被突破时,最近使用最少的连接将被关闭。
Nginx 的官方文档给出了指示,否定了最大连接数的可能:keepalive 指令不会限制一个 nginx worker 进程到 upstream 服务器连接的总数量。请注意空闲 keepalive 连接的最大数量中空闲这个关键字。
keepalive 实际场景分析
先假设一个场景: 有一个 HTTP 服务,作为 upstream 服务器接收请求,响应时间为 100 毫秒。如果要达到 10000 QPS 的性能,就需要在 nginx 和 upstream 服务器之间建立大约 1000 条 HTTP 连接。nginx 为此建立连接池,然后请求过来时为每个请求分配一个连接,请求结束时回收连接放入连接池中,连接的状态也就更改为 idle。
之后假设这个 upstream 服务器的 keepalive 参数设置比较小,比如常见的 10.
再次假设请求和响应是均匀而平稳的,那么这 1000 条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的 idle 线程会非常的少,趋进于零。我们以 10 毫秒为一个单位,来看连接的情况(注意场景是 1000 个线程+100 毫秒响应时间,每秒有 10000 个请求完成):
每 10 毫秒有 100 个新请求,需要 100 个连接
每 10 毫秒有 100 个请求结束,可以释放 100 个连接
如果请求和应答都均匀,则 10 毫秒内释放的连接刚好够用,不需要新建连接,连接池空闲连接为零
如果请求通常不是足够的均匀和平稳,为了简化问题,我们假设应答始终都是平稳的,只是请求不平稳,第一个 10 毫秒只有 50,第二个 10 毫秒有 150:
下一个 10 毫秒,有 100 个连接结束请求回收连接到连接池,但是假设此时请求不均匀 10 毫秒内没有预计的 100 个请求进来,而是只有 50 个请求。注意此时连接池回收了 100 个连接又分配出去 50 个连接,因此连接池内有 50 个空闲连接。
最后注意看 keepalive=10 的设置,这意味着连接池中最多容许保留有 10 个空闲连接。因此 nginx 不得不将这 50 个空闲连接中的 40 个关闭,只留下 10 个。
再下一个 10 个毫秒,有 150 个请求进来,有 100 个请求结束任务释放连接。150 - 100 = 50,空缺了 50 个连接,减掉前面连接池保留的 10 个空闲连接,nginx 不得不新建 40 个新连接来满足要求。
可以看到,在短短的 20 毫秒内,仅仅因为请求不够均匀,就导致 nginx 在前 10 毫秒判断空闲连接过多关闭了 40 个连接,而后 10 毫秒又不得不新建 40 个连接来弥补连接的不足。
再来一次类似的场景,假设请求是均匀的,而应答不再均匀,前 10 毫秒只有 50 个请求结束,后 10 毫秒有 150 个:
前 10 毫秒,进来 100 个请求,结束 50 个请求,导致连接不够用,nginx 为此新建 50 个连接
后 10 毫秒,进来 100 个请求,结束 150 个请求,导致空闲连接过多,ngixn 为此关闭了 150-100-10=40 个空闲连接
第二个应答不均匀的场景实际上是对应第一个请求不均匀的场景:正是因为请求不均匀,所以导致 100 毫秒之后这些请求的应答必然不均匀。
现实世界中的请求往往和理想状态有巨大差异,请求不均匀,服务器处理请求的时间也不平稳,这理论上的大概 1000 个连接在反复的回收和再分配的过程中,必然出现两种非常矛盾场景在短时间内反复:
连接不够用,造成新建连接
连接空闲,造成关闭连接。从而使得总连接数出现反复震荡,不断的创建新连接和关闭连接,使得长连接的效果被大大削弱。
Keepalive 参数建议
造成连接数量反复震荡的一个推手,就是这个 keepalive 这个最大空闲连接数。毕竟连接池中的 1000 个连接在频繁利用时,出现短时间内多余 10 个空闲连接的概率实在太高。因此为了避免出现上面的连接震荡,必须考虑加大这个参数,比如上面的场景如果将 keepalive 设置为 100 或者 200,就可以非常有效的缓冲请求和应答不均匀。
keepalive 参数分析
对应的参数设置为每个 worker 子进程在缓冲中保持的到 upstream 服务器的空闲 keepalive 连接的最大数量。当超过这个数量值的时候,会将最近最少使用(LRU)的连接进行关闭。需要考虑的是 keepalive 指令不会限制一个 worker 进程到 upstream 服务器连接的总数量。其参数应该设置为一个足够小的数字来让 upstream 服务器来处理新进来的连接。
如果想让 upstream 每次都处理新的进来的连接,就应该将这个值放的足够小。反过来理解,就是如果不想让 upstream 服务器处理新连接,就应该放大一些?
Keepalive 的使用案例
Keepalive 对接 memcached 服务
使用 keepalive 连接的 memcached upstream 配置的例子:
Keepalive 对接 Http1.1 的 Web 服务
对于 HTTP,proxy_http_version 指定应该设置为”1.1”,而”Connection” header 应该被清理:
Keepalive 对接 Http1.0 的 Web 服务
HTTP/1.0 持久连接可以通过传递”Connection: Keep-Alive” header 到 upstream server, 但是不推荐使用这种方法。
Keepalive 对接 FastCGI 的 Web 服务
对于 FastCGI 服务器,要求设置 fastcgi_keep_conn 来让长连接工作:
当使用默认的 round-robin 之外的负载均衡算法时,必须在 keepalive 指令之前激活他们。SCGI 和 uwsgi 协议没有 keepalive 连接的概念。
保持和 client 的长连接
为了在 client 和 nginx 之间保持上连接,有两个要求:
client 发送的 HTTP 请求要求 keep alive
nginx 设置上支持 keep alive
HTTP 配置
默认情况下,Nginx 已经自动开启了对 client 连接的 keep alive 支持。一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调整个别参数。需要修改 nginx 的配置文件(在 nginx 安装目录下的 conf/nginx.conf):
keepalive_timeout 指令
keepalive_timeout 指令的语法:
第一个参数设置 keep-alive 客户端连接在服务器端保持开启的超时值。值为 0 会禁用 keep-alive 客户端连接。
可选的第二个参数在响应的 header 域中设置一个值“Keep-Alive: timeout=time”。这两个参数可以不一样。
注:默认 75s 一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为 120s 或者 300s。第二个参数通常可以不用设置。
keepalive_requests 指令
keepalive_requests 指令用于设置一个 keep-alive 连接上可以服务的请求的最大数量。当最大请求数量达到时,连接被关闭。默认是 100。
keepalive_requests 指令的实现原理
指一个 keepalive 请求建立之后,Nginx 就会为这个连接设置一个计数器,记录这个 keep alive 的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则 Nginx 会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
这个参数往往被大多数人忽略,因为大多数情况下当 QPS(每秒请求数)不是很高时,默认值 100 凑合够用。但是,对于一些 QPS 比较高(比如超过 10000QPS,甚至达到 30000,50000 甚至更高) 的场景,默认的 100 就显得太低。
简单计算一下,QPS=10000 时,客户端每秒发送 10000 个请求(通常建立有多个长连接),每个连接只能最多跑 100 次请求,意味着平均每秒钟就会有 100 个长连接因此被 nginx 关闭。
为了保持 QPS,客户端不得不每秒中重新新建 100 个连接。因此,如果用 netstat 命令看客户端机器,就会发现有大量的 TIME_WAIT 的 socket 连接(即使此时 keep alive 已经在 client 和 nginx 之间生效)。
因此对于 QPS 较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少 TIME_WAIT。
保持和 server 的长连接
为了让 Nginx 和 server(Nginx 称为 upstream)之间保持长连接,典型设置如下:
总结
keepalive 这个参数一定要小心设置,尤其对于 QPS 比较高的场景,推荐先做一下估算,根据 QPS 和平均响应时间大体能计算出需要的长连接的数量。比如前面 10000 QPS 和 100 毫秒响应时间就可以推算出需要的长连接数量大概是 1000. 然后将 keepalive 设置为这个长连接数量的 10%到 30%。
如果不喜欢计算的同学也可以直接设置为 keepalive=1000 之类的,一般都 OK 的了。
评论