Nginx管理维护运维规范

一、版本约束

软件的不同版本,在使用起来都有可能带来不可预知的影响,因此需要统一整理,固定下来,不允许轻易变更。

image-20241202233419932

  • Nginx开源版官网,点击右侧download可以看到各个版本的Nginx,其中Mainline是抢先的主干版本(版本号是奇数),Stable是稳定版(版本号是偶数)

  • 系统环境,软件版本

    • OpenEuler22.03 LTS SP4
    • nginx: 1.24.0

二、软件安装路径

  • 部署路径

    /home/application/nginx

  • 代码路径

    /home/application/nginx/html

  • 日志路径

    /home/application/nginx/logs

  • 配置文件路径

    /home/application/nginx/conf/nginx.conf 【主配置】

    /home/application/nginx/conf.d/*

  • ssl证书路径

    /home/application/nginx/cert

三、安装

3.1 下载

wget http://nginx.org/download/nginx-1.24.0.tar.gz

3.2 安装依赖

安装依赖包,NGINX是C语言写的,pcre-devel支持正则表达式,openssl 开启加密

 yum -y install gcc pcre-devel openssl-devel tar make

3.3 创建nginx用户

useradd -s /sbin/nologin nginx -M

3.4 指定安装目录

启用 HTTP/2 模块;启用 SSL 模块;启用 Stub Status 模块;启用4 层TCP/UDP 代理模块;启用 Gzip 静态模块
{.is-success}

/home/application/nginx

mkdir /home/application/nginx
tar -xf nginx-1.24.0.tar.gz
cd nginx-1.24.0
 ./configure --prefix=/home/application/nginx --user=nginx --group=nginx --with-http_v2_module --with-http_ssl_module --with-http_stub_status_module --with-stream --with-http_gzip_static_module  --pid-path=/home/application/nginx/nginx.pid

3.5 编译安装

make && make install

3.6 服务启动

3.6.1 编写 nginx.service 文件

vim /lib/systemd/system/nginx.service

[Unit]
Description=The NGINX HTTP and reverse proxy server
 
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStart=/home/application/nginx/sbin/nginx
ExecReload=/home/application/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target

3.6.2 服务启动

systemctl daemon-reload
 
systemctl enable nginx
 
systemctl start nginx 
 
systemctl reload nginx  [重新加载配置文件,相当于 nginx -s reload]

四、平滑升级步骤

4.1 下载

wget https://nginx.org/download/nginx-1.26.2.tar.gz

4.2 解压

tar xf nginx-1.26.2.tar.gz

4.3 查看原先使用的模块

[root@nginx ~]# nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled

configure arguments: --prefix=/home/application/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-stream

4.4 编译安装

cd nginx-1.26.2
./configure --prefix=/home/application/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-stream
make

4.4 复制启动文件

mv /home/application/nginx/sbin/nginx /home/application/nginx/sbin/nginx-old

cp nginx-1.26.2/objs/nginx /home/application/nginx/sbin/

4.5 平滑升级

make upgrade

4.6 查看版本

/home/application/nginx/sbin/ -v

五、常见配置与常见优化项

5.1 版本隐藏(http模块)

server_tokens off

5.2 运行用户(全局模块)

user nginx

5.3 work工作进程(全局模块)

为了充分利用 NGINX的性能特点,在不同的使用场景下,应该适当调整 worker 进程的数量。

基本规则是 worker 进程的数量应该和 CPU的核数相同。

但也有一些场景略有不同:

  • 如果负载是CPU密集型,例如处理大量的 TCP/P 请求、执行 SSL或压缩),那么 worker进程的数量应该和CPU的核数相匹配
  • 如果负载大多和磁盘IO操作相关,比如代理静态瓦片地图;(从磁盘或大量代理中获取不同内容集),那么 worker进程的数量可能是 CPU核数的 1.5倍~2倍。还有一些场景,需要根据内存大选择 worker 进程的数量,但效率取决于磁盘存储的内容类型和配置
worker_processes auto

5.4 进程连接数(events模块)

worker_connections  1024

5.5 开启文件发送(http模块)

sendfile  on

5.6 会话保持超时时间(http模块)

keepalive_timeout 65

5.7 导入其它配置文件(http模块)

include /home/application/nginx/conf.d/*.conf;

5.8 日志

  • 日志格式(http模块)

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
    
    
  • 日志等级

    notice info
    
  • 日志切割

    1. vi cut_nginx_logs.sh
    #!/bin/bash
    
    # 获取日期
    d=$(date +%Y-%m-%d)
    
    # 定义存储目录
    dir="/home/application/nginx/logs"
    
    # 定义需要分割的源日志
    logs_file='/home/application/nginx/logs/access.log'
    logs_error='/home/application/nginx/logs/error.log'
    
    # 定义nginx的pid文件
    pid_file='/home/application/nginx/logs/nginx.pid'
    
    if [ ! -d "$dir" ]
    then
    	mkdir $dir
    fi
    
    # 移动日志并重命名文件
    mv ${logs_file} ${dir}/access_${d}.log
    mv ${logs_error} ${dir}/error_${d}.log
    
    # 发送kill -USR1信号给Nginx的主进程号,让Nginx重新生成一个新的日志文件
    kill -USR1 $(cat ${pid_file})
    
    PS:这里将脚本放在了home目录下,可根据实际情况存放脚本
    这里没设置删除删除原有日志文件,可根据实际情况自行设置
    
    2. 设置定时任务 每两天执行一次切割任务
    crontab -e
    59 23 */2 * * /home/application/nginx/cut_nginx_logs.sh
    

