首页  /  Nginx 进阶  /  正文

水平扩展的性能优化

Nginx 进阶 2026-10-02📖 15 分钟👁 —
🚀 Nginx 水平扩展与优化第 2 / 8 页12345678📖 完整导航 →

一、水平扩展:性能优化

浏览器的 keepalive(简单了解)

keepalive 在响应头里通常以 Connection: keep-alive 或 Keep-Alive: timeout=xxx 的形式出现,即浏览器往往自带 keepalive 连接。

何时启用/关闭?用户不长时间停留在一个页面,开 keepalive(就是对建立的 tcp 连接复用);html 页面有子请求(控制台 F12 查看),用到时会自己加载,这种请求一般只抓一次,所以不开 keepalive。

在 nginx 中关闭 keepalive

参数 keepalive_timeout 65; 代表活跃时间为 65s,65s 内有活动就保持连接(长连接),反之关闭,部分特殊场景需要关闭 keepalive。

当值为 0,keepalive 为关闭状态。未关闭时查看 HTTP 请求包,connection 字段可能有 keep-alive 值,关闭时 value 变为 close。此时请求包可能还是 keep-alive,而响应时 close,这意味着浏览器发起请求想要保持连接~

数据包中看具体的情况(客户端)

数据包可以看到到底有没有建立长连接,抓包可以用 charles,charlesproxy.com。

为什么要用这个工具呢?目的就是查看是否有 keepalive。百度首页几十个 JS、CSS、图片,如果每个都新建 TCP 连接,页面加载就慢;用了 keepalive,同一个连接复用多次。这个结论只有抓包能看到,控制台看不到。

keepalive 在 nginx 的详细配置

官方写了,keepalive 有最大连接时间,一旦到了,强制切断连接。

以下内容可以在官方文档查看,完整的配置都在官方的文档中。

nginx 和客户端之间的配置:

客户端和 Server 之间 keepalive 相关的几个参数

对上游服务器的模块是 upstream,官方文档标题就是:

upstream 模块里的 keepalive、keepalive_requests、keepalive_time、keepalive_timeout

对客户端的模块:直接搜索 disable 找到 keepalive 相关。

客户端发请求、服务端算完再返回,两次写操作之间就是 send_timeout

keepalive_timeout 是保持连接的时间,这个是返回数据的时间,这个不能比前者短,假设连接时间是 65s,send 数值是 60s,可能没传完会断开,因为有的复杂计算操作等待时间会长一些。

超过设定时间不活动会失效,这个参数可以配置两个。

配置一个 HTTP 响应包只有一个 keepalive 的值,如果配置俩就是下图:

响应头里的 Keep-Alive 是 timeout=65
对谁配在哪
浏览器 到 Nginxserver 或 location 块
Nginx 到 上游upstream 块 + location 块

上面部分是浏览器和 nginx 之间的,下面了解 nginx 到上游服务器的配置选项。

直接写入 /etc/nginx/nginx.conf 的 http 块下,全局生效。

http 块里 keepalive_timeout、keepalive_time、send_timeout 的写法

nginx 和上游服务器之间的配置

在 upstream 块配置

keepalive_time 这个指令在 Nginx 1.19.10 才加入,Nginx 版本可能是更早的,不支持这个参数,直接删了就行。

在 location 块

用户所带的信息经过 nginx 的转发,会清除掉,然后再把请求转发到后端,后端不知道用户的 IP 等信息(后端不和用户交互)。

比如浏览器和 nginx 是长连接的,数据包有 connection 的 keepalive 请求头。

proxy_set_header Connection ""; 就是把字段 Connection 的值改为空,value 要加引号。

清空 Connection 头(设为空字符串)意味着不传 Connection 头给后端,后端按 HTTP 1.1 默认行为处理(长连接)。如果写成 Connection: close,后端收到后会主动断开,长连接就废了。所以这行的目的是清除浏览器带来的旧 Connection 头,让 Nginx 和后端之间按 HTTP 1.1 默认走长连接。

补充内容:Nginx 内置了 keepalive,不需要装任何第三方模块,基础篇提到的是 keepalived,与 keepalive 不同。

upstream httpget {
    server 172.16.0.9;
    server 10.1.4.16;
    keepalive 16;
}

server {
    listen 80;
    server_name 168kaguyachi.site;

    root /var/www/html;
    index index.html;

    #向客户端
    keepalive_requests 500;
    send_timeout 70s;
    keepalive_timeout 65 65;


    location / {
    proxy_pass http://httpget;

    add_header Content-Type "text/html; charset=utf-8";
    #向上游
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    }

}
# 需要注意没有高亮的参数,可能nginx版本不支持

这里再配置的时候遇到了很多困难的问题,放到了本内容的子目录笔记下。

压力测试和调优方式

apt install apache2-utils -y 安装工具。

yum install httpd-tools 是 centos 的。

参数:

ab 的参数说明,-n、-c、-t、-b 这些

参数很多但是一般只用 -c 和 -n。

在 nginx 的机器上安装,进行压测。

ab -n 10000 -c 30 http://172.16.0.9/ 对该地址发起 1w 个请求并发 30。

一般是在网关机器安装,对上游服务器测试(结尾要有 /)。

关键数据如下:

指标位置含义
QPSRequests per second每秒处理多少请求,越高越好
失败数Failed requests必须为 0,不为 0 说明扛不住
平均响应时间Time per request 第一行用户平均等多久,越低越好
99% 响应时间Percentage 倒数第二行99% 的请求在多少 ms 内完成

用 1w 个请求测试:

ab -n 10000 -c 30 http://172.16.0.9/
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 172.16.0.9 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests # 总共发送的请求


Server Software:        SimpleHTTP/0.6
Server Hostname:        172.16.0.9
Server Port:            80

