Nginx使用及性能优化

猎隼丶止戈 猎隼丶止戈 | 598 | 2023-02-21

介绍

Nginx​ 是一个集静态资源、负载均衡、反向代理于一身的 Web 服务器,Nginx​ 是目前负载均衡技术中的主流方案,几乎绝大部分项目都会使用它,Nginx​ 是一个轻量级的高性能 HTTP​ 反向代理服务器,同时它也是一个通用类型的代理服务器,支持绝大部分协议,如 TCP、UDP、SMTP、HTTPS​ 等。

Nginx​ 与 Redis 相同,都是基于多路复用模型构建出的产物,因此它与 Redis​ 同样具备 「资源占用少、并发支持高」 的特点,在理论上单节点的 Nginx​ 同时支持 5W​ 并发连接,而实际生产环境中,硬件基础到位再结合简单调优后确实能达到该数值。

负载均衡

02811a9cd4b54357af2aa95d07d537ec.png

代理

正向代理

当客户端访问一台服务器有障碍,访问不到的时候,这时候就可以找一台可以访问到该服务器的另外一台服务器去代替他去访问,这台代替他去访问的服务器称之为代理服务器。然后客户端就可以把请求发送给代理服务器,然后通过代理服务器去访问目标服务器。由代理服务器将目标服务器的返回数据返回给客户端,客户端可以清楚目标服务器的地址,但是目标服务器并不清楚来自哪个客户端,他只知道来自哪个代理服务器。所以,正向代理可以屏蔽或者说隐藏掉客户端的信息。

9a09b6465deb401e9041abb91b52c9fd.png

反向代理

从代理中我们得知代理服务器是给客户端做代理的,他和客户端是一伙的。而反向代理呢其实就是和正向代理反过来,他和服务器是一伙的,它屏蔽掉了服务器的信息,经常用在多台服务器的分布式部署上,像一些大的网站,由于访问人数很多,就需要多台服务器来解决人数多的问题,这时这些服务器就由一个反向代理服务器来代理,客户端发来请求,先由反向代理服务器,然后按一定的规则分发到明确的服务器,而客户端不知道是哪台服务器。常常用 Nginx 来作反向代理。

ffb1836a5dae42708b47e9546267a181.png

部署

Windows

b415d4e4d2ed40fca2f2907870bcaa26.png

下载带 Windows 标识的,下载后为 zip 文件,在目标文件夹进行解压。

在目标文件夹打开命令行,输入启动指令:

start nginx

Nginx 常用指令:

# 启动命令
start nginx

# 重新加载
nginx -s reload

# 强制停止
nginx -s stop

# 停止
nginx -s quit

# 测试配置文件
nginx -t

Linux

源码编译(非必要,不推荐)

Docker

nginx & tengine

version: '3'
services: 
  nginx:
    container_name: nginx
    image: nginx:1.21.3
    restart: always
    # network_mode: "host"
    ports:
      - 80:80
      - 8080:8080
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /home/nginx/html:/home/nginx/html
      - /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /home/nginx/log:/var/log/nginx

nginxWebUI

version: '3'
services: 
  nginx:
    container_name: nginx
    image: cym1102/nginxwebui
    restart: always
    # network_mode: "host"
    ports:
      - 80:80
      - 8080:8080
    environment:
      - TZ=Asia/Shanghai
      - BOOT_OPTIONS: "--server.port=8080"
    volumes:
      - /home/nginx/html:/home/nginx/html
      - /home/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /home/nginx/log:/var/log/nginx

Nginx 配置(重点)

配置文件作用
nginx.confnginx 的基本配置文件
mime.typesMIME 类型关联的扩展文件
fastcgi.conf与 fastcgi 相关的配置
proxy.conf与 proxy 相关的配置
sites.conf配置 nginx 提供的网站,包括虚拟主机常见的配置文件及其作用

Nginx.conf 总体解析

nginx.conf 主要分为以下三个大部分:

  • 全局配置:其中可能包含 event 配置
  • events {}:定义 event 模型工作特性
  • http {}:定义 http 协议相关的配置,包含 server 跟 location
#全局配置
events {
    #events
}

