nginx学习记录

编程知识 更新时间:2023-04-16 02:34:25

注:本文内容来源于网络

1. nginx 基本介绍

 nginx使用可扩展事件驱动架构,相对传统的过程驱动框架,需要的内存更少,并且在并发连接增多时,使内存使用更加可预测。传统WEB体系中,每个客户端连接都作为一个单独的进程或线程处理,随着网站的流行度增加,并发连接数量增加,Web服务器将减慢,对客户端的响应也会大大变慢,在线程/进程切换时运行新的上下文时,将会消耗额外的内存和时间,影响性能。nginx为了实现10倍以上的性能,优化服务器资源使用,同时也能扩展和支持网站的动态增长,nginx是最知名的模块化、事件驱动、异步、单线程WEB服务器和WEB代理之一。

 作为高性能Web和反向代理服务器,有如下特性:

  • 可以作为WEB服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率,能够支持高达 50,000 个并发连接数的响应;
  • 可以作为负载均衡(多个服务器对外提供单一的网络服务)服务器:Nginx 既可以在内部直接支持 Rails 和 PHP,也可以支持作为 HTTP代理服务器 对外进行服务,Nginx 用 C 编写, 不论是系统资源开销还是 CPU 使用效率都比 Perlbal 要好的多;
  • 可以作为邮件代理服务器:Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发的目的之一);

 Nginx 安装简单,配置文件简洁(还能够支持perl语法),Bugs少,启动也容易,并且几乎可以做到7*24不间断运行,即使运行数个月也无须重启,而且可以在不间断服务的情况下进行软件版本的升级。

 处理并发连接方面,传统的基于进程或线程的模型涉及使用单独进程或线程处理每个连接,并阻止网络或输入/输出操作。 根据应用,在内存和CPU消耗方面可能效率会比较低。产生一个单独的进程或线程需要准备一个新的运行时环境,包括分配堆和堆栈内存,以及创建新的执行上下文,额外的CPU时间也用于创建这些项目,这可能会导致由于线程需要在过多的上下文切换上的转机而导致性能下降,所有这些并发症都表现在较老的Web服务器架构(如Apache)中。从一开始nginx就是一个专门工具,可以实现更高性能、更密集和经济地使用服务器资源,同时实现网站的动态发展,还采用了不同的模式。 它实际上受到各种操作系统中高级事件机制的不断发展的启发,演变成了一个模块化、事件驱动、异步、单线程的非阻塞架构的nginx代码基础。nginx大量使用复用和事件通知,并有专门用于分离进程的特定任务。 连接在有限数量的单线程进程称为工人(worker)的高效运行循环中处理。在每个工作(worker)中,nginx可以处理每秒数千个并发连接和请求。

【注意】

 一般情况都是在Linux中部署nginx,nginx的Windows版本不是一个功能完整的端口,nginx和Windows内核架构有一些不能很好地交互的局限性,在Windows版本的nginx已知问题包括并发连接数量低得多,性能下降,无缓存,无带宽监管,可能将来的版本会不断完善。

2. 安装

 nginx有多个版本,最简单的就是win版本直接解压运行即可,最复杂的是从源码构建,如果需要一些特殊的功能,在包和端口不可用的情况下,也可以从源代码编译来安装,源码构建的安装方式虽然复杂,但很灵活。通常直接如下安装即可:
1.更新系统和依赖包安装

 我本地是CentOS 7版本,首先进行系统升级:

yum update

关于升级命令,作一个区分:

# 升级所有包,改变软件设置和系统设置,系统版本内核都升级
yum -y update

# 升级所有包,不改变软件设置和系统设置,系统版本升级,内核不改变
yum -y upgrade

然后安装依赖包:

yum -y install gcc gcc-c++ autoconf automake libtoll make cmake
yum -y install zlib zlib-devel openssl openssl-devel pcre-devel

2.下载nginx包

 可以到官网先下载在上传,或者直接通过wget直接获取,这里是已最新的stable版本1.14.0版本为例进行下载编译:

# 目录可以随意指定
cd /usr/local/src/
# 下载nginx
wget -c http://nginx/download/nginx-1.14.0.tar.gz
# 解压
tar zxvf nginx-1.14.0.tar.gz

3.创建nginx用户及用户组

 切换至root用户创建用户和用户组:

# 创建一个名字为`nginx`的用户组
groupadd nginx

# 创建一个用户,名字为nginx(第一个表示组名,第二个表示创建的用户名),-M参数用户不为创建的新用户创建新的用户目录,将用户nginx拉到用户组nginx中
useradd -g nginx -M nginx

修改bash登录信息:

vim /etc/passwd

# 将nginx的信息修改为下面的,新添加的用户一般在该文件的最后一行
nginx:x:1003:1003::/home/nginx:/sbin/nologin

4.编译配置安装

 进入刚刚解压后的源码目录:

[root@10 nginx-1.14.0]# pwd
/usr/local/src/nginx-1.14.0

# 下面命令中的反斜杠表示继续换行,linux不会直接执行未完成的命令
[root@10 nginx-1.14.0]# ./configure --prefix=/usr/local/nginx \
> --pid-path=/usr/local/nginx/run/nginx.pid \
> --with-http_ssl_module \
> --user=nginx \
>  --group=nginx \
> --with-pcre \
> --without-mail_pop3_module \
> --without-mail_imap_module \
> --without-mail_smtp_module

其中--prefix=/usr/local/nginx表示将nignx内容安装到指定目录下,这里指定的是/usr/local/nginx目录,下面进行编译安装:

[root@10 nginx-1.14.0]# make
[root@10 nginx-1.14.0]# make install

这样以后,nginx就被安装在了/usr/local/nginx这个目录中,验证一下编译安装的nginx信息:

[root@10 nginx-1.14.0]# /usr/local/nginx/sbin/nginx -v

# 得到如下信息
nginx version: nginx/1.14.0

3. 配置nginx

 nginx默认是监听的是浏览器的80端口(可根据需求改成对应的端口),即配置中的:

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

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

在修改后nginx也提供了验证配置文件是否合法的命令:

# 进入nginx的sbin目录,验证配置文件
./nginx -t

# 验证成功的结果
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

# 验证失败的结果,改了一个不存在的端口号
nginx: [emerg] invalid port in "80123456" of the "listen" directive in /usr/local/nginx/conf/nginx.conf:36
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

验证配置文件成功后,启动nginx:

# 前提:进入nginx的sbin文件目录下启动
./nginx

# 查看nginx是否启动成功,直接查看nginx相关的端口
ps -ef | grep nginx
# 结果如下,说明启动成功了
root      80298      1  0 16:30 ?        00:00:00 nginx: master process ./nginx
nginx     80299  80298  0 16:30 ?        00:00:00 nginx: worker process
root      80322  73145  0 16:30 pts/1    00:00:00 grep --color=auto nginx

nginx的常用命令,前提都是先进入nginx的sbin目录下:

# 1. 启动
./nginx
# 2. 有序停止nginx,直接用上述中的主进程号(即master process),上面是80298
kill -QUIT 80298
# 3. 快速停止nginx
kill -TERM 80298
# 4. 强制停止Nginx
pkill -9 nginx
# 5. 重启nginx,会重新载入配置文件
./nginx -s reload

除了通过上述查看进程的运行情况外,最直观的方式是直接用curl或者打开浏览器,输入本机IP查看,由于默认监听80端口,所以会有如下的结果:

[root@10 sbin]# curl -X GET localhost

# 结果如下
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx/">nginx</a>.<br/>
Commercial support is available at
<a href="http://nginx/">nginx</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

上述都是针对Linux环境下的运行,对于windows版本的Nginx基本操作更加简单,如下:

  1. nginx 启动会一闪而过: start nginx 或者直接双击nginx.exe;
  2. 验证配置文件是否正确:nginx -t
  3. 改动配置文件后需要重新加载配置文件:nginx -s reload
  4. 查看nginx是否启动成功:tasklist /fi "imagename eq nginx.exe"
  5. 关闭nginx服务同样一闪而过: nginx -s stop(快速停止) 或 nginx -s quit(完全有序的停止)。

4. Nginx基本点

 nginx有一个主进程和多个worker进行工作,主进程的目的是读取和评估配置,并维护woker进程,worker进程对请求进行实际处理,nginx采用基于事件的模型和依赖于操作系统的机制来有效地在worker进程之间分配请求,具体worker进程的数量可在配置文件中定义,并且可以针对给定的配置进行修改,或者自动调整到可用CPU内核的数量。nginx的配置文件为根目录下的conf/nginx.conf

 在nginx启动后,可以通过-s参数调用可执行文件来控制它,语法如下:

./nginx -s signal

其中signal可以为:

  • stop:快速关闭服务;
  • quit:正常关闭服务(会等待工作进程服务完成当前请求再停止nginx进程);
  • reload:重新加载配置文件;
  • reopen:重新打开日志文件;

其中reload基本在配置改动后会使用该命令进行会让nginx重新加载配置:当主进程收到要重新加载配置的信号,它将检查新配置文件的语法有效性,并尝试应用其中提供的配置。如果新配置是成功的,主进程将启动新的工作进程,并向旧的工作进程发送消息,请求它们关闭,旧的worker进程接收关闭命令,停止接受新连接;否则,主进程回滚更改,并继续使用旧配置,并继续维护当前请求,直到所有这些请求得到维护。

4.1 利用nginx提供静态内容服务

 如果一个网站需要提供静态文件,这些静态文件由本地不同的目录提供,比如/data/html/data/images,那就需要在配置文件中进行指定,使用2个位置块在http块内设置服务器块,在/data/html下放一个html文件index.html,在data/images中放一个图片文件,下面直接干这样一个事儿:

# 创建 2 个目录
[root@localhost ~]# mkdir -p /data/html
[root@localhost ~]# mkdir -p /data/images

# 进入images目录下载一个百度一下的图片
cd /data/images
wget https://www.baidu/img/bd_logo1.png

# 在html目录中创建一个html文件,内部写一个 <h2> New Static WebSite Demo </h2>
vim ../html/index.html

# 进入nginx的conf目录,复制一份nginx的配置文件然后更改
cp -p nginx.conf nginx.conf.back

vim nginx.conf

然后将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 {
  server {
    location / {
      root /data/html;
    }
    location /images/ {
      root /data;
    }
  }
}

最后启动nginx验证:

./nginx

curl -X GET localhost

# 结果如下:
<h2>New Static WebSite Demo.</h2>

# 查看图片,会出现一片二进制流乱码,可以用浏览器打开
curl -X GET localhost/images/bd_logo1.png