5.9 配置文件对齐

约定对齐格式每次缩进4个空格

5.10 开启压缩gzip配置(http模块)

gzip on;
gzip_http_version 1.1;
gzip_min_length 1k;
gzip_comp_level 4;
gzip_types text/plain application/json application/javascript application/octet-stream application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/bmp application/x-bmp image/x-ms-bmp application/vnd.ms-fontobject font/ttf font/opentype font/x-woff;
gzip_vary on;
gzip_buffers 4 16k;

5.11 缓存配置

  • 协商缓存

    在nginx中,协商缓存是一种机制,用于在客户端和服务器之间协商确定是否使用缓存的内容。当客户端发送一个请求时,nginx会检查该请求是否已经被缓存,如果有缓存的内容可用,则nginx会发送一个条件请求给服务器,询问服务器是否有新的内容可用。如果服务器返回的响应表明内容没有改变,则nginx会使用缓存的内容,否则会将新的内容缓存起来并返回给客户端。

    协商缓存通常使用HTTP头部字段进行协商,常见的字段包括If-Modified-Since、If-None-Match等。这些字段可以帮助服务器判断内容是否有改变,从而决定是否返回新的内容。

    通过使用协商缓存,可以减少网络传输的数据量,提高网站的性能和效率。同时,协商缓存也可以减轻服务器的负载,节省带宽和资源的消耗。

    location /gis3d {
                   alias /home/application/nginx/html/fxkj-screen/;
                   index index.html;
                   try_files $uri $uri/ /gis3d/index.html;
    				#协商缓存
                    add_header Cache-Control max-age=no-cache;
    }
    
  • 强制缓存

    强制缓存是指在客户端和服务器之间不进行任何协商,直接使用缓存的内容。当客户端发送一个请求时,nginx会检查该请求是否已经被缓存,如果有缓存的内容可用且尚未过期,则nginx会直接返回缓存的内容给客户端,而无需向服务器发送请求。

    强制缓存通常使用HTTP头部字段进行控制,常见的字段包括Expires和Cache-Control。这些字段可以指定缓存的有效期,客户端在有效期内再次请求相同的资源时,会直接使用缓存的内容。

    使用强制缓存可以减少对服务器的请求次数,加快网页加载速度,提高用户体验。但是需要注意的是,如果在有效期内服务器的内容发生了变化,客户端将无法得知,仍然使用缓存的内容,可能导致展示的内容不是最新的。为了解决这个问题,可以使用协商缓存的机制进行判断和更新。

    	location /3dmap {
          	alias /home/application/nginx/html/3d/;
    		#强制缓存
            add_header Cache-Control max-age=360000;
    }
    

5.12 不缓存配置

  • 方法一:server模块中location方法
location /fxkj-base {
                alias /home/application/nginx/html/fxkj/fxkj-base/;
                index index.html;
                #对于htm,html的页面不缓存
                if ($request_filename ~ .*\.(htm|html)$) {
                      add_header Cache-Control 'no-store, no-cache, must-revalidate';
                }
                try_files $uri /fxkj-base/index.html;
}
  • 方法二:http模块中map方法
