写点什么

深入浅出学习透析 Nginx 服务器的基本原理和配置指南「Keepalive 性能分析实战篇」

作者:洛神灬殇
  • 2022-11-27
    江苏
  • 本文字数:4883 字

    阅读完需:约 16 分钟

深入浅出学习透析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,不是用来设置超时值


Syntax:    keepalive connections;Default:    —Context:    upstreamActivates the cache for connections to upstream servers.
复制代码


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 个连接在反复的回收和再分配的过程中,必然出现两种非常矛盾场景在短时间内反复:


  1. 连接不够用,造成新建连接

  2. 连接空闲,造成关闭连接。从而使得总连接数出现反复震荡,不断的创建新连接和关闭连接,使得长连接的效果被大大削弱。

Keepalive 参数建议

造成连接数量反复震荡的一个推手,就是这个 keepalive 这个最大空闲连接数。毕竟连接池中的 1000 个连接在频繁利用时,出现短时间内多余 10 个空闲连接的概率实在太高。因此为了避免出现上面的连接震荡,必须考虑加大这个参数,比如上面的场景如果将 keepalive 设置为 100 或者 200,就可以非常有效的缓冲请求和应答不均匀

keepalive 参数分析

  • 对应的参数设置为每个 worker 子进程在缓冲中保持的到 upstream 服务器的空闲 keepalive 连接的最大数量。当超过这个数量值的时候,会将最近最少使用(LRU)的连接进行关闭。需要考虑的是 keepalive 指令不会限制一个 worker 进程到 upstream 服务器连接的总数量。其参数应该设置为一个足够小的数字来让 upstream 服务器来处理新进来的连接。

  • 如果想让 upstream 每次都处理新的进来的连接,就应该将这个值放的足够小。反过来理解,就是如果不想让 upstream 服务器处理新连接,就应该放大一些?

Keepalive 的使用案例

Keepalive 对接 memcached 服务

使用 keepalive 连接的 memcached upstream 配置的例子:


upstream memcached_backend {    server 127.0.0.1:11211;    server 10.0.0.2:11211;    keepalive 32;}server {    ...    location /memcached/ {        set $memcached_key $uri;        memcached_pass memcached_backend;    }}
复制代码
Keepalive 对接 Http1.1 的 Web 服务

对于 HTTP,proxy_http_version 指定应该设置为”1.1”,而”Connection” header 应该被清理:


upstream http_backend {    server 127.0.0.1:8080;    keepalive 16;}server {    ...    location /http/ {        proxy_pass http://http_backend;        proxy_http_version 1.1;        proxy_set_header Connection "";        ...    }}
复制代码
Keepalive 对接 Http1.0 的 Web 服务

HTTP/1.0 持久连接可以通过传递”Connection: Keep-Alive” header 到 upstream server, 但是不推荐使用这种方法。

Keepalive 对接 FastCGI 的 Web 服务

对于 FastCGI 服务器,要求设置 fastcgi_keep_conn 来让长连接工作:


upstream fastcgi_backend {    server 127.0.0.1:9000;    keepalive 8;}server {    ...    location /fastcgi/ {        fastcgi_pass fastcgi_backend;        fastcgi_keep_conn on;        ...    }}
复制代码


当使用默认的 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):


http {    keepalive_timeout  120s 120s;    keepalive_requests 10000;}
复制代码

keepalive_timeout 指令

keepalive_timeout 指令的语法:


Syntax:    keepalive_timeout timeout [header_timeout];Default:    keepalive_timeout 75s;Context:    http, server, location
复制代码


  1. 第一个参数设置 keep-alive 客户端连接在服务器端保持开启的超时值。值为 0 会禁用 keep-alive 客户端连接。

  2. 可选的第二个参数在响应的 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)之间保持长连接,典型设置如下:


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;        }    }}
复制代码



总结


  • keepalive 这个参数一定要小心设置,尤其对于 QPS 比较高的场景,推荐先做一下估算,根据 QPS 和平均响应时间大体能计算出需要的长连接的数量。比如前面 10000 QPS 和 100 毫秒响应时间就可以推算出需要的长连接数量大概是 1000. 然后将 keepalive 设置为这个长连接数量的 10%到 30%。

  • 如果不喜欢计算的同学也可以直接设置为 keepalive=1000 之类的,一般都 OK 的了。

用户头像

洛神灬殇

关注

🏆InfoQ写作平台-签约作者🏆 2020-03-25 加入

【个人简介】酷爱计算机科学、醉心编程技术、喜爱健身运动、热衷悬疑推理的“极客达人” 【技术格言】任何足够先进的技术都与魔法无异 【技术范畴】Java领域、Spring生态、MySQL专项、微服务/分布式体系和算法设计等

评论

发布
暂无评论
深入浅出学习透析Nginx服务器的基本原理和配置指南「Keepalive性能分析实战篇」_nginx_洛神灬殇_InfoQ写作社区