http {
   
   # 可以有多个upstream
   upstream name {   
      server 1;
      server 2;
   }
   
   # 可以有多个server
   server {
        # 可以有多个location
        location [PATTERN] {
   
        }
   }
}

以下是一份 nginx.conf​ 配置示例文件:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/Nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
  
    #日志格式
    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #日志存储路径
    #access_log  logs/access.log  main;
    # 默认开启简单来说就是启用sendfile()系统调用来替换read()和write()调用,
    # 减少系统上下文切换从而提高性能,当 Nginx 是静态文件服务器时
    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    # 请求超时时间
    keepalive_timeout  65;
    # 传输压缩
    #gzip  on;
  
    #websocket
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    # 负载均衡
    upstream demo {   
        server 127.0.0.1:7070;
        server 127.0.0.1:7071;
    }

    server {
        # 访问端口 可以重复 重复会根据server_name区分
        listen       80;
        # 一般填写域名
        server_name  localhost;
	
        #charset koi8-r;
	
        #access_log  logs/host.access.log  main;
	
        # 静态资源
        location / {
            root   /home/html/dist;
            index  index.html index.htm;
            # vue项目要加
            # 127.0.0.1/test.gif 会依次查找 
            # 1.文件/home/html/test.gif
            # 2.文件夹 /home/html/test.gif/下的index文件
            # 3. 请求127.0.0.1/index.html
            try_files $uri $uri/ /index.html
        }
    
        # 反向代理接口
        location /demo {
            # 请求体大小(默认1M,需要修改,不然超过1M文件上传会返回413)
            client_max_body_size 1024M;
            # 代理地址
            proxy_pass http://127.0.0.1:9090;
            # 代理重定向 默认不需要改
            proxy_redirect default;
            add_header backendIP $upstream_addr;
            # $host        浏览器请求的ip,不显示端口
            # $http_host   浏览器请求的ip和端口号
            # $proxy_host  被代理服务的ip和端口号
            proxy_set_header  Host $http_host;  
            proxy_set_header   X-Real-IP        $remote_addr; 
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; 
        }
  
        # 反向代理接口+负载均衡
        location /demo {
            # 请求体大小(默认1M,需要修改,不然超过1M文件上传会返回413)
            client_max_body_size 1024M;
            # 代理地址
            proxy_pass http://demo;
            # 代理重定向 默认不需要改
            proxy_redirect default;
            add_header backendIP $upstream_addr;
            # $host        浏览器请求的ip,不显示端口
            # $http_host   浏览器请求的ip和端口号
            # $proxy_host  被代理服务的ip和端口号
            proxy_set_header  Host $http_host;  
            proxy_set_header   X-Real-IP        $remote_addr; 
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for; 
        }
  
        # 反向代理websocket
        location /websocket {
            proxy_pass http://127.0.0.1;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
  
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /home/html/dist;
        }
    }
}

location

location 有两种匹配规则:

  • 匹配 URL 类型,有四种参数可选,当然也可以不带参数。

    location [ = | ~ | ~* | ^~ ] uri { … }
    
  • 命名 location,用 @ 标识,类似于定于 goto 语句块。

    location @name { … }
    

匹配 URL

  1. (=) (表示完全匹配)

    location = /demo {
      .....
    }
    
    # 只匹配http://127.0.0.1/demo 
    # http://127.0.0.1/demo [匹配成功]
    # http://127.0.0.1/demo/index [匹配失败]
    
  2. (~) (表示执行正则匹配,区分大小写。)

    location ~ /Demo {
      .....
    }
    
    # http://127.0.0.1/Demo/ [匹配成功]
    # http://127.0.0.1/demo/ [匹配失败]
    
  3. (~*) (表示执行正则匹配,忽略大小写)

    location ~* /Demo {
      .....
    }
    
    # http://127.0.0.1/Demo/ [匹配成功]
    # http://127.0.0.1/demo/ [匹配成功]
    
  4. (^~) (表示普通字符串匹配上以后不再进行正则匹配)

    location ^~ /index {
      .....
    }
    
    # 以 /index/ 开头的请求,都会匹配上
    # http://127.0.0.1/index/index  [匹配成功]
    # http://127.0.0.1/index/error  [匹配成功]
    # http://127.0.0.1/error/error  [匹配失败]
    
  5. 不加任何规则时,默认是大小写敏感,前缀匹配,相当于加了 (~)​ 和 (^~)

    location /index {
      ......
    }
    
    # http://127.0.0.1/index [匹配成功]
    # http://127.0.0.1/index/index [匹配成功]
    # http://127.0.0.1/test/index [匹配失败]
    # http://127.0.0.1/Index [匹配失败]
    # 匹配到所有uri
    
  6. 正则匹配

    location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)
    
    # .* 出现一次或多次
    # \. 代表匹配后缀分隔符.
    # (html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css) 后缀名
    

