Linux 运维手册之 NGINX 网页服务
后知后觉 暂无评论

NGINX 是一个免费的,开源的,高性能的 HTTP 服务器和反向代理,以及 IMAP / POP3 代理服务器。 NGINX 以其高性能,稳定性,丰富的功能集,简单的配置和低资源消耗而闻名。

概述

为方便描述,下文简称 NGX。

虽然 NGINX 市场占有率并不占有绝对优势,但是对比其他 WEB 服务器还是有很大优势。

  1. 作为网页服务器:相比 ApacheNGINX 使用更少的资源,支持更多的并发连接(能够支持高达 50000 个并发连接数的响应),静态页面处理性能比 Apache3倍以上,但对动态内容支持较弱,需要其他依赖进行实现,并且组件不如 Apache 丰富。
  2. 作为负载均衡服务器:相比 PerlbalNGINX 既可以在内部直接支持 RailsPHP 程序对外进行服务, 也可以支持作为HTTP代理服务器对外进行服务。采用C语言进行编写,不论是系统资源开销还是CPU使用效率都要好很多。

其他优势

  1. NGINX 配置相对简洁,启动过程迅速,服务宕机可能性低,并且支持服务平滑升级。
  2. Apache是同步多进程模型,一个连接对应一个进程;NGINX是异步的,多个连接(万级别)对应一个进程。
  3. NGINX的优势是处理静态请求,CPU内存使用率低,Apache适合处理动态请求,所以一般前端用NGINX作为反向代理抗住压力,Apache作为后端处理动态请求。

NGINX 部署

支持发行版

官方预编译版本支持以下发行版

RHEL/CentOS

版本支持的平台
6x86_64, i386
7x86_64, ppc64le
8x86_64

Debian

版本代号支持的平台
6squeezex86_64, i386
7wheezyx86_64, i386
8jessiex86_64, i386
9stretchx86_64, i386
10busterx86_64, i386

Ubuntu

版本代号支持的平台
14.04trustyx86_64, i386, aarch64/arm64
16.04xenialx86_64, i386, ppc64el, aarch64/arm64
18.04bionicx86_64
19.04discox86_64

SLES

版本支持的平台
12x86_64
15x86_64

Alpine

版本支持的平台
3.8x86_64
3.9x86_64
3.10x86_64

安装说明

RHEL/CentOS

安装基础包

sudo yum install yum-utils

导入官方源

sudo vim /etc/yum.repos.d/nginx.repo

插入以下内容