【注意】上述访问index.html直接使用的是curl -X GET localhost是因为html的文件名为index.html,nginx可以自动找到,但是如果是其他的名字需要显式的写出来,否则是找不到的,比如后面查看图片时就必须指出文件名,否则是找不到路径的。

上述配置中,http块中包含了两个服务块:

http {
  server {
    location / {
      root /data/html;
    }
    location /images/ {
      root /data;
    }
  }
}

对于上述http块中的server块中的location基本语法为alias file-path\directory-path(即别名 文件路径),location指令指定了用于指示位置的路径,它可能看起来类似于root这样的形式,但文档根目录不会更改,只是用于请求的文件系统路径(说的简单点就是文件本身路径不会变,只是请求的路径变了),比如:

location  /i/ {
 alias   /spool/w3/images/;
}

那么当请求/i/top.gif时,实际请求的是/spool/w3/images/top.gif这个文件,注意,这里是可以在替换的路径中使用变量的。alias指令不能在指定的正则表达式的location内部使用,如果想使用就必须使用rewriteroot的组合。

配置文件一般可以包括服务器监听的端口和服务器名称区分不同的server块,当nginx决定哪个服务器处理请求后,它会根据服务器块内部定义的location指定的参数测试请求头中指定的URI,该location块指定与请求中的URI相比较的/前缀,对于匹配请求,URI将被添加到root指定的路径(即/data/html),以形成本地文件系统上所请求文件的路径,如果有几个匹配的location块,nginx将选择具有最长前缀来匹配location块,上面的location块提供最短的前缀长度为1,因此只有当所有其他location块不能提供匹配时,才会使用该块。

注意】关于aliasroot

对于上述location中配置的aliasroot做一个简单的说明,其实看命名就可以看出来,alias是别名,用来替换URI的,比如上述的配置

location  /i/ {
 alias   /spool/w3/images/;
}

那么此时请求/i/top.gif时,直接用/spool/w3/images/代替/i/,所以实际请求的是/spool/w3/images/top.gif这个文件;

root是指定根目录,location出现的URI是追加其后面的,如上述配置改为:

location  /i/ {
 root   /spool/w3/images/;
}

再去请求/i/top.gif时,那么实际请求的文件是/spool/w3/images/i/top.gif,简单点说就是alias是替换,而root是追加。

最后还有一个坑,alias指令指定的路径后面一定要加/,不然基本就废了,请求该路径时Nginx给的状态码是403,而不是404,很难排查(会疯掉……),记录一下(我踩了多次,找不到原因,偶然翻到文档才解决◑﹏◐)

4.2 利用Nginx设置简单的代理服务器

 将nginx设置为代理服务器是一种较为常见的用途,它可以作为一个接收请求的服务器,将其传递给代理服务器,从代理服务器中检索响应,并将其发送给客户端,和上面的一样在/data目录下创建一个up1/index.html文件,里面随便放点东西,比如<h1>Hello Proxy</h1>,然后在nginx的配置文件中已有的http块中加上如下的server块:

server {
    # nginx默认使用的80端口,上面没有配置访问时无需指定端口号即可访问,这里指定8080端口
    listen 8080;
    root /data/up1;

    location / {
    }
}

在访问时直接curl -X GET localhost:8080这里必须指定端口号,否则会被映射到原来上一个静态内容服务的Demo,但是现在需要将nginx搞成一个代理服务器,直接修改静态内容服务Demo中配置的第一个server块,将location内部的root /data/html修改为proxy_pass http://localhost:8080;,此时重新载入nginx配置文件后,再次访问localhost将不再是New Static WebSite Demo,而是Test proxy!,因为在80端口原来的/被代理到了8080端口,而8080端口的/是映射到Test proxy的html文件的。然后将之前映射的图片的location块内容修改为:

location ~ \.(gif|jpg|png)$ {
    root /data/images;
}

上述的location配置的时候使用了正则表达式,此时nginx将过滤以.gif.jpg以及.png结尾的URI(其他的结尾字符的路径将不会过滤出来,直接会404),其中~表示正则表达式的开始标识符(它没有实际意义),而$则表示正则表达式结尾的标识符,也没有实际意义。

注意proxy_pass语法

主要是通过该参数指定的路径一定要完整(包含所有URI完整字符串,包括\字符),比如上述的配置,如果指定为proxy_pass http://localhost:8080,那么是无法访问到Test proxy页面,直接404,这玩意儿也很细致。

4.3 利用Nginx设置Fast CGI代理

 nginx可用于将请求路由到使用各种框架和PHP等编程语言构建的应用程序的FastCGI服务器,这里最基本的配置是直接用fastcgi_param代替上述基本代理配置中的proxy_pass参数即可,在PHP中使用SCRIPT_FILENAME参数用于确定脚本名称,QUERY_STRING用于传递请求参数。

5. Nginx的进程和运行时控制

 NGINX有一个主进程和一个或多个worker进程。 如果启用缓存,缓存加载程序和缓存管理器进程也将在启动时运行。主程序的主要目的是读取和评估配置文件以及维护wroker进程。worker进程执行请求的实际处理,NGINX依赖于操作系统的机制来有效地在worker进程之间分配请求,工作进程的数量可在nginx.conf配置文件中定义,可以针对给定的配置进行修复,或者自动调整为可用CPU内核数。

 要重新加载配置文件,可以停止或重新启动NGINX,或者发送信号到主进程。 可以使用-s参数运行nginx命令(调用NGINX可执行文件)来发送信号,这点在第4章节中已有涉及,语法:
./nginx -s signal
其中signal可以为:

  • stop:快速关闭服务;
  • quit:正常关闭服务(会等待工作进程服务完成当前请求再停止nginx进程);
  • reload:重新加载配置文件;
  • reopen:重新打开日志文件;

当然杀死程序也可使用,默认情况下,主进程的进程ID写入到nginx/logs/nginx.pid或者/var/run/nginx.pid文件中(但是我并没有发现),该文件的名称可以在配置文件中使用pid指令进行更改(我更改后直接报错)。nginx可以使用信号控制,主程序支持以下信号:

  • TERMINT - 快速关闭
  • QUIT - 正常关闭
  • HUP - 改变配置,跟上改变的时区(仅适用于FreeBSD和Linux),使用新配置启动新的工作进程,正常关闭旧的工作进程
  • USR1 - 重新打开日志文件
  • USR2 - 升级可执行文件
  • WINCH - 正常关闭工作进程

个别worker进程可以使用信号控制(尽管这不是必需的),支持的信号有:

  • TERMINT - 快速关闭
  • QUIT - 正常关闭
  • USR1 - 重新打开日志文件
  • WINCH - 调试异常终止(需要启用debug_points)

【注】关于nginx.pid文件的位置以及配置

1.上述网络上所说的两个位置上我并没有找到nginx.pid文件,最后发现我本地的文件位置为/usr/local/nginx/run/nginx.pid,如果是在找不到,可以直接locate nginx.pid搜一下,如果还找到,先更新一下库update db,然后再locate nginx.pid搜索。

2.关于在main环境中使用pid指令配置nginx.pid文件的位置和名字时,使用./nginx -s reload指令重载配置文件时会出现nginx: [error] open() "/data/nginx.pid" failed (2: No such file or directory)错误,是因为nginx被停止时,原先的nginx.pid被删除,而reopenreload命令需要通过nginx.pid文件获取主进程号,如果不存在,就报错了。所以如果需要自定义配置nginx.pid文件信息,正确的做法是先停止nginx服务,然后再改配置文件,最后启动,下面是简单配置示例:

# 先停止nginx服务
./nginx -s stop

# 在nginx.conf中添加配置
pid /data/nginx.pid;

# 启动nginx
./nginx

通过ps -ef | grep nginx指令查看nginx的主进程的id,然后通过查看配置的nginx.pid文件,可以发现两者是一致的。

更改配置文件

 为了使nginx重新读取配置文件,应将HUP信号发送到主进程,因为主进程是负责维护配置文件的,它会首先检查语法的有效性,然后尝试应用新的配置,即打开日志文件和新的监听套接字,如果失败则回滚并继续使用旧配置;如果操作失败,它将启动新的worker进程,并向旧的worker进程发送消息,请求它们正常关闭。旧的worker进程模切监听套接字,并继续监听旧客户端服务,在所有的客户端被服务后,旧的worker进程将会被关闭。

循环日志文件

 要循环日志文件,首先需要重命名,之后USER1信号发送到主进程,然后主进程将重新打开所有当前打开的日志文件,并将其分配给正在运行的工作进程的非特权用户作为所有者。成功重新打开后,主程序关闭所有打开的文件,并将消息发送到工作进程,要求他们重新打开文件。工作进程也会打开新文件并立即关闭旧文件。因此,旧文件几乎立即可用于后处理,如压缩。

6. Nginx的配置文件

 上面已有涉及部分nginx的配置文件,位置为nginx/conf/nginx.conf,进入nginx的安装目录(而非源码目录)/usr/local/nginx,查看nginx的配置文件:

# 查看nginx的配置文件
vim conf/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        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

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

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}

网络上有人对nginx的配置文件做了区域划分的很清晰,如下:

nginx的配置文件是由指令及其参数组成,简单(单行)指令各自以分号结尾;其他指令作为“容器”,将相关指令组合在一起,将其包围在花括号{xxx}中。官方一点就是配置文件由指令控制模块组成,指令控制模块分为简单指令和块指令:

  • 简单指令:由空格分隔的名称和参数组成,并以分号;结尾,比如:error_log logs/error.log info;
  • 块指令:具有与简单指令相同的结构,但不是以分号结尾,而是以大括号{}包围的一组附加指令结束;
  • 上下文:如果块指令在大括号内部有其他指令,则称为上下文,如eventshttpserverlocation
  • 主上下文:配置文件中放置在任何上下文之外的伪指令都被认为是主上下文,eventshttp指令驻留在主上下文中,serverhttp中的,而locationhttp块中;
  • 注释:在行首使用#标记即表示该行为注释;

几个顶级指令(称为上下文)适用于将不同流量类型的指令组合在一起:

  • events – 一般连接处理;
  • http – HTTP协议流量;
  • mail – Mail协议流量;
  • stream – TCP协议流量;

指定在这些上下文之外的指令是在主上下文中(即main环境),在每个流量处理上下文中,可包括一个或多个服务器server上下文来定义控制请求处理的虚拟服务器,可以在服务器环境中包含的指令根据流量类型而有所不同。对于HTTP流量(http上下文),每个服务器指令控制对特定域或IP地址上的资源请求的处理,服务器上下文中的一个或多个位置location上下文定义了如何处理特定的URI集合。对于邮件和TCP流量(mailstream上下文),服务器指令各自控制到达特定TCP端口或UNIX套接字的流量处理,下面是一个简单的配置实例:

user nobody; # main 上下文的指令

events {
    # 连接处理的配置
}

http {

    # 对于特定HTTP的配置并影响所有虚拟服务器

    server {
        # HTTP虚拟服务器1的配置

        location /one {
            # /one 路径的映射配置
        }

        location /two {
            # /two 路径的映射配置
        }
    }

    server {
        # HTTP虚拟服务器2的配置
    }
}

stream {
    # 对于特定TCP的配置并影响所有虚拟服务器

    server {
        # TCP虚拟服务器1的配置
    }
}

对于大多数指令,在另一个上下文(子上下文)中定义的上下文将继承父级中包含的伪指令的值,要覆盖从父进程继承的值,请在子上下文中包含该指令。

对于proxy_set_header field value,允许将字段重新定义或附加到传递给代理服务器的请求标头,默认是:

proxy_set_header Host $proxy_host;
proxy_set_header Connection close;

其中value可以包含文本和其他的组合,当且仅当在当前级别上没有定义proxy_set_header指令时,这些指令才从先前级别继承,默认情况下,仅重新定义两个字段:

proxy_set_header Host       $proxy_host;
proxy_set_header Connection close;

如果允许缓存,来自原始请求的头部字段If-Modified-SinceIf-Unmodified-SinceIf-None-MatchIf-MatchRangeIf-Range将不会传到代理服务器。可以像这样传递未更改的host请求头字段:

proxy_set_header Host       $http_host;

但是如果这个字段没有出现在客户端的请求中,那将将没有东西传过来,在这种情况下使用$host变量会更好一些,如果这个字段不存在,它的值等于请求头host字段中的服务器名字或者是主服务器的名字:

proxy_set_header Host       $host;

此外,服务的名字可以和代理服务器的端口一起传过来:

proxy_set_header Host       $host:$proxy_port;

如果header头字段中为空字符串,那么这个字段将不会传给代理服务器:

proxy_set_header Accept-Encoding "";

7. Nginx配置Web服务器

 在高层次上,将NGINX配置作为Web服务器有一些问题需要了解,定义它处理哪些URL以及如何处理这些URL上的资源的HTTP请求。在较低层次上,配置定义了一组控制对特定域或IP地址的请求的处理的虚拟服务器。用于HTTP流量的每个虚拟服务器定义了称为位置的特殊配置实例,它们控制特定URI集合的处理。每个位置定义了自己的映射到此位置的请求发生的情况。NGINX可以完全控制这个过程。每个位置都可以代理请求或返回一个文件。此外,可以修改URI,以便将请求重定向到另一个位置或虚拟服务器。此外,可以返回特定的错误代码,也可以配置特定的页面以对应于每个错误代码。

1. 设置虚拟服务器

 nginx配置文件至少包括一个服务器指令来定义虚拟服务器,当nginx处理请求时,他首先选择提供请求的虚拟服务器,虚拟服务器由http上下文中的服务器指定定义,如:

http {
    server {
        # Server configuration
    }
}

通过上面几个例子知道,其实http上下文中可以放多个server块定义多个虚拟服务器,server块配置通常包含包括一个listen指令(如果没有,那就是默认本地80端口),如果只有IP没有端口,那也是对应IP的80端口,用于指定服务器侦听请求的IP地址和端口,IPv4和IPv6地址均被接受,如:

server {
    # 指定监听IP为127.0.0.1和端口8080
    listen 127.0.0.1:8080;
    # The rest of server configuration
}

如果有多个服务器与请求的IP地址和端口相匹配,则NGINX将根据服务器块中的server_name指令测试请求的主机头域,server_name可以是完整精确的名字、也可以是通配符(一个字符串,开头或结尾或头尾都包含*)或者正则表达式(使用perl语法书写正则,开始之前都要使用~标识符,如果指定以xx结尾,那需要写成xx$),如:

server {
    listen      80;
    server_name example.org www.example.org;
    ...
}

如果匹配主机头几个名称,则NGINX通过按以下顺序搜索名称并使用其找到的第一个匹配来选择一个:

  • 确切的名字(完整准确的名称);
  • 以星号开头的最长通配符,例如:*.example
  • 以星号结尾的最长通配符,如:mail.*
  • 第一个匹配正则表达式(按照出现在配置文件中的顺序);

如果主机头字段与服务器名称不匹配,则NGINX会将请求路由到请求到达端口的默认服务器。 默认服务器是nginx.conf文件中列出的第一个服务器(本地80端口),除非将listen_server参数包含在listen指令中以明确指定服务器为默认值。

server {
    listen    80    default_server;
    ...
}

下面配置了2个虚拟服务器,对应的域名为vhost1vhost2,其中vhost1网站的主目录在/data/html/vhost1vhost2网站的主目录在/data/html/vhost2

server {
    listen       80;
    server_name vhost1.com www.vhost1.com;
    index index.html index.html;
    root  /data/html/vhost1;
    access_log  /var/log/vhost1.com.log;
}

server {
    listen       80;
    server_name vhost2.com www.vhost2.com;
    index index.html index.html;
    root  /data/html/vhost2;
    access_log  /var/log/vhost2.com.log;
}

2. 配置位置

 NGINX可以根据请求URI向不同的代理发送流量或提供不同的文件,这些块是使用server指令中的location指令来定义。例如,定义三个location块,以指示虚拟服务器向一个代理服务器发送一些请求,将其他请求发送到不同的代理服务器,并通过从本地文件系统传递文件来提供其余请求,NGINX测试根据所有location指令的参数请求URI,并应用匹配location中定义的指令,在每个location块内,通常可能(除了一些例外)放置更多的location指令以进一步细化特定组请求的处理(即location中嵌location),location指令有两种类型参数:

  • 前缀字符串(路径名):这种情况必须匹配的URI必须以前缀字符开头;
  • 正则表达式:如果前缀字符串方式匹配不到才会使用正则表达式搜索位置,以~~*加空格的方式开头,前者区分大小写,后者不区分大小写;

如果location指定为前缀字符串,如:

location /some/path/ {
    ...
}

那它可以匹配到/some/path/开头的URI,如/some/path/xxx.html,但它不能匹配/xxx/some/path/,因为/some/path/没有出现在开头位置(这里的开头位置是指除了主机host之后的位置)。当然将location使用正则表达式指定,如:

location ~ \.html? {
    ...
}

正则表达式开始之前需要指定是否区分大小写,如果区分大小写,在正则表达式之前使用~标识,如果不区分大小写则使用~*标识,如:

# 区分大小写,匹配包含 .html 字符串的任何位置
location ~ \.html? {
    ...
}

要找到最符合URI的位置,NGINX首先将URI与前缀字符串的位置进行比较,然后用正则表达式搜索位置(默认是优先使用前缀字符串进行匹配的),除非使用^~修饰符对正则表达式给予更高的优先级,在前缀字符串中,NGINX选择最具体的字符串(也就是最长和最完整的字符串),下面是选择处理请求的位置的确切逻辑:

  • 测试所有URI的前缀字符串;
  • =(等号)修饰符定义了URI和前缀字符串完全匹配,如果找到完全匹配,则搜索停止;
  • 如果^~(插入符号)修饰符预先添加最长匹配前缀字符串,则不会检查正则表达式;
  • 存储最长匹配的前缀字符串;
  • 根据正则表达式测试URI;
  • 断开第一个匹配的正则表达式并使用相应的位置;
  • 如果没有正则表达式匹配,则使用与存储的前缀字符串相对应的位置;

=修饰符的典型用例是/(正斜杠)的请求,如果请求/是频繁的,则指定=/作为location指令的参数加速处理,因为搜索在第一次比较匹配成功后就停止搜索了,如:

location = / {
    ...
}

location上下文可以包含定义如何解析请求的指令,提供静态文件或将请求传递给代理的服务器,在以下示例中,匹配第一个location上下文的请求将从/data/images目录中提供文件,并将匹配第二个位置的请求传递给承载www.example域内容的代理服务器:

server {
    location /images/ {
        root /data;
    }

    location / {
        proxy_pass http://www.example.com;
    }
}

root指令指定要在其中搜索要提供的静态文件的文件系统路径,与该位置相关联的请求URI将附加到路径,以获取要提供的静态文件的全名,在上面的示例中,要响应/images/logo.png的请求,NGINX提供服务器本地实际对应文件是:/data/images/logo.pngproxy_pass指令将请求传递给使用配置的URL访问代理服务器,然后将代理服务器的响应传回客户端,在上面的示例中,所有以/images/开头的URI的请求都将被传递给代理的服务器。

3. 使用变量

 可以使用配置文件中的变量,使NGINX进程的请求根据定义的情况而有所不同而进行赋值,变量是在运行时计算的命名值,用作指令的参数,一个变量由它的名字开头的$(美元)符号表示,变量根据NGINX的状态定义信息,例如正在处理的请求的属性,有许多预定义的变量,如核心HTTP变量,可以使用setmapgeo指令定义自定义变量,大多数变量在运行时计算的,并包含与特定请求相关的信息,例如,$remote_addr包含客户端IP地址,$uri保存当前的URI值。

4. 返回特定的状态码

 一些网站URI需要立刻返回具有特定错误或重定向代码的响应,例如当页面被暂时移动或永久移动时,最简单的方法是使用return指令,例如返回未找到的404状态码:

location /wrong/url {
    return 404;
}

返回的第一个参数是响应代码,可选的第二个参数可以是重定向的URL(如果状态码指定为301302303307)或在响应体中返回文本,例如:

location /permanently/moved/url {
    return 301 http://www.example.com/moved/here;
}

返回指令可以包含在locationserver上下文中。

【注意】

上述如果指定的状态码是上述的状态码,即301302303307,那么此时再加一个重定向的URL,如上面的http://www.example/moved/here,那么在请求localhost/permanently/moved/url时,会直接重定向到指定的URL(即http://www.example/moved/here),如果是其他的状态码,不会重定向,会在请求的页面上将指定的URL显示出来,如下(定义的状态码为415):

5. 重写URI请求

 可以通过rewrite指令在请求处理期间多次修改请求URI,(这个指令也是见名之意,重写就是rewrite),该指令具有两个必选参数和一个可选参数:

  • 第一个是必选参数,表示请求URI必须匹配的正则表达式(第一个参数);
  • 第二个是必选参数,表示用于替换匹配URI的URI(第二个参数);
  • 第三个是可选参数,表示可以停止进一步重写指令的处理或发送重定向(代码301或302)的标志。