@ 标识

Nginx 内部跳转

location /index {
    error_page 404 @index_error;
}

location @index_error {
    .....
}

# 以 /index 开头的请求,如果链接的状态为 404。则会匹配到 @index_error 这条规则上。

优先级

=​ > ^~​ > ~ | ~*​ > 最长前缀匹配​ > /

root 跟 alisa 区别

location /demo {
    root   /home/html/dist;
    index  index.html index.htm;
}

location /demo {
    alisa  /home/html/dist;
    index  index.html index.htm;
}

# 访问 http://127.0.0.1/demo/test.jpg
# root情况  查找 /home/html/dist/demo/test.jpg 文件
# alisa情况 查找 /home/html/dist/test.jpg文件

loaction 后面结尾带不带/

location /demo/ {
    root   /home/html/dist;
    index  index.html index.htm;
}

如果 URI 的结构是 http://127.0.0.1/demo/index/

尾部如果缺少 / 将导致重定向。

因为根据约定,URL 尾部的 / 表示目录,没有 / 表示文件

所以访问 /index/ 时,服务器会自动去该目录下找对应的默认文件。

如果访问 /some-dir 的话,服务器会先去找 index 文件,找不到的话会将 index 当成目录,重定向到 /index/ ,去该目录下找默认文件。

upstream

常规配置

upstream{   
    # weight请求分发权重比为1:2
    # max_fails 失败次数
    # fail_timeout 如果在这个时间内请求失败次数打到了max_fails,则暂停这台服务请求
    # fail_timeout后 重新激活这个服务,后续请求失败1次则暂停这台服务请求,循环
    server 127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;   
    server 127.0.0.1:8090 weight=2 max_fails=2 fail_timeout=30s;
    # backup 一般不使用 除非上面都挂了或者过忙时
    server 127.0.0.1:9090 backup 
    # down 停止这台服务请求
    server 127.0.0.1:9090 down
}