[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
小贴士:默认情况下会安装 stable 分支,若要启用 mainline 分支,请执行下述命令。
sudo yum-config-manager --enable nginx-mainline

安装 NGINX

sudo yum install nginx

Debian

安装基础包

sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring

设置软件仓库

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

若想使用 mainline 分支,请执行

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
https://nginx.org/packages/mainline/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

导入官方密钥

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

配置仓库权重,使用官方仓库,而不是发行版仓库

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

安装 NGINX

sudo apt update && sudo apt install nginx

Ubuntu

安装基础包

sudo apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

设置软件仓库

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
注意:因 Ubuntu 18.04 彻底移除了 x86 架构的支持,因此需要加入[arch=amd64]只检索 amd64 ,否则会有报错信息。

若想使用 mainline 分支,请执行

echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

导入官方密钥

curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

配置仓库权重,使用官方仓库,而不是发行版仓库

echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

安装 NGINX

sudo apt update && sudo apt install nginx

软件包来源校验

存储库都使用数字签名来验证下载软件包的来源与完整性(是否被篡改)。检查校验签名,则需要下载官方签名密钥将其导入系统密钥池。

DebianUbuntu 中不导入密钥会报以下错误:

W: GPG error: http://nginx.org/packages/ubuntu bionic InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY ABF5BD827BD9BF62
E: The repository 'http://nginx.org/packages/ubuntu bionic InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.

也可以使用以下命令导入密钥:

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ABF5BD827BD9BF62
注意:后面的密钥串ABF5BD827BD9BF62应替换为错误信息中的数值。

NGINX 模块

自动索引模块

ngx_http_autoindex_module

官方文档:点击跳转

配置模板:

location / {
    root html;
    autoindex on;
    autoindex_localtime on;
    autoindex_exact_size off;
}

语法:

autoindex on/off;
默认参数:off
字段位置:http, server, location

参数:

autoindex_exact_size off;    # 单位换算
# 可选参数:on | off ,默认 on 单位为 byte,off 换算为 kB,MB,GB

autoindex_localtime on;      # 时间格式
# 可选参数:on |off ,默认 off 显示的文件时间为GMT时间。on 显示的文件时间为文件的服务器时间。

charset utf-8,gbk;           # 字符集设置
# 可选参数:UTF-8 | GBK | GB2312 ,添加GBK后即可解决中文乱码

autoindex_format html;       # 网页格式
# 可选参数: html | xml | json | jsonp ,默认 html 。

状态监测模块

ngx_http_stub_status_module

官方文档:点击跳转

配置模板:

location = /basic_status {
    stub_status;
}

语法:

stub_status;
默认参数:  —
字段位置:server, location

访问限制模块

ngx_http_access_module

官方文档:点击跳转

配置模板:

location / {
    deny  192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    allow 2001:0db8::/32;
    deny  all;
}

语法:

allow address | CIDR | unix: | all;
默认参数:  —
字段位置:http, server, location, limit_except

用户认证模块

ngx_http_auth_basic_module

官方文档:点击跳转

配置模板

location / {
    auth_basic           "closed site";
    auth_basic_user_file conf/htpasswd;
}

语法:

auth_basic string | off;
默认:   off
auth_basic_user_file file;
字段位置:http, server, location, limit_except
注意:密码文件需严格按照以下格式进行生成。密码文件名可使用变量。
# comment
name1:password1
name2:password2:comment
name3:password3

生成密码可以使用 Apache 工具包中的小工具进行生成

sudo yum install httpd-tools
sudo htpasswd -c /etc/nginx/auth_conf password

连接限制模块

ngx_http_limit_conn_module

官方文档:点击跳转

配置模板1:

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    ...
    server {
        ...
        location /download/ {
            limit_conn addr 1;
        }

配置模板2:

http {
    limit_conn_zone $binary_remote_addr zone=perip:10m;
    limit_conn_zone $server_name zone=perserver:10m;
    ...
    server {
        ...
        limit_conn perip 10;
        limit_conn perserver 100;
    }

语法:

limit_conn zone number;
默认:    —
字段位置:http, server, location

日志:

limit_conn_log_level info | notice | warn | error;
默认:error;
字段位置:http, server, location

状态:

limit_conn_status code;
默认:503;
字段位置:http, server, location

请求限制模块

ngx_http_limit_req_module

官方文档:点击跳转

配置模板:

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=one burst=5;
        }

语法:

limit_req zone=name [burst=number] [nodelay];
默认:    —
字段位置:http, server, location

示例用法:

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

server {
    location /search/ {
        limit_req zone=one burst=5;
    }
说明:允许每秒平均不超过 1 个请求,并发不超过 5 个请求。

如果不希望在请求受限的情况下拒绝过多的请求,则应使用以下参数来推迟多余的请求:

limit_req zone=one burst=5 nodelay;

日志:

limit_req_log_level info | notice | warn | error;
默认:    error;
字段位置:http, server, location

状态:

limit_req_status code;
默认: 503;
字段位置:http, server, location

日志记录模块

ngx_http_log_module

官方文档:点击跳转

NGINX 提供完备的日志记录功能,此模块可以深度定义日志格式,便于筛选可用信息。

配置模板:

log_format compression '$remote_addr - $remote_user [$time_local] '
                       '"$request" $status $bytes_sent '
                       '"$http_referer" "$http_user_agent" "$gzip_ratio"';

access_log /spool/logs/nginx-access.log compression buffer=32k;

语法:

access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
默认:
access_log logs/access.log combined;
字段位置:http, server, location, if in location, limit_except

日志变量详解:

$remote_addr, $http_x_forwarded_for # 记录客户端IP地址
$remote_user                        # 记录客户端用户名称
$request                            # 记录请求的URL和HTTP协议
$status                             # 记录请求状态
$body_bytes_sent                    # 发送给客户端的字节数,不包括响应头的大小
$bytes_sent                         # 发送给客户端的总字节数
$msec                               # 日志写入时间。 单位为秒, 精度是毫秒。
$http_referer                       # 记录从哪个页面链接访问过来的
$http_user_agent                    # 记录客户端浏览器相关信息
$request_length                     # 请求的长度(包括请求行, 请求头和请求正文)。
$request_time                       # 请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始, 直到把最后一个字符发送给客户端后进行日志写入为止。
$time_iso8601                       # ISO8601标准格式下的本地时间。
$time_local                         # 通用日志格式下的本地时间。

日志缓存功能:

open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
默认:off;
字段位置:http, server, location
参数描述
max设置缓存中的最大文件描述符数量,如果缓存被占满,采用LRU算法将描述符关闭
inactive设置存活时间,默认是 10s
min_uses设置在 inactive 时间段内,日志文件最少使用多少次后,该日志文件描述符记入缓存中,默认是1次
valid设置检查频率, 默认 60s
off禁用缓存

NGINX 配置

此部分配置实质上是核心模块中内容(ngx_http_core_module),但是使用频率最高,因此单独拿出来进行分析。

监听端口(listen

示例配置:

server {
    listen       80;
    server_name  www.domain.com;
    root         /var/www/html/domain.com;
    index        index.php index.html
    ...
}
注意:浏览器在访问页面时会默认访问 80 端口,因此网站的监听端口请勿修改(若做了端口转发或者负载均衡可使用其他端口),以防访客无法正常使用。

根目录(root

示例配置:

server {
    listen       80;
    server_name  www.domain.com;
    root         /var/www/html/domain.com;
    index        index.php index.html
    ...
}

root 指定了此主机的根目录,所有资源会在此目录下寻找,因此请设置正确的根目录,部分产品解压后存在多个目录,请分辨实际的网站目录并指定。

补充:指定根目录后可以使用文件的相对地址来指定资源。

与之相关的还有一个 alias 作用与 root 相似,会将root目录重定向为alias 地址。

举个例子:

server {
        listen 80;
        index index.html;
        location /request_path/code/ {
                alias /local_path/code/;
        }
}

实际请求:/local_path/code/index.html

server {
        listen 80;
        index index.html;
        location /request_path/code/ {
                root /local_path/code/;
        }
}

实际请求:/local_path/code/request_path/code/index.html

索引页面(index

示例配置:

server {
    listen       80;
    server_name  www.wavengine.com;
    root         /var/www/html/wavengine/blog;
    index        index.php index.html
    ...
}
说明:指定索引页面后,NGINX 会按指定的顺序在根目录下搜索,显示为当前主机的主页。

虚拟主机 (VH

虚拟主机是指在一个 NGINX 配置文件中指定多个网站

$ cat /etc/nginx/conf.d/all.conf

配置文件模板

server {
    listen       80;
    server_name  blog.domain.com;
    root         /var/www/html/domain.com/blog;
    ...
}
 
server {
    ...
    listen       80;
    server_name  ftp.domain.com;
    root         /var/www/html/domain.com/ftp;
}

也可以将多个主机拆分为多个文件(文件名可以自定义,但必须以 .conf 结尾)

$ ll
-rw-r--r-- 1 root root 1.1K Apr 17 09:46 default.conf
-rw-r--r-- 1 root root  353 Aug 14 00:07 ftp.conf
-rw-r--r-- 1 root root 2.6K Aug 14 23:01 blog.conf
total 12K
小贴士:推荐在主配置文件上 /etc/nginx/nginx.conf 配置 http 段配置,在 /etc/nginx/conf.d/ 中配置 server 段配置。不同的域名可以监听在同一个端口,请求会根据请求的域名进行自动分配,但是若同一域名绑定相同的地址会触发优先级问题(应尽量避免)。

资源定位(location

location 用于在一个主机中匹配用户访问的资源,并进行处理和回应。

匹配符匹配规则优先级
=表示精确匹配1
^~表示以某字符串开头2
~表示区分大小写的正则匹配3
~*表示不区分大小写的正则匹配4
!~表示区分大小写不匹配的正则5
!~*表示不区分大小写不匹配的正则6
/通用匹配,任何请求都会匹配到7
注意:~~* 正则匹配规则在匹配后会寻找更精准的location再次进行匹配。

文件检查(try_files

按指定顺序检查文件是否存在,并使用第一个找到的文件进行请求处理,若全找不到则内部重定向至最后一个参数;同时将根据请求rootalias指令从文件参数构造文件的路径。可以通过在名称的末尾指定斜杠来检查目录的存在,例如:$uri/

字段位置:server, location

示例配置:

server {
    listen      80;
    server_name test.domain.com;
    root        /test/code;
    index       index.html;
    location / {
        try_files $uri @java_page;
    }
    location @java_page {
        proxy_pass http://127.0.0.1:8080;
    }
}
说明:此配置表示若用户访问示例地址后会在 /test/code 下寻找 index.html 若不存在文件会将请求导向本地端口8080进行解析(Tomcat)。

最后一个参数也可以指向一个命名空间,比如下面的例子,同理最后一个参数也可以用返回码。

location / {
    try_files $uri $uri/index.html $uri.html =404;
}
location / {
    try_files $uri $uri/ @drupal;
}

location ~ \.php$ {
    try_files $uri @drupal;

    fastcgi_pass  127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME /path/to$fastcgi_script_name;
    fastcgi_param SCRIPT_NAME     $fastcgi_script_name;
    fastcgi_param QUERY_STRING    $args;
}

location @drupal {
    fastcgi_pass   127.0.0.1:9000; 
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

需要注意的一点是若在 location 中使用,最后的参数请写完整相对路径。如下配置文件

    location /web/mall {
        root   /data;
        try_files $uri $uri/ @router;
        index  index.html;
    }

    location @router{
        rewrite ^.*$ /web/mall/index.html last;
    }

比如访问 http://www.domain.com/web/mall ,若没有找到文件会被内部重写到 http://www.domain.com$uri 中,而不是 http://www.domain.com/web/mall$uri ,因此在 @router 中使用需要写完整的相对路径,比如单页面 Vue 项目。

NGINX 负载均衡

LBLoad Balance,负载均衡)是将一个任务分摊到多个操作单元上进行执行,从而分摊压力,增加吞吐量、加强数据处理能力、提高网络的灵活性和可用性。

NGINX 的负载均衡实质上是“反向代理”。

反向代理与负载均衡之间的关系:如果后端是一台服务器就叫反向代理,如果有多台就是负载均衡。反向代理才能实现负载均衡,负载均衡是做反向代理的目的之一。

“正向代理”和“反向代理”的区别:

简单地理解,正向代理隐藏用户身份,反向代理隐藏服务器身份。

正向代理与反向代理

正向代理(!AVIF)

举个例子:

  我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。

正向代理的用途:
(1)访问原来无法访问的资源。
(2)作为缓存,加速访问资源。
(3)对客户端访问授权,上网进行认证。
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息。

反向代理(!AVIF)

举个例子:

  我是一个用户,我访问某个网站,但是实质上给我提供数据的是另一个服务器,但是它隐藏在代理服务器后,我只能知道我连接的是谁,但是不知道具体谁给我提供的服务。隐藏了真实服务器的信息,提高了安全指数。

反向代理的用途:
(1)保证内网的安全,可以使用反向代理提供WAF功能,阻止web攻击。
(2)负载均衡,分摊压力,提高数据处理能力。

NGINX 反向代理配置

语法:

proxy_pass URL;
默认参数:—
字段位置:location, if in location, limit_except

跳转重定向

语法:

proxy_redirect default;
可选参数:proxy_redirect off;proxy_redirect redirect replacement;
默认参数:proxy_redirect default;
字段位置:http, server, location

头部设定

在默认使用反向代理时会遇到一个问题,抓包时可在头部信息中看到真实服务器地址及端口,这是不合理的,因此需要使用重新定义头部信息功能。

语法:

proxy_set_header field value;
默认参数:proxy_set_header Host $proxy_host;
            proxy_set_header Connection close;
字段位置:http, server, location

例子:

# 用户请求的时候HOST的值是www.wavengine.com, 那么代理服务会像后端传递请求的还是www.wavengine.com
proxy_set_header Host $http_host;
# 将$remote_addr的值放进变量X-Real-IP中,$remote_addr的值为客户端的ip
proxy_set_header X-Real-IP $remote_addr;
# 客户端通过代理服务访问后端服务, 后端服务记录真实客户端地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

其他参数:

# 代理服务连接后端服务器的连接超时时间,建议使用默认值
语法:proxy_connect_timeout time;
默认参数:proxy_connect_timeout 60s;
字段位置:http, server, location

# 代理服务等待后端服务器的响应时间
语法:proxy_read_timeout time;
默认参数:proxy_read_timeout 60s;
字段位置:http, server, location

# 后端服务器在规定时间之内必须传完所有的数据至代理
语法:proxy_send_timeout time;
默认参数:proxy_send_timeout 60s;
字段位置:http, server, location

代理缓冲区

代理缓冲功能开启的情况下,NGINX会把后端返回的内容先放到缓冲区当中,然后再返回给客户端,边收边传, 不是全部接收完再传给客户端。

语法:proxy_buffering on | off;
默认参数:proxy_buffering on;
字段位置:http, server, location

语法:proxy_buffer_size size;
默认参数:proxy_buffer_size 4k|8k;
字段位置:http, server, location

语法:proxy_buffers number size;
默认参数:proxy_buffers 8 4k|8k;
字段位置:http, server, location

配置优化

在同一个配置文件中全部定义这些参数会导致配置文件冗余复杂,可读性差。因此将这部分配置写入一个配置文件,然后统一调用。

比如:

vim /etc/nginx/proxy_params

写入以下内容

proxy_redirect default;
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;

proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;

proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;
proxy_max_temp_file_size 256k;

在其他配置文件中可以直接使用:

location / {
    proxy_pass http://127.0.0.1:8080;
    include proxy_params;
}

NGINX 负载均衡

负载均衡的作用是分摊压力,因此需要一台服务器暴露在外面用于分配工作,其他的服务器用于实际承担工作内容,因此需要定义一个“负载均衡池”,就是一个定义工作服务器的清单。这个清单在 NGINX 中称为 upstream

配置模板:

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://backend;
    }
}
配置说明:定义了一个负载均衡池,其中有五台机器。weight 定义了权重,数值越大,分配使用的概率越大。backup 则是定义为备用机器,在其他机器可用的时候不会使用,只有当全部非备用机器宕机后才会实际使用。

upstream 中支持健康检查功能,可检测服务器的可用性,并对此进行实时调整。

配置模板:

resolver 10.0.0.1;

upstream dynamic {
    zone upstream_dynamic 64k;

    server backend1.example.com      weight=5;
    server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
    server 192.0.2.1                 max_fails=3;
    server backend3.example.com      resolve;
    server backend4.example.com      service=http resolve;

    server backup1.example.com:8080  backup;
    server backup2.example.com:8080  backup;
}

server {
    location / {
        proxy_pass http://dynamic;
        health_check;
    }
}
参数说明
down不参与负载均衡
backup预留的备份服务器
max_fails允许请求失败的次数
fail_timeout达到允许请求失败的次数后, 会在此时间内不参与负载均衡
max_conns限制最大的接收连接数

负载均衡调度算法

方式说明
轮询按时间顺序逐一分配到不同的后端服务器(默认)
weight加权轮询,值越大,分配到的访问几率越高
ip_hash每个请求按访问IP的校验结果分配,来自同一IP的固定访问一个后端服务器(不推荐)
url_hash按照访问URL的校验结果来分配请求,每个URL定向到同一个后端服务器(不推荐)
least_conn最少连接数,哪个机器连接数少就分发新连接

NGINX 缓存

通常情况下缓存是用来减少后端压力,将压力尽可能的往前推,减少后端压力,提高网站并发延时。

通常在完善架构下会使用 MemcachedRedis 来优化缓存,提高缓存的 IO ,加快取用速率。

语法:

proxy_cache zone | off;
默认参数:proxy_cache off;
字段位置:http, server, location

缓存路径:

proxy_cache_path path [levels=levels]
[use_temp_path=on|off] keys_zone=name:size [inactive=time]
[max_size=size] [manager_files=number] [manager_sleep=time][manager_threshold=time]
[loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off]
[purger_files=number] [purger_sleep=time] [purger_threshold=time];
默认参数:—
字段位置:http

缓存有效期:

proxy_cache_valid [code ...] time;
默认参数:—
字段位置:http, server, location

示例:

proxy_cache_valid 200 302 10m;
proxy_cache_valid 404   1m;

缓存支持断点续传:

location /tmp-test/ {
                proxy_cache tmp-test;
                proxy_cache_valid  200 206 304 301 302 10d;
                proxy_cache_key $uri;
                proxy_set_header Range $http_range;
                proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/;
}

NGINX 实例

用反向代理方式实现动静分离实例

将用户的访问的请求分离,动态请求和静态请求剥离开,保证服务的可用性,即使某一种不可用也不影响另一种。

系统部署服务地址节点
CentOSNGINX192.168.69.112N.1
CentOSNGINX192.168.69.113N.2
CentOSTomcat192.168.69.114N.3

在节点 2 (192.168.69.113)操作:

新建配置文件:

sudo vim /etc/nginx/conf.d/access.conf 

写入以下内容:

server{
        listen 80;
        root /code;
        index index.html;

        location ~ .*\.(png|jpg|gif)$ {
                gzip on;
                root /soft/code/images;
        }
}

准备一个静态资源(png)

sudo wget -O /code/images/nginx.png http://nginx.org/nginx.png

在节点 3 (192.168.69.114)操作:

部署动态环境:

[root@domain ~]# wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-9/v9.0.7/bin/apache-tomcat-9.0.7.tar.gz
[root@domain ~]# mkdir /opt/tomcat
[root@domain ~]# tar xf apache-tomcat-9.0.7.tar.gz -C /opt/tomcat/

编辑动态页面:

[root@domain ~]# vi /opt/tomcat/apache-tomcat-9.0.7/webapps/ROOT/java_test.jsp

写入以下内容:(注意不要使用vim

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<HTML>
    <HEAD>
        <TITLE>JSP Test Page</TITLE>
    </HEAD>
    <BODY>
      <%
        Random rand = new Random();
        out.println("<h1>Random number:</h1>");
        out.println(rand.nextInt(99)+100);
      %>
    </BODY>
</HTML>

在节点 1 (192.168.69.112)操作:

编辑配置文件:

[root@domain ~]# vim /etc/nginx/conf.d/proxy.conf 

写入以下内容:

upstream static {
        server 192.168.69.113:80;
}

upstream java {
        server 192.168.69.114:8080;
}

server {
        listen 80;
        server_name 192.168.69.112;

        location / {
                root /code;
                index index.html;
        }

        location ~ .*\.(png|jpg|gif)$ {
                proxy_pass http://static;
                include proxy_params;
        }

        location  ~ .*\.jsp$ {
                proxy_pass  http://java;
                include proxy_params;
        }

}

编辑网页文件:

[root@domain ~]# vim /code/index.html

写入以下内容:

<html lang="en">
<head>
        <meta charset="UTF-8" />
        <title>测试ajax和跨域访问</title>
        <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
</head>
<script type="text/javascript">
$(document).ready(function(){
        $.ajax({
        type: "GET",
        url: "http://192.168.69.112/java_test.jsp",
        success: function(data) {
                $("#get_data").html(data)
        },
        error: function() {
                alert("fail!!,请刷新再试!");
        }
        });
});
</script>
        <body>
                <h1>测试动静分离</h1>
                <img src="http://192.168.69.112/nginx.png">
                <div id="get_data"></div>
        </body>
</html>

部署完成,现在访问

http://192.168.69.112/java_test.jsp
http://192.168.69.112/nginx.png
http://192.168.69.112/index.html

查看效果

配置请求头解决静态文件跨域问题

在使用 Vue.js 等框架时,打包生成的 CSS 和 JS 文件相对较大,一般会选择对接 CDN 来进行提速,但是此种部署方式会导致一个问题,静态资源跨域问题。

No 'Access-Control-Allow-Origin' header is present on the requested resource

出现这种问题后就需要给服务器添加相应的请求头

location / {  
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
    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';

    if ($request_method = 'OPTIONS') {
        return 204;
    }
} 
参数说明
Access-Control-Allow-Origin服务器默认不允许跨域,配置此参数后即可接受跨域请求
Access-Control-Allow-Headers跨域请求允许的请求头类型
Access-Control-Allow-Methods跨域请求允许的请求方式

这里涉及到一个概念预检请求(Preflight Request),其实上面的配置涉及到了一个 W3C 标准 CROS(Cross-origin resource sharing,跨域资源共享)它的提出就是为了解决跨域请求的。

跨域资源共享(CORS)标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

其实 Content-Type 字段的类型为 application/json 的请求就是上面所说的搭配某些 MIME 类型的 POST 请求,CROS(Cross-origin resource sharing,跨域资源共享)规定,Content-Type 不属于以下 MIME 类型的,都属于预检请求:

application/x-www-form-urlencoded
multipart/form-data
text/plain

所以 application/json 的请求会在正式通信之前,增加一次"预检"请求,这次"预检"请求会带上头部信息

OPTIONS /api/test HTTP/1.1
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

服务器回应时,返回的头部信息如果不包含 Access-Control-Allow-Headers: Content-Type 则表示不接受非默认的的 Content-Type。

通过识别 UA 实现不同浏览器(设备)访问不同页面

示例配置:

http {
...
    upstream firefox {
        server 172.31.57.133:80;
    }
    upstream chrome {
        server 172.31.57.133:8080;
    }
    upstream iphone {
        server 172.31.57.134:8080;
    }
    upstream android {
        server 172.31.57.134:8081;
    }
    upstream default {
        server 172.31.57.134:80;
    }
...
}

server {
    listen       80;
    server_name  www.domain.com;

    location / {
        if ($http_user_agent ~* "Safari"){
        proxy_pass http://dynamic_pools;
        }     

        if ($http_user_agent ~* "Firefox"){
        proxy_pass http://static_pools;
        }

        if ($http_user_agent ~* "Chrome"){
        proxy_pass http://chrome;
        } 
        
        if ($http_user_agent ~* "iphone"){
        proxy_pass http://iphone;
        }
    
        if ($http_user_agent ~* "android"){
        proxy_pass http://and;
        }
    
        proxy_pass http://dynamic_pools;
        include proxy.conf;
        }
    }
}

访问不同路径使用不同服务器

方式一

使用负载资源池

upstream static_pools {
    server 10.0.0.9:80  weight=1;
}
upstream upload_pools {
    server 10.0.0.10:80  weight=1;
}
upstream default_pools {
    server 10.0.0.9:8080  weight=1;
}
 
server {
    listen       80;
    server_name  www.domain.com;
 
#url: http://www.domain.com/
    location / { 
        proxy_pass http://default_pools;
        include proxy.conf;
    }
 
#url: http://www.domain.com/static/
    location /static/ {
        proxy_pass http://static_pools;
        include proxy.conf;
    }
 
#url: http://www.domain.com/upload/
    location /upload/ {
        proxy_pass http://upload_pools;
        include proxy.conf;
    }
}

方式二

使用判断语句实现

if ($request_uri   ~*   "^/static/(.*)$")
{
        proxy_pass http://static_pools/$1;
}
if ($request_uri   ~*   "^/upload/(.*)$")
{
        proxy_pass http://upload_pools/$1;
}
location / { 
    proxy_pass http://default_pools;
    include proxy.conf;
}

NGINX 缓存

缓存过期周期

语法:

proxy_cache_valid [code ...] time;
默认参数: —
字段位置: http, server, location

配置模板:

proxy_cache_valid 200 302 10m;
proxy_cache_valid 404   1m;

缓存的维度

语法:

proxy_cache_key string;
默认参数:proxy_cache_key $scheme$proxy_host$request_uri;
字段位置:http, server, location

配置模板:

proxy_cache_key "$host$request_uri $cookie_user";
proxy_cache_key $scheme$proxy_host$uri$is_args$args;

举个例子

1. 环境准备

系统角色地址
CentOSProxy192.168.31.152
CentOSWeb192.168.31.153

2. 节点准备

建立网页 Demo

# mkdir -p /code/web{1..3}
# for i in {1..3};do echo Code1-Url$i > /code/web1/url$i.html;done
# for i in {1..3};do echo Code2-Url$i > /code/web2/url$i.html;done
# for i in {1..3};do echo Code3-Url$i > /code/web3/url$i.html;done

写入配置文件

# cat >/etc/nginx/conf.d/web_node.conf <'EOF'
server {
        listen 8081;
        root /code/web1;
        index index.html;
}
server {
        listen 8082;
        root /code/web2;
        index index.html;
}
server {
        listen 8083;
        root /code/web3;
        index index.html;
}
EOF

重启服务

sudo systemctl restart nginx

检查服务

sudo ss -lnt | grep 80
State       Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN      0      100         0.0.0.0:8081                          *:*                  
LISTEN      0      100         0.0.0.0:8082                          *:*
LISTEN      0      100         0.0.0.0:8083                          *:*

配置缓存

sudo mkdir /code/cache
sudo vim /etc/nginx/conf.d/proxy_cache.conf

写入如下配置

upstream cache {
    server 192.168.31.153:8081;
    server 192.168.31.153:8082;
    server 192.168.31.153:8083;
}
proxy_cache_path /soft/cache levels=1:2 keys_zone=code_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
        listen 80;
        server_name 192.168.31.152;
        location / {
                proxy_pass http://cache;
                proxy_cache code_cache;
                proxy_cache_valid 200 304 12h;
                proxy_cache_valid any 10m;
                add_header Nginx-Cache "$upstream_cache_status";
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503  http_504;
                include proxy_params;
        }
}
参数说明
proxy_cache存放缓存临时文件
levels按照两层目录分级
keys_zone开辟空间名, 10m:开辟空间大小, 1m可存放8000key
max_size控制最大大小, 超过后Nginx会启用淘汰规则
inactive一小时没有被访问缓存会被清理
use_temp_path临时文件, 会影响性能, 建议关闭
proxy_cache开启缓存
proxy_cache_valid状态码200/304的过期为12h, 其余状态码10分钟过期
proxy_cache_key缓存key
add_header增加头信息, 观察客户端respoce是否命中
proxy_next_upstream出现502-504或错误, 会跳过此台服务器访问下台

缓存测试

[root@domain ~]# curl -s -I http://192.168.31.152/url3.html | grep "Nginx-Cache"
Nginx-Cache: MISS

[root@domain ~]# curl -s -I http://192.168.31.152/url3.html | grep "Nginx-Cache"
Nginx-Cache: HIT

缓存清理

指定页面不缓存,在 server 字段中添加即可。

upstream cache {
    server 192.168.31.153:8081;
    server 192.168.31.153:8082;
    server 192.168.31.153:8083;
}
proxy_cache_path /soft/cache levels=1:2 keys_zone=code_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
        listen 80;
        server_name 192.168.31.152;
        if ($request_uri ~ ^/(url3|login|register|password)) { # 新增内容
                set $cookie_nocache 1;
        }
        location / {
                proxy_pass http://cache;
                proxy_no_cache $cookie_nocache $arg_nocache $arg_comment; # 新增内容
                proxy_no_cache $http_pargma $http_authorization; # 新增内容
                proxy_cache code_cache;
                proxy_cache_valid 200 304 12h;
                proxy_cache_valid any 10m;
                add_header Nginx-Cache "$upstream_cache_status";
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503  http_504;
                include proxy_params;
        }
}

缓存日志修改默认日志格式

[root@domain pinghsu]# cat /etc/nginx/nginx.conf 
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$http_user_agent' '$request_uri' '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"' '"$upstream_cache_status"';

附录

参考链接

本文撰写于一年前,如出现图片失效或有任何问题,请在下方留言。博主看到后将及时修正,谢谢!
禁用 / 当前已拒绝评论,仅可查看「历史评论」。
  1. avatarImg

    这个网页底下的效果是怎么实现的?求教

    Chrome 69.0 Windows 10
    IP 属地 未知
  2. avatarImg

    很好的教程,收藏了!

    Chrome 69.0 Windows 10
    IP 属地 未知