简单点说,就是URI匹配的时候是根据第一个参数匹配的,如果匹配上了,就将URI映射到第二个参数表示的地址上,其实和之前的location一样,只不过跟在location后面的URI字符串又被放在第一个参数中进行更为详细的匹配,最后将它映射到实际的地址上(第二个参数),例如:

location /users/ {
    rewrite ^/users/(.*)$ /show?user=$1 break;
}

上述配置中定义的三个参数依次为^/users/(.*)$/show?user=$1break。在locationserver上下文中可以包含多个rewrite指令,nginx按照它们发生的顺序逐个执行指令,选择上下文时,server上下文中的rewrite指令将会被执行依次,如果URI与其中一个匹配的上,则在处理所有定义的rewrite指令后,将搜索新的location块,下例显示了与返回指令相结合的rewrite指令:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  last;
    return  403;
    ...
}

上例配置区分了两组URI,其中$1$2表示的是正则匹配到的第一个位置和第二个位置的内容(如果正则中使用(xx)括号包裹了,那么$取到的内容不仅仅是匹配的内容,而是整个括号中的内容),对于点号.使用转义字符表示为\.,具体的匹配:诸如/download/some/media/file.jk匹配到第一个URI,然后映射到实际地址为/download/some/mp3/file.mp3,由于最后一个标志,所以跳过后续指令(这里的后续指令是指本次所在的rewrite指令,拿到替换后的URI就继续下面的操作),但nginx继续处理该请求,该请求现在具有不同的URI,类似的,/download/some/audio/file.op的URI被替换为/download/some/mp3/file.ra,如果URI与rewrite指令不匹配,nginx将返回403状态码给客户端。有两个中断处理重写指令的参数(即第三个参数):

  • last:停止执行当前服务器或位置上下文中的重写指令,但是NGINX会搜索与重写的URI匹配的位置,并且应用新位置中的任何重写指令(URI可以再次更改,往下继续匹配);
  • break:像break指令一样,在当前上下文中停止处理重写指令,并取消搜索与新URI匹配的位置。新位置(location)块中的rewrite指令不执行;

下面具体看一下重写URI指令模块中的详细指令:

5.1 break指令

break用于停止处理当前rewrite模块,可用于serverlocationif中,如果在location中使用,那在该位置继续进一步处理请求,下面是一个简单的示例:

if ($slow) {
    limit_rate 10k;
    break;
}

5.2 if指令

if指令通常会指定一个condition,完整语法为if (condition) { ... },可用于serverlocation中,如果这个conditiontrue,那大括号内不指令块将会执行,相应的请求也会被分配到if指令内部的配置,同样的,if指令会从先祖级别配置那里继承一些常规配置(如果没有被复写的话),指令中的condition可以是下面的一些值:

  • 变量名、如果为0j或者空字符串就为false(在1.0.1版本之前,任何以0开头的字符串都认为是false);
  • 使用=!=运算符比较变量和字符串;
  • 使用正则表达式匹配的变量(~区分大小写,~*不区分大小写,正则可以使用$1……$9获取匹配到的字符串,也可以使用取反操作!~!~*,如果正则表达式中包含了};字符,那整个表达式应该用单引号或双引号括起来);
  • 检查文件是否存在的操作(使用-f!-f命令);
  • 检查目录是否存在的操作(使用-d!-d命令);
  • 检查文件、目录、链接是否存在的操作(使用-e!-e命令);
  • 检查可执行文件(使用-x!-x操作);

比如:

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}

if ($request_method = POST) {
    return 405;
}

if ($slow) {
    limit_rate 10k;
}

if ($invalid_referer) {
    return 403;
}

$invalid_referer嵌入变量的值可以由valid_referers指令进行指定。

5.3 return指令

 停止处理并返回给客户端一个特定的状态码,非标准代码444在不发送响应头的情况下关闭连接,return指令的基本语法为:

return code [text];
return code URL;
return URL;

主要用于serverlocationif中,从0.8.42版本开始,可以在return指令中指定重定向的URL(但仅限于对于指定的状态码为301302303307308的情况,仅针对nginx 1.14版本)或响应体(响应体中包含的文本和重定向的URL可以包含变量)。

5.4 rewrite指令

 基本功能就是对匹配的URI进行替换,语法为rewrite regex replacement [flag];,主要用于serverlocationif中,先用第一个参数(regex),即一个正则表达式对URI进行匹配(注意,如果正则表达式中存在};字符,那需要使用单引号或双引号将整个表达式包裹起来),如果匹配成功就利用第二个参数replacement进行URI的重写,可以有多个rewrite指令,按照配置的顺序依次执行,如果有第三个参数flag(可选参数),可以使用标志终止对指令的进一步处理。如果第二个参数replacement是以http://https://$scheme开头,那么处理停止,并将重定向返回给客户端。第三个参数是可选参数,是一个标志参数,可以是以下的内容:

  • last:停止处理当前的ngx_http_rewrite_module指令集,并开始搜索与更改的URI匹配的新位置;
  • break:与break指令一样,停止处理当前的ngx_http_rewrite_module指令集;
  • redirect:返回一个带有302状态码的临时重定向,通常是在replacement参数不是以http://https://$scheme开头的情况;
  • permanent:返回带有301状态码的永久重定向;

完整的重定向URL可以根据具体的请求方案(即$scheme,应该就是请求的协议,比如httphttps)和server_name以及重定向的端口来决定,比如:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  last;
    return  403;
    ...
}

上述的rewrite是直接放到了server块中的,如果将rewrite指令放到/download/location中的话,那上面的last标记就要换成break了,否则nginx将会进行10次循环然后返回500状态码,如下:

location /download/ {
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  break;
    return  403;
}

这里需要注意的是,如果replacement字段中包含了某些新的请求参数,那之前请求的URI中的参数将会追加到replacement字段中带的参数后面,如果先前请求的URI带的参数直接不要了,那需要在replacement字段的末尾处加一个问号?,这样就不会将之前的参数追加到后面了,比如:

rewrite ^/users/(.*)$ /show?user=$1? last;

5.5 rewrite_log指令

 启用或禁用将ngx_http_rewrite_module模块指令处理结果记录到info级别的error_log中,可以指定为onoff(默认),语法为rewrite_log on | off注意这里一定要将原来的error.log或者自定义的错误日志的级别设置为info,否则是看不到rewrite的处理结果的,以下是我的配置:

# 复习错误日志配置,指定为error1.log文件,并且记录级别为info
error_log logs/error1.log info;

在请求URI后,查看error1.log,文件末尾的记录出现rewrite的处理结果(可以发现nginx是按照rewrite定义的顺序依次执行的):

2018/10/18 16:51:47 [notice] 9221#0: *40 "^(/download/.*)/media/(.*)\..*$" does not match "/download/data/audio/dxt.mp4", client: 127.0.0.1, server: , request: "GET /download/data/audio/dxt.mp4 HTTP/1.1", host: "localhost:8082"
2018/10/18 16:51:47 [notice] 9221#0: *40 "^(/download/.*)/audio/(.*)\..*$" matches "/download/data/audio/dxt.mp4", client: 127.0.0.1, server: , request: "GET /download/data/audio/dxt.mp4 HTTP/1.1", host: "localhost:8082"
2018/10/18 16:51:47 [notice] 9221#0: *40 rewritten data: "/download/data/mp4/dxt.mp4", args: "", client: 127.0.0.1, server: , request: "GET /download/data/audio/dxt.mp4 HTTP/1.1", host: "localhost:8082"

5.6 set指令

 设置指定变量的值,该值可以包含文本,变量及其组合。语法为set $variable value;,即设置一个变量名为variable的变量,值为value

注意

这里分享一下我的测试过程,配置为:

  server {
    listen 8081;
    rewrite ^(/mp34/.*)/media/(.*)\..*$ $1/$2.mp3 last;
    rewrite ^(/mp34/.*)/audio/(.*)\..*$ $1/$2.mp4  last;
  }

虚拟机上的目录结构:

在线正则检测结果:

实际请求的URL与结果:

原以为死活匹配不上,第一天没搞懂,然后再测的时候,去错误日志error1.log中查看时,偶然看到怎么一条记录:

[error] 7970#0: *29 open() "/usr/local/nginx/html/download/data/mp4/dxt.mp4" failed (2: No such file or directory), client: 127.0.0.1, server: , request: "GET /download/data/audio/dxt.mp4 HTTP/1.1", host: "localhost:8082"

想到了昨天的测试不通过的原因,一眼瞄出了问题,并不是正则匹配不上的问题,而是nginx默认是将根目录搞成了Nginx所在目录下html文件下,那问题就很好解决了,直接将根目录指定为/即可,即:

  server {
    listen 8081;
    root /;
    rewrite ^(/mp34/.*)/media/(.*)\..*$ $1/$2.mp3 last;
    rewrite ^(/mp34/.*)/audio/(.*)\..*$ $1/$2.mp4  last;
  }

6. 重写HTTP响应

 有时可能需要重写HTTP相应中的内容,将某个字符串替换为另一个字符串,可以使用sub_filter指令来定义要应用的重写,该指令支持变量和替代链,使更复杂的更改成为可能。比如,可以更改引用除代理服务器之外的绝对链接:

location / {
    sub_filter      /blog/ /blog-staging/;
    sub_filter_once off;
}

如果有需求将http://更改为http://,并从请求头域替换本地主机地址到主机名,sub_filter_once指令告诉Nginx在一个位置location内连续应用sub_filter伪指令:

location / {
    sub_filter     'href="http://127.0.0.1:8080/'    'href="http://$host/';
    sub_filter     'img src="http://127.0.0.1:8080/' 'img src="http://$host/';
    sub_filter_once on;
}

【注意】如果发生另一个sub_filter匹配就,则使用sub_filter修改的响应部分将不再被替换!nginx都是使用后面的替换前面的。

使用error_page指令可以配置nginx返回自定义页面以及错误码,替换响应中的其他错误码,或将浏览器重定向到其他的URI,如下:

error_page 404 /404.html;

error_page指令指定要返回404页面错误代码的页面为/404.html

当NGINX找不到页面时,它会将代码301替换为代码404,并将客户端重定向到http:/example/new/path.html,当客户端仍尝试访问其旧URI的页面时,此配置非常有用,301代码通知浏览器页面已经永久移动,并且需要在返回时自动替换旧地址,可以像下面这样设置:

location /old/path.html {
    error_page 404 =301 http:/example.com/new/path.html;
}

注:上述的配置将对所有匹配到的URI进行操作,只要该location下任意一个请求没有找到出现404,就直接重定向,并带回301状态码。

以下配置是在未找到文件时将请求传递给后端的小栗子,因为在error_page指令的等号之后没有指定状态代码,所以对客户机的响应具有代理服务器返回的状态代码(不一定是404):

server {
    ...
    location /images/ {
        # Set the root directory to search for the file
        root /data/www;

        # Disable logging of errors related to file existence
        open_file_cache_errors off;

        # Make an internal redirect if the file is not found
        error_page 404 = /fetch$uri;
    }

    location /fetch/ {
        proxy_pass http://backend/;
    }
}

当没有找到文件时,error_page指令指示NGINX进行内部重定向,error_page指令的最终参数中的$uri变量保存当前请求的URI,该URI在重定向中被传递。例如,如果没有找到/images/some/file,它将被替换为/fetch/images/some/file,并且新的搜索位置(location)开始,最后请求最终在第二个location上下文中,并被代理到http://backend/,如果没有找到文件,则open_file_cache_errors指令可防止写入错误消息,因为丢失的文件可被正确地处理,但这不是必需的。

8. Nginx配置静态内容服务器

 NGINX可以用来提供静态内容服务,定义搜索路径以查找请求文件的方法,以及如何设置索引文件。

8.1 根目录和索引文件

root指令指定用于搜索文件的根目录,获取请求文件路径,nginx将请求URI附加到root指令指定的路径,root指令可以放在httpserverlocation上下文中,它适用于不包括root指令的所有location块以显式重新定义根,如:

server {
    root /www/data;

    location / {
    }

    location /images/ {
    }

    location ~ \.(mp3|mp4) {
        # 这里从新定义了根目录
        root /www/media;
    }
}

NGINX在文件系统的/www/data/images/目录中搜索以/images/开头的URI,如果URI以.mp3.mp4扩展名结尾,则NGINX会在/www/media/目录中搜索.mp3.mp4文件,因为它在匹配的location块中定义。如果请求以斜杠/结尾,则NGINX将其视为对目录的请求(即它认为你请求的是这个目录中的默认文件,如果没有/它则认为你请求是最后一个/后面的文件),并尝试在目录中找到索引文件,index指令定义索引文件的名称(默认值为index.html),如果请求URI为/images/some/path/,则NGINX会传递文件/www/data/images/some/path/index.html(如果存在),如果不存在文件,NGINX默认返回HTTP代码404(未找到),如果想配置NGINX以返回自动生成的目录列表,需要将on参数添加到autoindex指令中,如:

location /images/ {
    autoindex on;
}

【注意】上述自动索引必须是在没有默认文件的情况才能自动索引,否则nginx会自动映射到对应路径下的默认文件上(即index.html),其实就是最常看到的镜像仓库,类似于下面的:

当然,也可以手动指定默认文件(默认是index.html),而且可以指定多个(使用空格进行分隔),nginx将按照指定的顺序依次寻找这些文件,如果位置靠前的文件可以在对应目录下搜索到,那么就将其设置为默认文件,否则依次向位置靠后的文件进行搜索,此时index.html将不再是默认文件(如果不指定的话)。

8.2 尝试几个选项

try_file指令可用于检查指定的文件或目录是否存在并进行内部重定向,如果没有指定的文件或目录,则返回特定的状态代码,如果要检查与请求URI相对应的文件的存在,使用try_files指令和$uri变量,如:

server {
    root /data;

    location /images/ {
        try_files $uri /images/default.gif;
    }
}

该文件以URI的形式指定,它使用在当前位置或虚拟服务器的上下文中设置的rootalias伪指令进行处理,在这种情况下,如果与原始URI相对应的文件不存在,则NGINX将内部重定向到最后一个参数中指定的URI(总共3个参数:try_files$uri以及/images/..),也就是返回/data/images/default.gif

【注意】这里测试的时候,发现如果直接写本地路径是无效的,应该也是一个URI才有效,即之前已经定义的location,如:

http {
  server {

    location / {
      root /data/html;
      autoindex on;
    }

    location /test/ {
        root /data/test;
        # 第三个参数写的是上面定义的location,而不是直接写本地路径
        try_files $uri /;
    }
  }
}

上述的第三个参数也可以不写重定向的URI,也可以直接写成Nginx返回的状态码,如:

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

如果这个URI没有默认文件,那么直接返会一个404的状态码,当然,这个状态码可以根据实际需求进行定制。

当然,上述的第三个参数不仅可以是URI、状态码,也可以是一个代理,如:

location / {
    try_files $uri $uri/ @backend;
}

location @backend {
    proxy_pass http://backend.example.com;
}

8.3 优化Nginx服务内容的速度

 加载速度在用户体验层面来讲是非常重要的,所以有必要对其进行适当的优化。

启用sendfile

 默认情况下,NGINX会自动处理文件传输,并在发送文件之前先将其复制到缓冲区中,所以这里是进行了2步行为,启用sendfile指令可以直接略过将数据复制到缓冲区的步骤,直接将数据从一个文件描述符直接复制到另一个文件描述符;或者,为了防止一个快速连接完全占用工作进程,可以通过定义sendfile_max_chunk指令来限制在单个sendfile()调用中传输的数据量,如:

location /mp3 {
    # 直接发送,省去复制文件到缓冲区的步骤
    sendfile           on;
    # 限制在直接传输的数据量
    sendfile_max_chunk 1m;
    ...
}

启用tcp_nopush

 将tcp_nopush选项与sendfile一起使用,可以让NGINX能够通过sendfile获取数据块之后,在一个数据包中发送HTTP响应头(不太懂,后续再做学习),如:

location /mp3 {
    sendfile   on;
    tcp_nopush on;
    ...
}

启用tcp_nodelay

tcp_nodelay选项可以覆盖Nagle的算法,最初是为了解决慢网络中的小数据包问题而设计的,该算法将大量小数据包整合到较大的数据包中,并以200 ms的延迟发送数据包。如今,当服务大型静态文件时,无论数据包大小如何,都可以立即发送数据。延迟也会影响在线应用程序(ssh,在线游戏,网上交易)。默认情况下,tcp_nodelay指令设置为on,表示Nagle的算法被禁用,该选项仅用于Keepalive连接,如:

location /mp3  {
    tcp_nodelay       on;
    keepalive_timeout 65;
    ...
}

优化积压队列

 NGINX可以处理传入连接的速度,一般规则是建立连接时,将其放入监听套接字的“侦听”队列中,在正常负载下,有一个低队列,或根本没有队列,但是在高负载下,队列可能会急剧增长,这可能会导致性能不均衡,连接丢失和延迟查看当前的侦听队列:

netstat -lan

在实际测试过程中,CentOS 7 系统不能查看到具体的侦听队列,得到的貌似是一个MQ的信息,具体如下:

Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN     
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.1:53354         127.0.0.1:80            ESTABLISHED
tcp        0      0 127.0.0.1:80            127.0.0.1:53354         ESTABLISHED

可以查看到80端口的监听的队列有多少个不接受的链接,以及限制的连接的数量为多少(比如80的监听队列中有10个不接受的连接,而连接限制为128个连接),基本的有不被接受的连接是正常的情况;除此之外,也有这样一种情况,比如超过128个连接限制的192个不可接受的连接,当网站的流量很大时,这种情况也是很常见的,为了达到最佳性能,需要添加Ngin在操作系和Nginx的配置的排队等待接收的最大连接数:

  • 编辑/etc/sysctl.conf在最后一行添加参数net.core.somaxconn = 4096
  • 调整nginx,如果第一步中的somaxconn参数的值设置为大于512,那需要改NGINX listen指令的backlog参数以匹配:
server {
    listen 80 backlog 4096;
    # The rest of server configuration
}

实际这么配置时,出现了参数无法识别的问题,【待解决】

9. Nginx配置反向代理

 代理通常用于在多个服务器之间分配负载,无缝地显示来自不同网站的内容,或者通过HTTP以外的协议将请求传递给应用服务器,可以通过不同的协议将nginx请求传递给代的服务器,修改发到代理服务器的客户端的请求头,同时也可以配置的来自代理服务器的的响应缓冲。

9.1 将请求传递给代理服务器

 这一步是最基本的要求,nginx将请求的发送给指定的代理服务器,并将获取和响应返回给客户端,可以使用指定的协议将请求代理HTTP服务器(另一个nginx服务器或其他服务器)或非HTTP服务器(可以使用特定框架运行的应用程序),支持的协议有:FastCGIuwsgiSCGImemcached,将请求传递给HTTP代理服务器,在location中用proxy_pass指令指定即可,如:

location /some/path/ {
    # 指定代理服务器的地址
    proxy_pass http://www.example.com/link/;
}

上述的配置可以将原来请求/some/path/这个location的所有请求全部传递给proxy_pass参数指定的URI即/link/,但是,这里如果将URI与地址一起指定,它将替换与location参数匹配请求URI的部分,而不是像上面那样直接整体替换,如使用/some/path/page.html的URI请求将被代理到http://www.example/link/page.html(而不是什么都不管直接去请求http://www.example/link/),如果地址被指定为没有URI,或者不可能确定要替换的URI部分,则会传递完整的请求URI(可能是修改)。这个参数可以像上面使用域名,也可以使用IP来指定,如果默认不是80端口还可以带上端口号,如:

location ~ \.php {
    proxy_pass http://127.0.0.1:8000;
}

要将请求传递给非HTTP代理服务器,应使用适当的**_ pass指令,具体如下:

  • fastcgi_pass:将请求传递给FastCGI服务器;
  • uwsgi_pass:将请求传递给uwsgi服务器;
  • scgi_pass:将请求传递给SCGI服务器;
  • memcached_pass:将请求传递给memcached服务器;

注意,proxy_pass指令也可以指向一组命名的服务器,在这种情况下,根据指定的方法在组中的服务器之间分配请求。

9.2 传递请求头

 默认情况下,Nginx在代理请求hostConnection中重新定义了两个头字段,并消除值为空字符串的头字段,Hast设置为$proxy_host变量,Connection设置为close,要更改这些设置以及其他的header字段,需要使用proxy_set_header指令,这个指令可以在一个或多个location中指定,也可以在server上下文中或http块中指定,如:

location /some/path/ {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://localhost:8000;
}

这里的Host变量设置为$host变量,为了防止头域被传递给代理服务器,可以独立地将这个字段设置为空字符串:

location /some/path/ {
    proxy_set_header Accept-Encoding "";
    proxy_pass http://localhost:8000;
}

9.3 配置缓冲区

 默认情况下,Nginx缓存来自于服务器的响应,响应存储于内部缓存区,并不会发送到客户端,直接缓冲有助于通过慢客户端优化性能,如果响应从NGINX同步传递到客户端,这可能会浪费代理服务器时间,然而,当启用缓冲时,NGINX允许代理服务器快速处理响应,而NGINX存储响应时间与客户端需要下载的时间一样长。负责启用和禁用缓冲的指令是proxy_buffering,默认情况下,它被设置为开启且缓冲已启用。

 在以下示例中,缓冲区的默认数量增加,并且响应的第一部分的缓冲区的大小小于默认值,使用proxy_buffer_size配置的缓冲区来存储响应的当前部分:

location /some/path/ {
    proxy_buffers 16 4k;
    proxy_buffer_size 2k;
    proxy_pass http://localhost:8000;
}

如果缓存被禁用,则在从代理服务器接收缓冲时,响应将同步发送到客户端,对于需要尽快开始接收响应的快速交互式客户端,此行为可能是可取的。如果要禁用特定位置的缓冲,需要在location块中将proxy_buffering伪指令设置为off,如:

location /some/path/ {
    # 默认开启,关闭需要显式关闭
    proxy_buffering off;
    proxy_pass http://localhost:8000;
}

9.4 选择传出IP地址

 如果代理服务器有多个网络接口,有时可能需要选择特定的源IP地址才能连接到代理服务器或上游,如果NGINX后端的代理服务器只配置为接受来自特定IP网络或IP地址范围的连接,在这种情况下,这个配置选项就很有用,proxy_bind指令可以用来干这事儿,基本语法为proxy_bind address [transparent] | off;,如果将值指定为off那之前从祖先级配置中继承的proxy_bind将会失效(失效后将允许系统自动分配本地IP和端口);transparent参数允许到代理服务器的传出连接源自非本地IP地址,如一个客户端的真实IP:

proxy_bind $remote_addr transparent;

要使transparent参数生效,通常需要使用超级用户权限来启动nginx的worker进程,在Linux中这个参数无须特别指定,因为worker进程从主进程继承了CAP_NET_RAW功能;除此之外,还需要配置内核路由表以拦截来自代理服务器的网络流量。

location /app1/ {
    proxy_bind 127.0.0.1;
    proxy_pass http://example/app1/;
}

location /app2/ {
    proxy_bind 127.0.0.2;
    proxy_pass http://example/app2/;
}

IP地址也可以用变量指定,$server_addr变量传递接受请求的网络接口的IP地址,如:

location /app3/ {
    proxy_bind $server_addr;
    proxy_pass http://example.com/app3/;
}

【注意】

上述的配置一直以为是配置IP访问权限的,搞来搞去发现好像不是这么一回事儿,翻了一下,配IP的访问权限是用的是allowdeny指令,下面才是配置IP访问限制的示例:

    location / {
      # 允许此IP访问
      allow 127.0.0.1;
      # 禁止此IP访问
      deny 10.4.37.108;
      deny 10.4.37.90;
      deny all;

      root /data/html/;
      autoindex on;
    }

上述配置中需要注意的是allowdeny指令是顺序执行的,即nginx会按序依次去检测访问的IP是不是127.0.0.1如果是就放行,否则再判断是不是10.4.37.108,如果是就禁止,否则再判是不是10.4.37.90,如果是就禁止,其他的IP就禁止,如果将deny all;放在最前面,那直接所有的IP都被被禁止,因为判断的时候,首先执行deny all;的判断,包含了所有的IP。最后请教别人,说是一个域名有多个IP(即有多个服务器多服务于一个应用,好像是负载均衡),然后这里的选择传出IP地址,表示的是当用户对一个域名发出请求时,proxy_bind可以为下面proxy_bind指定的域名指定具体的服务器(表现为proxy_bind表示的IP)来处理请求。

10. Nginx压缩和解压缩

 压缩响应正常情况下都会显著减少传输数据量的大小,但由于压缩在运行时发生,它也会增加相当大的处理开销,对性能产生负面影响,在向客户端发送响应之前,NGINX会执行压缩,但不会“压缩”已压缩的响应(例如,由代理的服务器)。

10.1 Nginx启用压缩

 要启用压缩,需要使用gzip指令,设置其为on即可,如:

gzip on;

默认情况下,NGINX仅使用MIME类型text/html压缩响,要使用其他MIME类型压缩响应,需要使用gzip_types指令列出其他类型,如:

gzip_types text/plain application/xml;

如果要指定要压缩的响应的最小长度,需要使用gzip_min_length指令,默认值为20字节(byte单位为B,字是bit是小写的b),如调正为1000字节:

gzip_min_length 1000;

默认情况下,NGINX不会压缩对代理请求的响应(来自代理服务器的请求),请求来自代理服务器的事实由请求中Via头字段是否存在进行确定,要配置这些响应的压缩,需要使用gzip_proxied指令,该指令具有多个参数,指定NGINX应压缩哪种代理请求。比如,仅对不会在代理服务器上缓存的请求压缩响应是合理的,那么gzip_proxied指令具有指示NGINX在响应中检查Cache-Control头字段的参数,如果值为no-cache, no-storeprivate,则压缩响应,另外,还必须包括Expires参数来检查Expires头域的值,这些参数在以下示例中与auth参数一起设置,该参数检查Authorization头字段是否存在(授权响应特定于最终用户,并且通常不被缓存):

gzip_proxied no-cache no-store private expired auth;

与大多数其他指令一样,配置压缩的指令可以包含在http上下文中,也可以包含在serverlocation配置块中,gzip压缩的整体配置实例如下:

server {
    gzip on;
    gzip_types      text/plain application/xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
    ...
}

10.2 Nginx启用解压缩

 某些客户端不支持使用gzip编码方法的响应,同时,可能需要存储压缩数据,或者即时压缩响应并将它们存储在缓存中,为了成功地服务于不接受压缩数据的客户端,NGINX可以在将数据发送到后一种类型的客户端时即时解压缩数据,解压缩需要使用gunzip指令,如:

location /storage/ {
    gunzip on;
    ...
}

gunzipgzip指令可以在相同的上下文中共存,如:

server {
    gzip on;
    gzip_min_length 1000;
    gunzip on;
    ...
}

注意,解压缩的指令在单独的模块中定义,默认情况下可能不包含在开源NGINX构建中。

10.3 Nginx发送压缩文件

 要将文件的压缩版本发送到客户端而不是常规文件,需要在适当的上下文中将gzip_static指令设置为on,如:

location / {
    gzip_static on;
}

为了服务/path/to/file的请求,NGINX尝试查找并发送文件/path/to/file.gz,如果文件不存在,或客户端不支持gzip,则NGINX将发送未压缩版本的文件,注意:gzip_static指令不启用即时压缩,它只是使用压缩工具预先压缩的文件,要在运行时即时压缩内容(而不仅仅是静态内容),需要使用gzip指令。

11. Nginx内容缓存

 当启用缓存时,NGINX将响应保存在磁盘缓存中,并使用它们来响应客户端,而不必每次都为同一内容代理请求。

11.1 启用缓存

 在顶层的http上下文中包含proxy_cache_path指令,第一个参数(必选)是缓存在本地的路径,第二个参数用keys_zone指令定义,用于存储有关缓存项目的元数据的共享内存区域的名称和大小,如:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m;
}

然后在要缓存服务器响应的上下文(协议类型,虚拟服务器或位置)中包含proxy_cache指令,将由keys_zone参数定义的区域名称指定为proxy_cache_path指令,如:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m;

    server {
        proxy_cache one;
        location / {
            proxy_pass http://localhost:8000;
        }
    }
}

keys_zone参数定义的大小不会限制缓存的响应数据的总量,缓存响应本身存储在文件系统上的特定文件中的元数据副本,要限制缓存的响应数据量,需要将max_size参数包含到proxy_cache_path指令中,在实际测试的时候,发现配置的缓存并没有生效。

11.2 涉及缓存的进程

 缓存中有两个额外的Nginx进程:

  • 缓存管理周期性的被激活以检查缓存的状态。如果缓存大小超过了max_cize_path指令设置的max_size参数,缓存管理将删除最近访问的数据,高速缓存管理器激活之间的缓存数据量可以临时超过限制。
  • Nginx启动后,缓存加载程序只运行一次。它将有关之前的缓存的数据的元数据加载到共享内存区域,一次加载整个缓存可能会在启动后的最初几分钟内消耗足够的资源来减慢Nginx的性能,为了避免这种情况,可以通过将以下的参数包含到proxy_cache_path伪指令来配置缓存的迭代加载:
    • loader_threshold :迭代的持续时间,以毫秒为单位(默认为200);
    • loader_files:在一次迭代期间加载的最大项目数(默认为100);
    • loader_sleeps:迭代之间的延迟(以毫秒为单位)(默认为50);

迭代持续300毫秒或直到加载了200个项目,配置如下:

proxy_cache_path /data/nginx/cache keys_zone=one:10m loader_threshold=300 loader_files=200;

11.3 指定要缓存的请求

 默认情况下,NGINX首次从代理服务器接收到这样的响应后,缓存对HTTP GETHEAD方法的请求的所有响应,作为请求的密钥(标识符),NGINX使用请求字符串,如果请求具有与缓存响应相同的密钥,则NGINX将缓存的响应发送给客户端,可以在httpserverlocation上下文中包含各种指令,以控制哪些响应被缓存,要更改在计算密钥时使用的请求字符,请包括proxy_cache_key伪指令:

proxy_cache_key "$host$request_uri$cookie_user";

Shell要定义在缓存响应之前必须进行具有相同密钥的请求的最小次数,应包括proxy_cache_min_uses指令:

proxy_cache_min_uses 5;

Shell要使用除GET和HEAD之外的方法来缓存对请求的响应,请将它们与GET和HEAD一起列为proxy_cache_methods伪指令的参数:

proxy_cache_methods GET HEAD POST;

11.4 限制或绕过缓存

 默认情况下,响应将无限期的保留在缓存中,只有缓存超过最大配置大小,然后按照最后一次请求的时间长度,它们才被删除,可以通过在httpserverlocation上下文中包含指令来设置缓存响应被认为有效的时间长度,甚至是否使用它们,要限制缓存响应与特定状态代码被认为有效的时间,需要使用proxy_cache_valid指令:

proxy_cache_valid 200 302 10m;
proxy_cache_valid 404      1m;