Document Path:          /
Document Length:        8 bytes

Concurrency Level:      30
Time taken for tests:   4.205 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1920000 bytes
HTML transferred:       80000 bytes
Requests per second:    2378.23 [#/sec] (mean) # QPS和请求复杂度有关
Time per request:       12.614 [ms] (mean)
Time per request:       0.420 [ms] (mean, across all concurrent requests)
Transfer rate:          445.92 [Kbytes/sec] received # 上面是吞吐量(可能未压满)

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        2    7  67.4      2    1038
Processing:     2    5  22.6      4     840
Waiting:        2    5  22.6      3     840
Total:          5   12  74.5      6    1434

Percentage of the requests served within a certain time (ms)
  50%      6
  66%      6
  75%      7
  80%      7
  90%      7
  95%      7
  98%      8
  99%      9
 100%   1434 (longest request)

不能单看个别数据,和综合的因素有关。

再用 10w 测试一次:

ab -n 100000 -c 30 http://172.16.0.9/
Benchmarking 172.16.0.9 (be patient)
Completed 10000 requests

Completed 100000 requests
Finished 100000 requests

Document Path:          /
Document Length:        8 bytes

Concurrency Level:      30
Time taken for tests:   41.168 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      19200000 bytes
HTML transferred:       800000 bytes
Requests per second:    2429.08 [#/sec] (mean)
Time per request:       12.350 [ms] (mean)
Time per request:       0.412 [ms] (mean, across all concurrent requests)
Transfer rate:          455.45 [Kbytes/sec] received

可以复制两次到文档对比。

控制变量:可以把 nginx 的 keepalive 的参数注释掉或者修改,再次测试来看。

测试出来的肯定不准确,但是可以参考。

测试网关:

ab -n 100000 -c 30 http://127.0.0.1/
Benchmarking 127.0.0.1 (be patient)
Completed 10000 requests

Finished 100000 requests


Server Software:        nginx/1.18.0
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        7682 bytes

Concurrency Level:      30
Time taken for tests:   9.652 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      792600000 bytes
HTML transferred:       768200000 bytes
Requests per second:    10360.17 [#/sec] (mean)
Time per request:       2.896 [ms] (mean)
Time per request:       0.097 [ms] (mean, across all concurrent requests)
Transfer rate:          80190.13 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.3      1       7
Processing:     0    2   0.5      2      10
Waiting:        0    1   0.4      1       9
Total:          1    3   0.4      3      11

经过 Nginx 转发后 QPS 反而涨到 10360,直连后端只有 2429。不是 Nginx 加速了后端,而是你压测 127.0.0.1:80 时打到了网关的另一个站点(Document Length: 7682 bytes 是另一个页面的 HTML),这个站点是纯静态文件,没走 proxy_pass,Nginx 直接从磁盘读文件返回,性能当然暴增。

重新压测,指定 Host 头让它匹配 168kaguyachi.site,走负载均衡转发到后端。

ab -n 10000 -c 30 -H "Host: 168kaguyachi.site" http://127.0.0.1/
ab -n 10000 -c 30 -H "Host: 168kaguyachi.site" http://127.0.0.1/
Document Path:          /
Document Length:        10 bytes

Concurrency Level:      30
Time taken for tests:   3.532 seconds
Complete requests:      10000
Failed requests:        5000 # 失败请求
   (Connect: 0, Receive: 0, Length: 5000, Exceptions: 0)
Total transferred:      2455000 bytes
HTML transferred:       90000 bytes
Requests per second:    2831.09 [#/sec] (mean) # QPS
Time per request:       10.597 [ms] (mean) # 用户平均多久
Time per request:       0.353 [ms] (mean, across all concurrent requests)
Transfer rate:          678.74 [Kbytes/sec] received

Percentage of the requests served within a certain time (ms)

  99%      9 # 99%的响应完成时间
 100%   3029 (longest request)

经过 Nginx 反向代理后,QPS 反而涨了 16%,响应时间降了 14%。

keepalive 16 和 proxy_http_version 1.1 生效了,Nginx 和后端之间复用了长连接,减少了握手开销,反向代理反而比直连更快。

反向代理的优点就是保护好反向代理服务器就能保护好内网。

Tomcat 直连和走 nginx 的压测对比

不会 tomcat,仅用视频数据学习。

测试 tomcat 的项目在压测前先访问进行初始化,更准确。

在 tomcat 对自身压测:

直连 tomcat 压测,QPS 3222,吞吐量 25310

在 linux 安装的 tomcat 默认以 apr 形式运行网络接口(本机 c 语言形式),所以性能很高。

win 安装的是 bio,更差一些。

在 tomcat 对网关压测:

走 nginx 网关压测,QPS 4277,吞吐量 33688

正常情况 10w 算少的,一般都测试 5min 以上,压测一下很可能看不出问题。

区别在于直接压 tomcat 没有连接复用(keepalive),压 nginx 会高因为本身 nginx 就好。

为什么反向代理比直连更好?直接看图:

上面 ab 直压 tomcat 是 3200,下面经过 nginx 是 7000

验证:把 keepalive 都注释掉,直连 nginx:

注释掉 keepalive 之后,QPS 掉到 2957

关掉不加 keepalive,走 nginx 比直走 tomcat 吞吐量掉了近 1w,其余的也掉了很多,所以 keepalive 的性能优化很强,nginx 也很强。

加了 keepalive 不等于并发量变高,因为测试环境的客户端不支持 keepalive,真实的客户端都是浏览器默认支持的 keepalive,直连和加 nginx 要自己决定做减法(何时在 tomcat 前置 nginx 性能有明显提升)。

还有一个原因是 tomcat 本身的性能不如 nginx,用 nginx 代理更好。

总之,实际环境中要根据需求调整架构。