#nginx.conf主配置文件中定义map

http {
..................
 map $request_filename $cache_control {
        default "";
        "~*\.html$" 'no-store, no-cache, must-revalidate';
        "~*\.htm$" 'no-store, no-cache, must-revalidate';
    }
.............
}
#server 中的配置文件直接引入map

server {

................
           location /fxkj-base {
                alias /usr/local/nginx/html/fxkj/fxkj-base/;
                index index.html;
				#map中加载不缓存配置
				add_header Cache-Control $cache_control;
                try_files $uri /fxkj-base/index.html;
           }

           location /fxkj-score {
                alias /usr/local/nginx/html/fxkj/fxkj-score/;
                index index.html;
				#map中加载不缓存配置
				add_header Cache-Control $cache_control;
                try_files $uri /fxkj-score/index.html;
           }
................

}

image-20230905164141788

5.12 文件最大上传限制(http,server模块)

client_max_body_size 100m;

5.13 根据访问前端IP,限制访问内容

护网期间,不允许外网WEB 访问系统地图

location /3dmap {
   proxy_ignore_headers Set-Cookie Cache-Control;
   proxy_hide_header Cache-Control;
   proxy_hide_header Set-Cookie;
   proxy_pass http://112.112.112.112:5004/;
   if ($host = '111.111.111.111') {
	  return 400;
   }
}

5.14 跨域配置

#参数解释
Access-Control-Allow-Origin:指定允许访问的源(域名)。
Access-Control-Allow-Headers:指定允许的头部字段。
Access-Control-Expose-Headers: 用于控制浏览器允许客户端访问的额外响应头字段。
Access-Control-Allow-Methods:指定允许的HTTP方法。
Access-Control-Allow-Credentials:指示是否允许发送身份凭证(如cookies)。




#常见配置:
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
                add_header Access-Control-Expose-Headers mark;
                add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
				add_header 'Access-Control-Allow-Credentials' 'true';
  • 静态文件-允许跨域访问
location /baidu {
		alias /home/application/baidu;
		#允许跨域
		add_header 'Access-Control-Allow-Origin' '*';
    	add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
    	add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
    	add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    	expires 7d;
		#启用了目录索引功能
		autoindex on;
}
  • 接口-允许跨域访问
location / {
                add_header Access-Control-Allow-Origin *;
                add_header Access-Control-Expose-Headers mark;
                add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
                add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
				add_header 'Access-Control-Allow-Credentials' 'true';
			
                proxy_pass http://172.16.10.140:8002;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
            }

image-20230905164421765

5.15 http 和 https 共存一个配置文件

[root@nginx conf.d]# cat http-https.conf

server {

        	listen       8208;
			    listen 	     18208 ssl http2;
        	    root         /home/application/nginx/html/fxkj;
	            ssl_certificate /home/application/nginx/conf/server.crt;
                ssl_certificate_key /home/application/nginx/conf/server.key;
                ssl_session_timeout 5m;
                ssl_protocols TLSv1.2 TLSv1.3;
                ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
                ssl_prefer_server_ciphers on;

#httpp-https重定向
        location =/ {
                if ($scheme = "http") {
                        proxy_pass http://$http_host/admin/;
                        }

                if ($scheme = "https") {
                        proxy_pass https://$http_host/admin/;
                        }
                }
...............................
}

5.16 HTTP反向代理,传递请求头

需要注意配置 proxy_pass 时,当在后面的 url 加上了/,相当于是绝对路径,则 Nginx不会把 location中匹配的路径部分加入代理uri
{.is-warning}

这几个参数是用于反向代理配置的,具体含义如下:

  1. proxy_set_header Host $host; 将客户端的 Host 头部传递给后端;如果一个 Nginx 实例代理多个域名(如 example.com 和 test.com),后端需要根据 Host 来区分不同的请求。可以确保后端服务器接收到正确的域名信息。
  2. proxy_set_header X-Real-IP $remote_addr; 将客户端的真实IP地址传递给后端服务器。这样后端服务器可以获取到客户端的真实IP地址。
  3. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 将客户端的原始IP地址传递给后端服务器。如果请求经过多个代理服务器,每个代理服务器会将自己的IP地址追加到该参数中,以便后端服务器可以获取到完整的客户端IP地址列表。
  4. proxy_set_header X-Forwarded-Proto $scheme; 告知后端客户端的实际协议(HTTP 或 HTTPS)。
  5. proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 支持协议切换(如 WebSocket)。
  6. proxy_set_header X-Custom-Header "my-custom-value"; 传递自定义信息供后端使用,应用需求(如特定用户标识、区域信息等)。