在上述的配置中,使用状态码200302的响应有效时间为10分钟,而404的状态码响应时间有效时间为1分钟,如果要定义具有所有状态码的响应有效时间,需要指定any为第一个参数,而不是具体的状态码,如:

proxy_cache_valid any 5m;

要定义Nginx不会向客户端发送缓存响应的条件,需要使用proxy_cache_bypass指令,每个参数定义一个条件并由多个变量组成,如果至少有一个参数不为空,并且不等于“0”(零),则NGINX不会在缓存中查找响应,而是将请求立即转发到后端服务器,如:

proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;

要定义NGINX根本不缓存响应的条件,需要使用proxy_no_cache指令,以与proxy_cache_bypass伪指令相同的方式定义参数,如:

proxy_no_cache $http_pragma $http_authorization;

11.5 从缓存中清除内容

 NGINX可以从缓存中删除过期的缓存文件,删除过期的缓存内容以防止同时提供旧版本和新版本的网页,在接收到包含自定义HTTP头或PURGE的HTTP方法的特殊purge请求时,缓存被清除。

11.5.1 配置缓存清除

 设置使用PURGE的HTTP方法请求并删除匹配的URL:

  • http块层级上,创建一个新的变量,如$purge_method,取决于$request_method变量,如:
http {
    ...
    map $request_method $purge_method {
        PURGE 1;
        default 0;
    }
}
  • location中配置高速缓存,包括指定缓存清除请求条件的proxy_cache_purge指令,这里演示直接使用上面配置的$purge_method,如下:
server {
 listen      80;
 server_name www.example.com;

 location / {
     proxy_pass  http://localhost:8002;
     proxy_cache mycache;

     proxy_cache_purge $purge_method;
 }
}

11.5.2 发送清除指令

 配置proxy_cache_purge指令后,还需要发送一个特殊的缓存清除请求来清除缓存,比如:

curl -X PURGE -D – "http://www.example/*"
HTTP/1.1 204 No Content
Server: nginx/1.5.7
Date: Sat, 01 Dec 2015 16:33:04 GMT
Connection: keep-alive

在上述的例子中,使用Curl工具发送了一个删除的请求,具有公共URL部分(由星号通配符指定)的资源将被删除,但这些高速缓存条目将不会从缓存中完全删除,它们将被保留在磁盘上,直到它们被删除,成为非活动状态(proxy_cache_path的非活动参数),或由缓存清除程序进程处理,或客户端尝试访问它们 。

11.5.3 限制访问清除指令

 通常情况下,删除都要有权限的,Nginx提供了只能是有限数量的IP地址可以请求的功能,可以进行如下的设置:

geo $purge_allowed {
   default         0;  # deny from other
   10.0.0.1        1;  # allow from localhost
   192.168.0.0/24  1;  # allow from 10.0.0.0/24
}

map $request_method $purge_method {
   PURGE   $purge_allowed;
   default 0;
}

11.5.4 从缓存中完全删除文件

 要完全删除与星号相匹配的缓存文件,需要激活一个特殊的缓存清除程序,该过程将永久地遍历所有缓存条目,并删除与通配符相匹配的条目,在http块级别上,将purger参数添加到proxy_cache_path指令中,如下:

proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m purger=on;

在上述的例子中,NGINX检查请求中是否使用了PURGE方法,如果是,则分析客户端的IP地址,如果IP地址被列入白名单,那么$purge_method设置为$purge_allowed,其中1允许清除,0表示拒绝清除(即没有权限清除)。

11.5.5 缓存清除的完整示例

http {
    ...
    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m purger=on;

    map $request_method $purge_method {
        PURGE 1;
        default 0;
    }

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

        location / {
            proxy_pass        http://localhost:8002;
            proxy_cache       mycache;
            proxy_cache_purge $purge_method;
        }
    }

    geo $purge_allowed {
       default         0;
       10.0.0.1        1;
       192.168.0.0/24  1;
    }

    map $request_method $purge_method {
       PURGE   $purge_allowed;
       default 0;
    }
}

11.6 字节缓存

 有时,初始缓存填充操作可能需要一些时间,特别对于大文件。当第一个请求开始下载视频文件的一部分时,下一个请求将不得不等待整个文件被下载并被放入高速缓存,NGINX使缓存这样的范围请求成为可能,并逐渐用缓存片模块填充高速缓存。该文件被分为较小的“切片”。每个范围的请求选择将覆盖所请求范围的特定切片,并且如果此范围仍未缓存,则将其放入缓存中。对这些切片的所有其他请求将从缓存中获取响应。这中需求要启用字节范围缓存(这里需要确认NGINX是使用slice模块编译的):

  • 使用slice指令设置切片的大小,如:
location / {
 slice  1m;
}

slice的大小应适当调整,使切片快速下载,在处理请求时,如果slice设置太小可能会导致内存使用量过多和大量打开的文件描述符,slice设置太大则可能会导致延迟。

$slice_range变量包含到缓存键中:

proxy_cache_key $uri$is_args$args$slice_range;
  • 启用使用206状态代码缓存响应:
proxy_cache_valid 200 206 1h;

通过在Range头字段中传递$slice_range变量来将传递范围请求设置为代理服务器:

proxy_set_header  Range $slice_range;

字节范围缓存示例:

location / {
    slice             1m;
    proxy_cache       cache;
    proxy_cache_key   $uri$is_args$args$slice_range;
    proxy_set_header  Range $slice_range;
    proxy_cache_valid 200 206 1h;
    proxy_pass        http://localhost:8000;
}

注意,如果切片(slice)缓存打开,则不应更改初始文件。

11.7 组合配置示例

 下面的示例配置组合了上述某些缓存选项:

http {
    ...
    proxy_cache_path /data/nginx/cache keys_zone=one:10m loader_threshold=300 
                     loader_files=200 max_size=200m;

    server {
        listen 8080;
        proxy_cache one;

        location / {
            proxy_pass http://backend1;
        }

        location /some/path {
            proxy_pass http://backend2;
            proxy_cache_valid any 1m;
            proxy_cache_min_uses 3;
            proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
        }
    }
}

上述配置示例中,两个位置使用相同的缓存,但是以不同的方式。由于backend1服务器的响应很少更改,因此不包括缓存控制指令,首次请求响应缓存,并无限期保持有效。相比之下,对backend2服务的请求的响应频繁变化,因此它们被认为只有1分钟有效,并且在相同请求3次之前不被缓存,此外,如果请求符合proxy_cache_bypass指令定义的条件,则NGINX会立即将请求传递给backend2,而不在缓存中查找。

12. Nginx配置日志

12.1 设置错误日志

 NGINX将遇到不同严重性级别的问题信息写入错误日志,在配置文件中error_log指令将日志记录设置为特定文件,stderrsyslog,并指定要记录消息的最低级别。默认情况下,错误日志位于{NGING_INSTALL_PATH}/logs/error.log(绝对路径取决于操作系统和安装),并记录来自所指定的所有严重级别的消息,下面是我的错误日志的部分消息:

2018/10/16 08:18:26 [error] 79428#0: *243 open() "/data/html/favicon.ico" failed (2: No such file or directory), client: 10.4.37.108, server: , request: "GET /favicon.ico HTTP/1.1", host: "10.4.37.54"
2018/10/16 08:45:11 [notice] 86830#0: signal process started
2018/10/16 08:46:21 [notice] 86845#0: signal process started
2018/10/16 08:57:15 [emerg] 86951#0: unknown directive "gzip_static" in /usr/local/nginx/conf/nginx.conf:21
2018/10/16 08:58:40 [notice] 86961#0: signal process started

至于在错误日志中要记录哪些级别的信息,可以在配置文件中进行指定,如将错误消息的最小严重性级别更改为从错误记录到警告,就是如下的配置(三个参数,第一个是指令、第二个是路径、第三个是日志记录的最低级别):

# 这边默认根目录为nginx的根目录
error_log logs/error.log warn;
## 或者可写为: error_log /var/logs/nginx/error.log warn;

在使用上述的日志配置后,将记录warnerror critalertemerg级别的消息。
错误日志的默认设置全局工作。要覆盖它,需要将error_log指令放在main(顶级)配置上下文中。main上下文中的设置始终被其他配置级别继承,还可以在httpstreamserverlocation级别中指定error_log指令,并覆盖从较高级别继承的设置。如果发生错误,则该消息只写入一个错误日志,最接近发生错误的级别的错误日志。 但是,如果在同一级别指定了多个error_log伪指令,则会将消息写入所有指定的日志。

12.2 设置访问日志

 在处理请求之后,NGINX在访问日志中写入有关客户端请求的信息。默认情况下,访问日志位于{NGING_INSTALL_PATH}logs/access.log中,{NGING_INSTALL_PATH}为安装nginx的目录,信息以预定义的组合格式写入日志。如果要覆盖这个默认日志格式,需要使用log_format指令更改记录消息的格式,也需要结合access_log指令,来指定日志的位置及其格式。日志格式使用变量定义。以下示例定义扩展预定义组合格式的日志格式,其值指示响应gzip的压缩比,然后将格式应用于启用压缩的虚拟服务器:

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

    server {
        gzip on;
        access_log /spool/logs/nginx-access.log compression;
        ...
    }
}

至于读取生成时间值,主要由以下的规则生成:

  • 当通过多个服务器处理请求时,变量包含由逗号分隔的多个值;
  • 当从一个上游组到另一个上游组有内部重定向时,这些值以分号分隔;
  • 当请求无法到达上游服务器或无法接收到完整报头时,变量包含0(零);
  • 在连接到上游或从缓存中获取回复时出现内部错误的情况下,该变量包含-(连字符);

可以通过启用缓冲区的日志消息和名称包含变量的常用日志文件的描述符缓存来优化日志记录。要启用缓冲,要使用access_log指令的缓冲区参数来指定缓冲区的大小。当下一个日志消息不适合缓冲区以及其他情况时,缓冲的消息将被写入日志文件。

要启用日志文件描述符的缓存,需要使用open_log_file_cache指令。和error_log指令类似,在特定配置级别定义的access_log伪指令将覆盖以前级别的设置。当请求的处理完成时,消息将被写入到当前级别上配置的日志中,或者从先前的级别继承。如果一个级别定义了多个访问日志,则会将消息写入所有的访问日志。

12.3 启用条件日志记录

 条件记录是允许从访问日志中排除掉的琐碎或不重要的日志,在nginx中,条件日志记录由access_log伪指令的if参数启用,下面是一个示例(不包含2xx3xx状态码的请求):