负载均衡算法

  • 轮询

    rr 轮循即 round robin 默认调度算法,静态调度算法。客户端请求顺序把客户端的请求逐一分配到不同的后端节点服务器,这相当于 LVS 中的 rr 算法,如果后端节点服务器宕机(默认情况下 Nginx 只检测 80 端口)。宕机的服务器会自动从节点服务器池中剔除,以便客户端的用户访问不受影响。新的请求会分配给正产的服务器。

  • 权重轮询
    wrr 即 weight 权重轮循,静态调度算法。在 rr 轮循算法的基础上加上权重,即为权重轮循算法,当使用该算法时,权重和用户访问成正比,权重值越大,被转发的请求也就越多。可以根据服务器的配置和性能指定权重值大小,有效解决新旧服务器性能不均带来的请求分配问题。

    upstream demo {
        server 127.0.0.1 weight=1;
        server 127.0.0.1 weight=1;
    }
    
  • IP 哈希
    ip_hash 是静态调度算法,每个请求按客户端 IP 的 hash 结果分配,当新的请求到达时,先将其客户端 IP 通过哈希算法哈希出一个值,在随后的客户端请求中,客户 IP 的哈希值只要相同,就会被分配至同一台服务器,该调度算法可以解决动态网页的 session 共享问题,但有时会导致请求分配不均,即无法保证 1:1 的负载均衡,因为在国内大多数公司都是 NAT 上网模式,多个客户端会对应一个外部 IP,所以,这些客户端都会被分配到同一节点服务器,从而导致请求分配不均。LVS 负载均衡的 -P 参数、keepalived 配置里的 persistence_timeout 50 参数都类似这个 Nginx 里的 ip_hash 参数,其功能均为解决动态网页的 session 共享问题。

    upstream demo {
        ip_hash;
        server 127.0.0.1;
        server 127.0.0.1;
    }
    
  • 最小连接数
    least_conn 是动态调度算法,会根据后端节点的连接数来决定分配情况,哪个机器连接数少就分发。

    upstream demo {
        least_conn;
        server 127.0.0.1;
        server 127.0.0.1;
    }
    
  • 最短响应时间
    最短响应时间(fair)调度算法是动态调度算法,会根据后端节点服务器的响应时间来分配请求,响应时间端的优先分配。这是更加智能的调度算法。此种算法可以依据页面大小和加载时间长短只能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx 本身是不支持 fair 调度算法的,如果需要使用这种调度算法,必须下载 Nginx 的相关模块 upstream_fair。

    upstream demo {
        fair;
        server 127.0.0.1;
        server 127.0.0.1;
    }
    
  • url_hash 算法
    url_hash 算法是动态调度算法,按访问 URL 的 hash 结果来分配请求,使每个 URL 定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率命中率。(多用于后端服务器为缓存时的场景下)Nginx 本身是不支持 rul_hash 的,如果需要使用这种调度算法,必须安装 Nginx 的 hash 模块软件包。

    upstream demo {
        hash $request_uri;
        server 127.0.0.1;
        server 127.0.0.1;
    }
    

SSL 配置

添加 SSL 证书

server {
    listen    443 ssl;

    # 域名
    server_name  www.baidu.com;
  
    # 增加ssl 文件需要申请
    ssl_certificate ../ssl/8148314_www.baidu.com.pem;
    ssl_certificate_key ../ssl/8148314_www.baidu.com.key;
    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    # 指定密码为openssl支持的格式
    ssl_protocols  SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    # 密码加密方式
    ssl_ciphers  HIGH:!aNULL:!MD5;
    # 依赖SSLv3和TLSv1协议的服务器密码将优先于客户端密码
    ssl_prefer_server_ciphers  on;

    # 定义首页索引目录和名称
    location /admin {
        root html;
        index index.html index.htm;
    }
}

同域名从 http 强制跳转 https

server {
    listen 8080;

    server_name www.baidu.com;
  
    client_body_buffer_size 100m;
    client_max_body_size 100m;
  
    # 重点!!!添加该行代码
    rewrite ^(.*)$ https://${server_name}$1 permanent;
    # 上面的代码不行时,可以试下这个
    # error_page 497 =301 https://$host:1024$request_uri;
}

配置 https,重定向后 https 变成了 http+443

proxy_redirect default;​ 改成 proxy_redirect http:// https://;

location /admin {
    proxy_pass http://admin;
    proxy_redirect http:// https://
    proxy_set_header  Host $http_host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
}

实际使用优化

资源压缩

建立在动静分离的基础之上,如果一个静态资源的 Size​ 越小,那么自然传输速度会更快,同时也会更节省带宽,因此我们在部署项目时,也可以通过 Nginx​ 对于静态资源实现压缩传输,一方面可以节省带宽资源,第二方面也可以加快响应速度并提升系统整体吞吐。

Nginx​ 也提供了三个支持资源压缩的模块 ngx_http_gzip_module、ngx_http_gzip_static_module、ngx_http_gunzip_module​,其中 ngx_http_gzip_module​ 属于内置模块,代表着可以直接使用该模块下的一些压缩指令,后续的资源压缩操作都基于该模块,先来看看压缩配置的一些参数/指令:

bf63f5057839402cb3d57897ceaeaab8.png

了解了 Nginx​ 中的基本压缩配置后,接下来可以在 Nginx​ 中简单配置一下:

http{
    # 开启压缩机制
    gzip on;
  
    # 指定会被压缩的文件类型(也可自己配置其他类型)
    gzip_types text/plain application/javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
  
    # 设置压缩级别,越高资源消耗越大,但压缩效果越好
    gzip_comp_level 5;
  
    # 在头部中添加Vary: Accept-Encoding(建议开启)
    gzip_vary on;
  
    # 处理压缩请求的缓冲区数量和大小
    gzip_buffers 16 8k;
  
    # 对于不支持压缩功能的客户端请求不开启压缩机制
    gzip_disable "MSIE [1-6]\."; # 低版本的IE浏览器不支持压缩
  
    # 设置压缩响应所支持的HTTP最低版本
    gzip_http_version 1.1;
  
    # 设置触发压缩的最小阈值
    gzip_min_length 2k;

    # 关闭对后端服务器的响应结果进行压缩
    gzip_proxied off;
}

在上述的压缩配置中,最后一个 gzip_proxied​ 选项,可以根据系统的实际情况决定,总共存在多种选项:

  • ​​off​:关闭 nginx​ 对后台服务器的响应结果进行压缩。
  • expired:如果响应头中包含 Expires​ 信息,则开启压缩。
  • no-cache:如果响应头中包含 Cache-Control:no-cache​ 信息,则开启压缩。
  • ​​no-store​:如果响应头中包含 Cache-Control:no-store​ 信息,则开启压缩。
  • private:如果响应头中包含 Cache-Control:private​ 信息,则开启压缩。
  • no_last_modified:如果响应头中不包含 Last-Modified​ 信息,则开启压缩。
  • no_etag:如果响应头中不包含 ETag​ 信息,则开启压缩。
  • auth:如果响应头中包含 Authorization​ 信息,则开启压缩。
  • any:无条件对后端的响应结果开启压缩机制。

OK~,简单修改好了 Nginx​ 的压缩配置后,可以在原本的 index​ 页面中引入一个 jquery-3.6.0.js​ 文件:

<script type="text/javascript" src="jquery-3.6.0.js"></script>  

分别来对比下压缩前后的区别:

eb52202d48de4a7993716b4f00126ff9.png

从图中可以很明显看出,未开启压缩机制前访问时,js​ 文件的原始大小为 230K​,当配置好压缩后再重启 Nginx​,会发现文件大小从 230KB → 69KB​,效果立竿见影!

注意点:

  1. 对于图片、视频类型的数据,会默认开启压缩机制,因此一般无需再次开启压缩。
  2. 对于 .js​​ 文件而言,需要指定压缩类型为 application/javascript​​,而并非 text/javascript、application/x-javascript​​。

防盗链

首先了解一下何谓盗链:盗链即是指外部网站引入当前网站的资源对外展示 ​

来举个简单的例子理解:

好比壁纸网站 X​ 站、Y​ 站,X​ 站是一点点去购买版权、签约作者的方式,从而积累了海量的壁纸素材,但 Y​ 站由于资金等各方面的原因,就直接通过 <img src="X站/xxx.jpg" />​ 这种方式照搬了 X​ 站的所有壁纸资源,继而提供给用户下载。

那么如果我们自己是这个 X​ 站的 Boss​,心中必然不爽,那么此时又该如何屏蔽这类问题呢?那么接下来要叙说的**「防盗链」** 登场了!

Nginx​ 的防盗链机制实现,跟一个头部字段:Referer​ 有关,该字段主要描述了当前请求是从哪儿发出的,那么在 Nginx​ 中就可获取该值,然后判断是否为本站的资源引用请求,如果不是则不允许访问。Nginx​ 中存在一个配置项为 valid_referers​,正好可以满足前面的需求,语法如下:

valid_referers none | blocked | server_names | string ...;
  • none:表示接受没有 Referer​ 字段的 HTTP​ 请求访问。
  • blocked:表示允许 http://​ 或 https//​ 以外的请求访问。
  • server_names:资源的白名单,这里可以指定允许访问的域名。
  • string:可自定义字符串,支配通配符、正则表达式写法。

简单了解语法后,接下来的实现如下:

# 在动静分离的location中开启防盗链机制  
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css){
    # 最后面的值在上线前可配置为允许的域名地址
    valid_referers blocked 192.168.12.129;
    if ($invalid_referer) {
        # 可以配置成返回一张禁止盗取的图片
        # rewrite   ^/ http://xx.xx.com/NO.jpg;
        # 也可直接返回403
        return   403;  
    }
    root   /soft/nginx/static_resources;
    expires 7d;
}