image-20241201155214761

http {
    upstream backend {
        server backend_server_ip:backend_server_port;
        # 可以添加多个后端服务器,用来实现负载均衡
        # server backend_server_ip2:backend_server_port2;
    }

    server {
        listen 80;
        server_name your_domain.com;

    location /api {
        proxy_pass http://backend;
    proxy_set_header Host $host;

    # 传递客户端真实 IP
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 传递客户端协议
    proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket 支持
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # 自定义请求头
    proxy_set_header X-Custom-Header "my-custom-value";
    }
    }
}

5.17 websock代理

websock代理,注意 ws 和 wss ; http 和 https 中都需要配置具体的location 配置,指定后端的接口地址

PS : 如果是https 和 http 共存的项目,使用同一套前端代码,需要在js代码中 加入判断 【https连接下浏览器不允许ws协议,只允许wss协议】

image-20221009100726927

nginx主配置文件:
.........
    map $http_upgrade $connection_upgrade {
      default upgrade;
      '' close;
    }
.........


引入	配置文件:
location ^~/socket {
   proxy_buffering off;
   rewrite ^/accidentsocket/(.*)$ /$1 break;
   proxy_pass http://192.168.1.1:9005;
   proxy_read_timeout 300s;
   proxy_send_timeout 300s;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   #升级http1.1到 websocket协议
   proxy_http_version 1.1;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection  $connection_upgrade;
}

5.18 自定义 404 界面

在Nginx配置文件中,server 模块,添加一下内容

  server {

        error_page  404 /404.html;

        location /404.html {
        alias /usr/local/nginx/old/404.html;
        internal;
        }
}    
  • error_page 404 /404.html;表示当出现404错误时,Nginx会重定向到/404.html页面。固定写法
  • location = /404.html { ... }用于指定404页面的处理方式。
  • alias /path/to/your/website;将/404.html页面映射到你网站的文件目录下。
  • internal;表示该配置只能在Nginx内部使用,外部无法直接访问。

5.19 重定向问题

之前nginx 里配置的return 是直接到 XXXX:80/admin , 但是在容器场景下,外部的端口如果是15099,从重定向访问的时候 ,浏览器请求的是 XXXX:80/admin , 很明显客户端不可能通容器内部的80端口;

原来的配置

server {

        	listen       8208;
			    listen 	     18208 ssl http2;
        	root         /home/application/nginx/html/fxkj;
	        ssl_certificate /home/application/nginx/conf/server.crt;
                ssl_certificate_key /home/application/nginx/conf/server.key;
                ssl_session_timeout 5m;
                ssl_protocols TLSv1.2 TLSv1.3;
                ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
                ssl_prefer_server_ciphers on;

#httpp-https重定向
        location =/ {
                if ($scheme = "http") {
                        proxy_pass http://$host:$server_port/admin/;
                        }

                if ($scheme = "https") {
                        proxy_pass https://$host:$server_port/admin/;
                        }
                }
...............................
}

改变后的配置


server {

        	listen       8208;
			    listen 	     18208 ssl http2;
        	    root         /home/application/nginx/html/fxkj;
	            ssl_certificate /home/application/nginx/conf/server.crt;
                ssl_certificate_key /home/application/nginx/conf/server.key;
                ssl_session_timeout 5m;
                ssl_protocols TLSv1.2 TLSv1.3;
                ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
                ssl_prefer_server_ciphers on;

#httpp-https重定向
        location =/ {
                if ($scheme = "http") {
                        proxy_pass http://$http_host/admin/;
                        }

                if ($scheme = "https") {
                        proxy_pass https://$http_host/admin/;
                        }
                }
...............................
}

变量区别