map $status $loggable {
    ~^[23]  0;
    default 1;
}

access_log /path/to/access.log combined if=$loggable;

12.4 日志记录到Syslog

syslog实用程序是计算机消息记录的标准,并允许从单个syslog服务器上的不同设备收集日志消息。在NGINX中,对syslog的日志记录使用error_logaccess_log伪指令中的syslog:前缀进行配置。Syslog消息可以发送到服务器=可以是域名,IP地址或UNIX域的套接字路径。可以使用端口指定域名或IP地址来覆盖默认端口514。可以在unix:prefix之后指定UNIX域套接字路径:

error_log server=unix:/var/log/nginx.sock debug;
access_log syslog:server=[2001:db8::1]:1234,facility=local7,tag=nginx,severity=info;

在上述的配置示例中,NGINX错误日志消息将在调试日志记录级别写入UNIX域套接字,并将访问日志写入具有IPv6地址和端口1234的syslog服务器。

facility =参数指定正在记录消息的程序类型,默认值为local7,其他可能的值是:authauthprivdaemoncronftplprkernmailnewssysloguseruucplocal0local7

tag =参数将自定义标签应用于syslog消息(在我们的示例中为nginx)。severity =参数设置访问日志的syslog消息的严重性级别。严重性越来越高的可能值为:debuginfonoticewarnerror(默认)、critalertemerg。消息记录在指定的级别和更严重的级别。在我们的示例中,严重性级别错误还可以critalertemerg 级别。

13. Nginx的主要应用场景

 回到最初的介绍,Nginx可以用来干嘛干嘛,基本总结如如下4点:

  • 反向代理;
  • 负载均衡;
  • HTTP服务器(动静分离);
  • 正向代理;

13.1 反向代理

 反向代理应该是Nginx做的最多的一件事,什么是反向代理呢,以下是百度百科的说法:反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。简单来说就是真实的服务器不能直接被外部网络访问,所以需要一台代理服务器,而代理服务器能被外部网络访问的同时又跟真实服务器在同一个网络环境,当然也可能是同一台服务器,端口不同而已。下面是一段简单的反向代理实现的配置代码:

server {  
        listen       80;
        server_name  localhost;
        client_max_body_size 1024M;

        location / {
            # 直接将80端口的请求代理到8080端口
            proxy_pass http://localhost:8080;
            proxy_set_header Host $host:$server_port;
        }
}

13.2 负载均衡

 负载均衡也是Nginx常用的一个功能,负载均衡其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。而Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略,主要如下:

1.第一种负载均衡策略:RR(默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除,配置如下:

    upstream test {
        server localhost:8080;
        server localhost:8081;
    }
    server {
        listen       81;
        server_name  localhost;
        client_max_body_size 1024M;

        location / {
            proxy_pass http://test;
            proxy_set_header Host $host:$server_port;
        }
    }

负载均衡的核心代码为:

    upstream test {
        server localhost:8080;
        server localhost:8081;
    }

这里配置了2台服务器,当然实际上是一台,只是端口不一样而已,而8081的服务器是不存在的,也就是说访问不到,但是我们访问http://localhost的时候,也不会有问题,会默认跳转到http://localhost:8080具体是因为Nginx会自动判断服务器的状态,如果服务器处于不能访问(服务器挂了),就不会跳转到这台服务器,所以也避免了一台服务器挂了影响使用的情况,由于Nginx默认是RR策略,所以我们不需要其他更多的设置。

2.第二种负载均衡策略:权重

 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况,例如:

    upstream test {
        server localhost:8080 weight=9;
        server localhost:8081 weight=1;
    }

上述配置的意思就是10次一般只会有1次会访问到8081,而有9次会访问到8080,权重就体现出来了。

3.第三种负载均衡策略:ip_hash

 上面的2种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当我们的程序不是无状态的时候(采用了session保存数据),这时候就有一个很大的很问题了,比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用iphash了,iphash的每个请求按访问iphash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。比如:

    upstream test {
        ip_hash;
        server localhost:8080;
        server localhost:8081;
    }

4.第四种负载均衡策略:fair(第三方)

 按后端服务器的响应时间来分配请求,响应时间短的优先分配。配置示例:

    upstream backend {
        hash $request_uri;
        hash_method crc32;
        server localhost:8080;
        server localhost:8081;
    }

5.第五种负载均衡策略:url_hash(第三方)

 按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法,配置示例如下:

    upstream backend {
        hash $request_uri;
        hash_method crc32;
        server localhost:8080;
        server localhost:8081;
    }

13.3 HTTP服务器

 Nginx本身也是一个静态资源的服务器,当只有静态资源的时候,就可以使用Nginx来做服务器,同时现在也很流行动静分离,就可以通过Nginx来实现,首先看看Nginx做静态资源服务器的配置示例:

    server {
        listen       80;
        server_name  localhost;
        client_max_body_size 1024M;

        location / {
               root   E:/wwwroot;
               index  index.html;
           }
    }

如果访问http://localhost就会默认访问到E盘wwwroot目录下面的index.html,如果一个网站只是静态页面的话,那么就可以通过这种方式来实现部署。

13.4 动静分离

 动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路,下面是示例配置文件:

upstream test{  
       server localhost:8080;  
       server localhost:8081;  
    }  

    server {  
        listen       80;  
        server_name  localhost;  

        location / {  
            root   e:/wwwroot;  
            index  index.html;  
        }  

        # 所有静态请求都由nginx处理,存放目录为html  
        location ~ .(gif|jpg|jpeg|png|bmp|swf|css|js)$ {  
            root    e:/wwwroot;  
        }  

        # 所有动态请求都转发给tomcat处理  
        location ~ .(jsp|do)$ {  
            proxy_pass  http://test;  
        }  

        error_page   500 502 503 504  /50x.html;  
        location = /50x.html {  
            root   e:/wwwroot;  
        }  
    }

这样就可以把HTML以及图片和css以及js放到wwwroot目录下,而tomcat只负责处理jsp和请求,例如当我们后缀为gif的时候,Nginx默认会从wwwroot获取到当前请求的动态图文件返回,当然这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,然后通过反向代理和负载均衡配置过去就好了,只要搞清楚了最基本的流程,很多配置就很简单了,另外localtion后面其实是一个正则表达式,所以非常灵活。

13.5 正向代理

 正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端才能使用正向代理。当你需要把你的服务器作为代理服务器的时候,可以用Nginx来实现正向代理,但是目前Nginx有一个问题,那么就是不支持HTTPS,虽然我百度到过配置HTTPS的正向代理,但是到最后发现还是代理不了,当然可能是我配置的不对,所以也希望有知道正确方法的同志们留言说明一下,示例配置如下:

    resolver 114.114.114.114 8.8.8.8;
    server {

        resolver_timeout 5s;

        listen 81;

        access_log  e:/wwwrootproxy.access.log;
        error_log   e:/wwwrootproxy.error.log;

        location / {
            proxy_pass http://$host$request_uri;
        }
    }

resolver是配置正向代理的DNS服务器,listen是正向代理的端口,配置好了就可以在ie上面或者其他代理插件上面使用服务器ip+端口号进行代理了。Nginx是支持热启动的,直接reload即可。

附(Nginx常用配置):

1. 贴一下常用的Nginx的配置方式:

1.Nginx主配置文件:

worker_processes  1;

error_log  /data/logs/nginx/error.log  debug;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] $http_host $request_method "$uri" "$query_string" '
                      '$status $body_bytes_sent "$http_referer" $upstream_status $upstream_addr $request_time   $upstream_response_time '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    fastcgi_intercept_errors on;

    access_log  /data/logs/nginx/access.log  main;
    client_max_body_size 5120m;
    sendfile        on;

    limit_req_zone $binary_remote_addr zone=allips:10m rate=3r/m;

    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 32k;
    proxy_headers_hash_bucket_size 1024;
    proxy_headers_hash_max_size 512;

    proxy_connect_timeout 3000;
    proxy_read_timeout 3000;
    proxy_send_timeout 3000;
    proxy_buffer_size 64k;
    proxy_buffers   16 32k;
    proxy_busy_buffers_size 128k;
    proxy_temp_file_write_size 1024m;

    keepalive_timeout  300;

    gzip  on;

    proxy_intercept_errors on;
    include available/*.conf;

}

2.将不同服务的配置文件放在自定义的一个文件下(置于Nginx中的conf的文件夹内部),比如这里在conf文件夹中创建了一个available文件夹,然后将不同服务的配置文件放在该文件夹下,比如xxx.conf(一般用服务名):

server {
	listen 80;
        server_name task.xxx;
        access_log /data/logs/nginx/test_task_access.log main;
        
        # /public 开头的URI会进入到这个里面处理
        location /public {
                rewrite ^/(.*)$ /portal/$1 break;  # 重写URL,主要考虑ContextPath的问题
                proxy_pass http://portal; # 代理到该地址
                proxy_redirect http://portal.xxx/portal http://portal.xxx;
                proxy_set_header Host portal.xxx;
                proxy_set_header X-Forwarded-Proto http;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		        proxy_ignore_client_abort on;
        }

        # /private 开头的URI会进入到这个里面处理
        location /private {
                    rewrite ^/(.*)$ /mine/$1 break;
                    proxy_pass http://mine;
                    proxy_redirect http://mine.xxx/mine http://mine.xxx;
                    proxy_set_header Host mine.xxx;
                    proxy_set_header X-Forwarded-Proto http;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_ignore_client_abort on;
            }
        
        # 不能匹配上面的URI开头的URI会进入到这个里面处理
        location / {
            rewrite ^/(.*)$ /portal/$1 break;
            proxy_pass http://portal;
            proxy_redirect http://portal.xxx/portal http://portal.xxx;
                    proxy_set_header Host portal.xxx;
                    proxy_set_header X-Forwarded-Proto http;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_ignore_client_abort on;
        }
}

3.创建upstream配置文件指定域名映射IP的关系,同样放在available文件夹下,比如xxx_upstream.conf(一般用项目名字):

upstream task {
    ip_hash;
	# 即应用IP+端口,将上面代理的地址映射到10.5.3.28上的8088端口
    server 10.5.3.28:8088 max_fails=3 fail_timeout=30s;
}

2. 采坑记录

 ngix的 server name 不要使用_,非要使用请使用-,否则会有400的错误码,(本机绑定host时,也尽量避免使用_的使用)

更多推荐

nginx学习记录

本文发布于:2023-04-13 02:19:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/38134abb76cd4ca54e85599afb320174.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:nginx

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!

  • 71606文章数
  • 14阅读数
  • 0评论数