根据上述中的内容配置后,就已经通过 Nginx​ 实现了最基本的防盗链机制,最后只需要额外重启一下就好啦!当然,对于防盗链机制实现这块,也有专门的第三方模块 ngx_http_accesskey_module​ 实现了更为完善的设计,感兴趣的小伙伴可以自行去看看。

PS:防盗链机制也无法解决爬虫伪造 referers​ 信息的这种方式抓取数据。

性能优化

最后再来聊一下关于 Nginx​ 的性能优化,主要就简单说说收益最高的几个优化项。

打开长连接配置

通常 Nginx 作为代理服务,负责分发客户端的请求,那么建议开启 HTTP​ 长连接,用户减少握手的次数,降低服务器损耗,具体如下:

upstream xxx {  
    # 长连接数  
    keepalive 32;  
    # 每个长连接提供的最大请求数  
    keepalived_requests 100;  
    # 每个长连接没有新的请求时,保持的最长时间  
    keepalive_timeout 60s;  
}

开启零拷贝技术

零拷贝这个概念,在大多数性能较为不错的中间件中都有出现,例如 Kafka​ 、Netty​ 等,而 Nginx​ 中也可以配置数据零拷贝技术,如下:

# 开启零拷贝机制
sendfile on;

零拷贝读取机制与传统资源读取机制的区别:

  • 传统方式:硬件 --> 内核 --> 用户空间 --> 程序空间 --> 程序内核空间 --> 网络套接字
  • 零拷贝方式:硬件 --> 内核 --> 程序内核空间 --> 网络套接字

从上述这个过程对比,很轻易就能看出两者之间的性能区别。

开启无延迟或多包共发机制

Nginx​ 中有两个较为关键的性能参数,即 tcp_nodelay​ 、 tcp_nopush​ ,开启方式如下:

tcp_nodelay on;
tcp_nopush on;

TCP/IP​ 协议中默认是采用了 Nagle 算法的,即在网络数据传输过程中,每个数据报文并不会立马发送出去,而是会等待一段时间,将后面的几个数据包一起组合成一个数据报文发送,但这个算法虽然提高了网络吞吐量,但是实时性却降低了。

因此你的项目属于交互性很强的应用,那么可以手动开启 tcp_nodelay ​配置,让应用程序向内核递交的每个数据包都会立即发送出去。但这样会产生大量的 TCP ​报文头,增加很大的网络开销。

相反,有些项目的业务对数据的实时性要求并不高,追求的则是更高的吞吐,那么则可以开启 tcp_nopush​ 配置项,这个配置就类似于“塞子”的意思,首先将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,内核会尽量把小数据包拼接成一个大的数据包(一个 MTU​)再发送出去.

当然若一定时间后(一般为 200ms​),内核仍然没有积累到一个 MTU ​的量时,也必须发送现有的数据,否则会一直阻塞。

tcp_nodelay​ 、 tcp_nopush​ 两个参数是“互斥”的,如果追求响应速度的应用推荐开启 tcp_nodelay​ 参数,如 IM​、金融等类型的项目。如果追求吞吐量的应用则建议开启 tcp_nopush​ 参数,如调度系统、报表系统等。

注意:

  1. tcp_nodelay​ 一般要建立在开启了长连接模式的情况下使用。
  2. tcp_nopush​ 参数是必须要开启 sendfile​ 参数才可使用的。

调整 Worker 工作进程

Nginx​ 启动后默认只会开启一个 Worker​ 工作进程处理客户端请求,而我们可以根据机器的 CPU 核数开启对应数量的工作进程,以此来提升整体的并发量支持,如下:

# 自动根据CPU核心数调整Worker进程数量  
worker_processes auto;  

工作进程的数量最高开到 8 ​个就 OK 了,8 ​个之后就不会有再大的性能提升。

同时也可以稍微调整一下每个工作进程能够打开的文件句柄数:

# 每个Worker能打开的文件描述符,最少调整至1W以上,负荷较高建议2-3W  
worker_rlimit_nofile 20000;  

操作系统内核(kernel​)都是利用文件描述符来访问文件,无论是打开、新建、读取、写入文件时,都需要使用文件描述符来指定待操作的文件,因此该值越大,代表一个进程能够操作的文件越多(但不能超出内核限制,最多建议 3.8W ​左右为上限)。

开启 CPU 亲和机制

对于并发编程较为熟悉的伙伴都知道,因为进程/线程数往往都会远超出系统 CPU 的核心数,因为操作系统执行的原理本质上是采用时间片切换机制,也就是一个 CPU 核心会在多个进程之间不断频繁切换,造成很大的性能损耗。

而 CPU 亲和机制则是指将每个 Nginx​ 的工作进程,绑定在固定的 CPU 核心上,从而减小 CPU 切换带来的时间开销和资源损耗,开启方式如下:

# 自动分配
worker_cpu_affinity auto;

# 手动分配 (4核心)
worker_cpu_affinity 0001 0010 0100 1000;

开启 epoll 模型及调整并发连接数

在最开始就提到过:Nginx、Redis​ 都是基于多路复用模型去实现的程序,但最初版的多路复用模型 select/poll​ 最大只能监听 1024​ 个连接,而 epoll​ 则属于 select/poll​ 接口的增强版,因此采用该模型能够大程度上提升单个 Worker​ 的性能,如下:

events {  
    # 使用epoll网络模型  
    use epoll;
  
    # 用来设置是否允许同时接收多个网络连接
    multi_accept on;
  
    # 调整每个Worker能够处理的连接数上限  
    worker_connections  10240;
}

可选模型 :

  • select

    select 库是在 linux 和 windows 平台都支持的事件驱动模型库,并且接口的定义也基本相同,只是部分参数的含义略有差异,最大并发限制 1024,是最早期的事件驱动模型。

  • poll

    Linux 的基本驱动模型,windows 不支持此驱动模型,是 select 的升级版,取消了最大的并发限制,在编译 Nginx 的时候可以使用--with-poll_module 和--without-poll_module 这两个指定是否编译 select 库。

  • epoll

    epoll 库是 Nginx 服务器支持的最高性能的事件驱动库之一,epoll 是 poll 的升级版,但是与 poll 的效率有很大的区别。

    epoll 的处理方式是创建一个待处理的事件列表,然后把这个列表发给内核,返回的时候再去轮询检查这个表,以判断事件是否发生,epoll 支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时 epoll 库的 IO 效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作。

  • kqueue

    用于支持 BSD 系列平台的高效事件驱动模型,主要用在 FreeBSD 4.1 及以上版本、OpenBSD 2.0 及以上版本,NetBSD 及以上版本及 Mac OS X 平台上,该模型也是 poll 库的变种,因此和 epoll 没有本质上的区别,都是通过避免轮询操作提供效率。

  • /dev/poll

    用于支持 unix 衍行生平台的高效事件驱动模型,主要在 Solaris 平台、HP/UX,该模型是 sun 公司在开发 Solaris 系列平台的时候提出的用于完成事件驱动机制的方案,它使用了虚拟的/dev/poll 设备,开发人员将要监视的文件描述符加入这个设备,然后通过 ioctl()调用来获取事件通知,因此运行在以上系列平台的时候请使用 /dev/poll 事件驱动机制。

  • eventport

    该方案也是 sun 公司在开发 Solaris 的时候提出的事件驱动库,支持 Solaris 10 以上的版本,该驱动库能有效防止内核崩溃等情况的发生。

不同的操作系统采用不同的 I/O 模型:

  • Linux 下,Nginx 使用 epoll 的 I/O 多路复用模型
  • FreeBSD 下,Nginx 使用 kqueue 的 I/O 多路复用模型
  • Solaris 下,Nginx 使用/dev/poll 方式的 I/O 多路复用模型
  • Windows 下,Nginx 使用 icop 的 I/O 多路复用模型
文章标签: CI/CD
推荐指数:

真诚点赞 诚不我欺~

Nginx使用及性能优化

点赞 收藏 评论