在nginx中,​http_host、host 和 $server_port 是三个不同的变量,它们的作用和含义如下:

  1. $http_host:这个变量包含了客户端请求中的"Host"标头的值。它会包含请求的域名和端口号(如果有的话),例如:www.example.com 或者 www.example.com:8080
  2. host:这个变量包含了客户端请求中的"Host"标头的值,但不包含端口号。如果请求中没有指定端口号,那么host:这个变量包含了客户端请求中的host标头的值,但不包含端口号。如果请求中没有指定端口号,那么host中的值将不包含端口号,例如:www.example.com
  3. server_port:这个变量包含了客户端请求中的端口号。如果请求中指定了端口号,那么server_port:这个变量包含了客户端请求中的端口号。如果请求中指定了端口号,那么server_port中的值将是该端口号;如果请求中没有指定端口号,则$server_port中的值将是80(HTTP默认端口)或443(HTTPS默认端口)。

因此,http_host 包含了完整的主机名和端口信息,$http_host包含了完整的主机名和端口信息,host 只包含主机名而不包含端口信息,而$server_port 则只包含端口信息。

$http_host使用场景

$http_host变量通常在需要获取客户端请求中的完整主机名和端口信息时使用。一些常见的场景包括:

  • 虚拟主机配置:当配置多个虚拟主机时,可以使用$http_host变量来根据请求的主机名和端口号来路由到不同的虚拟主机配置。
  • 重定向规则:在配置重定向规则时,可以使用$http_host变量来获取请求的完整主机名和端口信息,并根据需要进行重定向。
  • 访问日志:在访问日志中记录客户端请求的完整主机名和端口信息。

总之,$http_host 变量在需要获取客户端请求中的完整主机名和端口信息的场景下非常有用。

5.20 关于server name 指令 问题

该指令一般用来区分相同 IP地址和端口组合下的不同服务。NGINX在接收到HTTP 请求后会根据 listen 指令的参数在文件中查找相匹配的 server 块,然后在查找到的所有 server 块中根据 server_name 继续选择。如果找不到对应的 server 块,那么默认使用得到的所有server 块中的第一个。

image-20241202231834194

在使用 server_name 指令时,会匹配请求头中的 Host 字段,通常是一个 FODN 域名或地址。可以使用精确匹配、泛域名匹配和正则表达式匹配这些匹配规则,具体的匹配顺序依次如下:

  • 精确匹配,如 www.example.co
  • 以通配符开始的字符串匹配,如*.example.com*
  • *以通配符结束的字符串匹配,如 www.example
  • 正则表达式匹配,如^www.example.com$
  • 配置了 default_server 参数的 server 块【如果没有匹配到其他server块的监听端口为80的server_name,那么将会使用当前server块作为默认服务器来处理请求。这通常用于设置默认的虚拟主机或者处理未知主机名的请求。】

案例:

我有一个 nginx 服务器,做为公司的 WEB 服务器,监听在 80 端口上;监听在下面不同的 server_name 下面, 且我的 DNS 解析是 把 *.srebro.cn 都 A 记录到 172.22.33.1 这个ip 上;这样就会有一个问题,当我访问任意 xx.srebro.cn 的时候,他就是随机跳转我下面的 server_name 上; 这意味着当请求到达nginx服务器时,如果没有匹配到其他server块的监听端口为80的server_name,那么将会使用当前server块作为默认服务器来处理请求。

  • wiki.srebro.cn
  • code.srebro.cn
  • map.srebro.cn

**处理方式:设置默认的虚拟主机或者处理未知主机名的请求 **

server {
  listen 80 default_server;
  listen 443 ssl default_server;
  ssl_certificate /home/application/nginx/cert/srebro.cn/srebro.cn.crt;
  ssl_certificate_key /home/application/nginx/cert/srebro.cn/srebro.cn.key;

  server_name _;
        root /home/application/nginx/html;
}

5.21 nginx 中默认使用 http1 还是http2?

在 Nginx 中,默认情况下使用的是 HTTP/1.1 协议。如果希望启用 HTTP/2,需要显式地在配置文件中进行设置。

如果nginx 是编译部署,在编译 Nginx 时没有添加 --with-http_v2_module 参数,Nginx 就不会包含 HTTP/2 模块,因此你无法在配置中启用 HTTP/2 功能

编译 Nginx在安装或重新编译 Nginx 时,加入以下参数:

./configure --with-http_v2_module ......

配置文件中启用 http2示例:

server {
    listen 443 ssl http2;  # 启用 HTTP/2
    server_name www.example.com;

    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;

    # 其他配置
}

在这个配置中,http2 参数使得 Nginx 启用 HTTP/2,并且这个设置仅对 SSL/TLS(即 HTTPS)连接有效。

默认 HTTP 协议:

  • HTTP/1.1 是 Nginx 的默认协议,除非显式启用 HTTP/2。
  • HTTP/2 需要 Nginx 版本 1.9.5 或更高,并且仅能在启用了 SSL/TLS 的情况下使用。

HTTP/1.1 对比 HTTP/2 协议

image-20241201151706495

5.22 nginx 的ssl 配置中ssl_prefer_server_ciphers on 参数用途

在 Nginx 的 SSL 配置中,ssl_prefer_server_ciphers on; 这个指令的作用是控制在客户端和服务器之间协商加密套件时,是否优先使用服务器指定的加密算法(cipher suite)

具体解释:

  • 加密套件(Cipher Suite)是用于 SSL/TLS 连接的加密算法集合,包括加密算法、消息认证码算法(MAC)、密钥交换算法等。
  • 客户端和服务器协商:当客户端和服务器建立 SSL/TLS 连接时,会互相协商一个共同支持的加密套件。这个过程通常是客户端先发送自己支持的加密套件列表,然后服务器从中选择一个双方都支持的加密套件。

作用:

  • 开启:如果设置为 on,服务器将优先选择自己配置的加密套件,而不是根据客户端的首选顺序来选择。这意味着,服务器可以强制使用更强的加密套件,尽管客户端可能更倾向于使用其他套件。
  • 关闭(默认值是 off):如果设置为 off,则服务器会遵循客户端提供的加密套件优先顺序,选择客户端列出的最优加密套件。
  • 提高安全性:通过优先选择服务器配置的加密套件,服务器可以确保使用强加密算法,避免客户端可能使用的弱加密算法(即使客户端支持它们)。这种配置有助于提高 SSL/TLS 连接的安全性。

配置文件示例:

server {

                listen       8208;
                listen        18208 ssl http2;
                ssl_certificate /home/application/nginx/conf/server.crt;
                ssl_certificate_key /home/application/nginx/conf/server.key;
                ssl_session_timeout 5m;
                ssl_protocols TLSv1.2 TLSv1.3;
                ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
                ssl_prefer_server_ciphers on;

在这个例子中,ssl_ciphers 指定了服务器允许的加密套件,而 ssl_prefer_server_ciphers on; 确保即使客户端提供了其他加密套件,服务器也会优先选择这些高强度的加密算法。 ssl_ciphers 指定算法为 ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE

5.23 split_clients 模块用途

Nginx 的 split_clients 模块 是一个非常实用的模块,主要用于实现流量分流请求随机分配,常被用在 A/B 测试、灰度发布或负载均衡场景中。

  • split_clients 模块根据预定义的权重,按一定比例将请求划分到不同的组。通过这种方式,可以将用户分配到不同的实验组、版本或后端服务器,便于测试新功能或分配流量。
  • 模块通过对某些用户属性(如 Cookie、IP 地址、客户端类型,请求参数等)的哈希值进行计算,来保证请求的分配是稳定的(即同一个用户总是会被分配到同一组)。

基本语法

split_clients "${variable}" $result_variable {
    range1 condition1;
    range2 condition2;
    ...
    * default_condition;
}
  • ${variable}:用于哈希的变量,可以是 $remote_addr$cookie_*$arg_* 等。
  • $result_variable:结果存储变量,用于判断用户属于哪一组。
  • range:划分的范围(权重)。
  • condition:条件的值,表示用户所属的组。
  • default_condition:默认条件,当范围未覆盖到某些请求时的兜底值。

(1)、实现 A/B 测试的示例

假设需要将 50% 的用户分配到版本 A,另 50% 分配到版本 B:

nginx


复制代码
split_clients "${remote_addr}" $variant {
    50%     "A";
    50%     "B";
}

location / {
    if ($variant = "A") {
        proxy_pass http://backend_A;
    }

    if ($variant = "B") {
        proxy_pass http://backend_B;
    }
}
  • 根据用户的 IP 地址($remote_addr),以 50% 的比例将请求分流到 backend_Abackend_B
  • 每个用户的分配是固定的,因为基于哈希计算。

(2)、实现灰度发布的示例

在灰度发布中,你可能希望将 20% 的流量发送到新版本,其余 80% 发送到旧版本:

nginx


复制代码
split_clients "${remote_addr}" $variant {
    20%     "new";
    *       "old";
}

location / {
    if ($variant = "new") {
        proxy_pass http://new_backend;
    }

    if ($variant = "old") {
        proxy_pass http://old_backend;
    }
}
  • 20% 表示将 20% 的用户分配到 new_backend
  • * 表示剩余的用户,分配到 old_backend

(3)、实现多版本分流

如果有多个版本需要测试,可以设置多个分流比例:

nginx


复制代码
split_clients "${remote_addr}" $variant {
    10%     "A";
    20%     "B";
    30%     "C";
    *       "D";
}

location / {
    proxy_pass http://backend_$variant;
}

此配置中:

  • 10% 的用户分配到版本 A;
  • 20% 的用户分配到版本 B;
  • 30% 的用户分配到版本 C;
  • 其余用户分配到版本 D。

实践: 使用 User-Agent 区分移动端和 PC 端

Nginx 的 $http_user_agent 变量可以获取客户端的 User-Agent 字段,通过检测其中的关键标识词来区分设备类型。

# 根据 User-Agent 设置变量
map $http_user_agent $device_type {
    ~*Mobile         "mobile";  # 包含 "Mobile" 的 User-Agent,通常为移动设备
    ~*Android|iPhone "mobile";  # 常见移动设备关键字
    default          "pc";      # 其他情况默认为 PC
}

# 使用 split_clients 进行分流
split_clients "${remote_addr}" $variant {
    50% "A";  # 分流到 A 组
    50% "B";  # 分流到 B 组
}

# 不同设备访问不同服务
location / {
    if ($device_type = "mobile") {
        proxy_pass http://mobile_backend_$variant;  # 移动端服务
    }

    if ($device_type = "pc") {
        proxy_pass http://pc_backend_$variant;      # PC 端服务
    }
}

5.24 配置缓冲区

在默认情况下,NGIX 缓冲区中的内容来自后端服务器。当后端服务器返回响应内容时NGINX 先将它存储在内部缓冲区中,直到接收到整个响应内容后才发送给客户端。缓冲区有于优化慢速客户端的性能,如果 NGINX把响应内容同步传递到客户端,那么有可能会浪费代理服务器的时间。而启用缓冲区后,NGINX允许代理服务器快速处理响应,NGINX把响应内容在储下来的时间和客户端下载响应内容所需的时间一样长。

负责启用和禁用缓冲区的指令是 proxy_buffering,其默认值为 on,表示启用缓冲区proxy_buffers 指令用于设置为请求分配的缓冲区大小及数量。缓冲区的大小由 proxybuffer_size 指令设置。来自代理服务器的响应内容的第一部分会被存储在单独的缓冲区中这部分通常包含一个相对较小的响应头。

缓冲区默认大小

默认缓冲区大小
以下是一些缓冲区的默认值(不同版本可能略有不同):

proxy_buffers:默认是 8 4k 或 8 8k(8 个缓冲区,每个缓冲区大小为 4k 或 8k)。
proxy_buffer_size:默认值通常为一个内存页大小(4k 或 8k,取决于系统架构)。
client_body_buffer_size:默认通常是 8k 或 16k。

自定义缓冲区大小

location / {
proxy_buffers 16 4k;         # 响应体缓冲区
proxy_buffer_size 2k;        # 响应头缓冲区
proxy_pass http://localhost:8080;
}

如果禁用缓冲区,则将从服务器接收到的响应同步发送到客户端。对于需要尽快开始接收响应的快速交互客户端,这样的配置是可行的。

例如在 websock 的场景下,为了时效性,快速交互客户端,就不会开启缓冲区配置

location ^~/socket {
   proxy_buffering off;  #禁用缓冲区
   rewrite ^/accidentsocket/(.*)$ /$1 break;
   proxy_pass http://192.168.1.1:9005;
   proxy_read_timeout 300s;
   proxy_send_timeout 300s;
   proxy_set_header Host $host;
   proxy_set_header X-Real-IP $remote_addr;
   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
   #升级http1.1到 websocket协议
   proxy_http_version 1.1;
   proxy_set_header Upgrade $http_upgrade;
   proxy_set_header Connection  $connection_upgrade;
}