- 目录
- 环境及目的
- nginx配置文件特点和结构
- 3 index定义主页
- 4 error_page定义错误页面
- 5 长连接相关指令
- 6 限制响应报文发送速率
- 7 文件元数据信息缓存
- 8 访问控制
- 9 basic认证
- 10 limit_except
- 11 状态信息页面
- 12 防盗链
- 13 作为https服务端
- 14 日志相关
- 1511 机制
- 1512 基本效果
- 1513 循环重定向问题
- 1514 替换为的路径是httphttps
- 1515 各flag
1 环境及目的
本文演示nginx作为web服务的常用配置,相对于httpd的配置说明。
nginx版本1.8,已编译安装完成。
安装目录及配置文件目录:
[root@node1 ~]% ls /usr/local/nginx
html logs nginx
[root@node1 ~]% ls /etc/nginx/
fastcgi.conf fastcgi_params.default mime.types nginx.conf.default uwsgi_params
fastcgi.conf.default koi-utf mime.types.default scgi_params uwsgi_params.default
fastcgi_params koi-win nginx.conf scgi_params.default win-utf
安装使用的选项见 。
下面主要在nginx配置文件中使用不同配置,验证nginx作为web服务的各功能。
为方便起见,把nginx命令添加至PATH:
[root@node1 ~]% cat /etc/profile.d/nginx.sh
export PATH=/usr/local/nginx:$PATH
2 nginx配置文件特点和结构
2.1 特性
- nginx是高度模块化的,编译时装载不同的模块,会在配置文件中引入不同的内置变量和指令;
- 除了内置变量还可自定义变量,格式”set NAME VALUE”,变量的设置和引用均需要”$”符号;
- nginx由各指令配置,每个指令必须以分号结尾;
- 配置指令使用格式类似httpd的,都是”DIRECTIVE VALUE”;
不同模块的参考信息在官网 http://nginx.org/en/docs/ 模块参考项下。其中说明了各模块的内置变量和指令。大部分内置变量在http核心模块。
在nginx配置目录下有很多文件,基本见名知意。主配置文件为nginx.conf:
[root@node1 ~]% ls /etc/nginx/
fastcgi.conf fastcgi_params.default mime.types nginx.conf.default uwsgi_params
fastcgi.conf.default koi-utf mime.types.default scgi_params uwsgi_params.default
fastcgi_params koi-win nginx.conf scgi_params.default win-utf
2.2 主配置文件结构
nginx主配置文件结构大致形如:
【main段】
events{
【events段】
}
http{
【http段】
}
……
本文编译nginx时没有使用”–with-mail”,所以配置文件中没有mail段。
和httpd类似,nginx的配置文件也可使用指令include把指定文件包含进来,这样可把配置定义在不同的文件中,便于编辑和维护。
主配置文件中,main配置段是直接写在配置文件中,events包括其他所有配置段,都需写在对应的大括号中。
main段和events段对全局有效,称为全局配置。
3 常用全局配置
3.1 main段
main配置段主要定义了nginx运行的各属性,默认为:
#user nobody; # 运行worker进程的用户。由于本机上的nginx在编译时指定了用户nginx,这里nobody是注释
worker_processes 1; # 启动的worker进程数
#error_log logs/error.log; # 错误日志的路径和级别
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid; # pid文件路径
main配置段指令由核心模块引入。常用指令:
指令 | 意义 |
user USER [GROUP] | 指定运行worker进程的用户和组(注意master进程要由root运行,因为要监听套接字)。如不指定组,则就是用户所在的组 |
pid PATH | 指定nginx的pid文件 |
worker_rlimit_nofile | 指定一个worker进程最多打开的文件数 |
worker_processes NUM | 指定启动的worker进程数1,默认值1。在较新版本中,参数可指定为”auto”,令其自动设置 |
worker_cpu_affinity CPU_MASK | 用于定义worker进程和哪颗CPU有”亲和性”,worker进程就会只在指定的CPU上运行了。CPU使用CPU掩码指定2。也可在CPU掩码前加入”auto”令其自动在指定CPU设置亲和性 |
worker_priority NUM | 设置worker进程的nice值以调整其优先级,默认值0。这个指令的参数范围也就是nice值范围(-20至19) |
查看上述配置效果:
验证worker进程绑定CPU效果:
1、本虚拟机有2CPU核心:
[root@node1 ~]% lscpu | grep "CPU(s)"
CPU(s): 2
On-line CPU(s) list: 0,1
NUMA node0 CPU(s): 0,1
2、启动nginx,因为默认worker进程数是1,所以只启动1个worker进程:
[root@node1 ~]% nginx
[root@node1 ~]% ps axo command,pid,psr | grep nginx | grep -v grep
nginx: master process nginx 1986 1
nginx: worker process 1987 0
3、设置worker_processes为2,效果:
worker_processes 2;
[root@node1 ~]% ps axo command,pid,psr | grep nginx | grep -v grep
nginx: master process nginx 1986 0
nginx: worker process 1996 0
nginx: worker process 1997 1
由于只有2核心,所以master和某个worker运行在同1核心上了。
4、使两worker进程绑定在不同核心:
worker_processes 2;
worker_cpu_affinity 01 10;
[root@node1 ~]% ps axo command,pid,psr | grep nginx | grep -v grep
nginx: master process nginx 1986 0
nginx: worker process 2017 0
nginx: worker process 2018 1
前者worker运行于CPU0,后者运行于CPU1。
5、更换绑定的核心:
worker_processes 2;
worker_cpu_affinity 10 01;
[root@node1 ~]% ps axo command,pid,psr | grep nginx | grep -v grep
nginx: master process nginx 1986 0
nginx: worker process 2062 1
nginx: worker process 2063 0
前者worker运行于CPU1,后者运行于CPU0。
验证调整worker进程优先级效果:
1、默认情况下,nice值为0:
[root@node1 ~]% ps axo command,pid,ni | grep nginx | grep -v grep
nginx: master process nginx 1986 0
nginx: worker process 2062 0
nginx: worker process 2063 0
2、调整nice值为-5:
worker_priority -5;
[root@node1 ~]% ps axo command,pid,ni | grep nginx | grep -v grep
nginx: master process nginx 1986 0
nginx: worker process 2078 -5
nginx: worker process 2079 -5
3.2 events段
默认配置:
events {
worker_connections 1024; # 定义每个worker进程最大并发连接数
}
events段常用指令:
指令 | 意义 |
worker_connections NUM | 指定每个worker进程的最大并发连接数3。 |
accept_mytex on|off | 多个worker进程负载均衡,轮流处理请求,否则是由较快的worker进程处理新请求。默认开启 |
lock_file PATH | 如果多个worker进程负载均衡,需要锁文件。该选项用于指定锁文件路径。编译安装时也可通过./configure的选项指定 |
4 web服务相关配置
http段用于定义nginx作为web服务的配置。
http段结构:
http{
……
server{
【server1配置】
}
server{
【server2配置】
}
……
}
server就相当于httpd中的虚拟主机。不同之处是nginx没有中心主机,即便只有一个站点,也要使用一个server段定义。
在server段外定义的配置,对所有server生效。
server段常用配置指令(如无特别说明在,则指令均来自http核心模块):
4.1 server_name、root、listen
指令 | 意义 |
server_name | 指定虚拟主机名。相当于httpd虚拟主机段中的ServerName |
root | 指定页面文件根目录,如果使用相对路径,则相对的是编译安装目录(编译时–prefix指定的路径)。类似于httpd中的DocumentRoot,但不尽相同,root在location段中也常使用。定义root后,各指令所指定的相对路径,就是相对于root了 |
listen IP [:PORT] [OPTIONS] | 指定监听的地址、端口 |
这是一个server最简单配置所必须的3指令。
注意:
httpd中如果是基于IP区分的各虚拟主机,可不定义主机名。对于nginx,监听的地址、端口和server_name一般都要定义。
比如,定义两虚拟主机,监听在192.168.0.106主机的6666和8888端口:
server {
listen 6666; # 不指定IP表示监听在本机所有IP的6666端口
server_name www.6666.com;
root /var/www/6666;
}
server {
listen 8888;
server_name www.8888.com;
root /var/www/8888;
}
对应的不同主页内容:
[root@node1 ~]% cat /var/www/6666/index.html
Index Page from 192.168.0.106:6666
[root@node1 ~]% cat /var/www/8888/index.html
Index Page from 192.168.0.106:8888
访问效果:
4.1.1 listen指令常用选项
listen指令格式为:listen IP [:PORT] [OPTIONS]
,其后可以跟众多选项。常用options为:
选项 | 意义 |
default_server | 设置默认虚拟主机,即当没有虚拟主机符合请求时,使用默认虚拟主机响应 |
rcvbuf=SIZE | 接收缓冲区大小 |
sndbuf=SIZE | 发送缓冲区大小 |
ssl | 限制仅能通过ssl连接进行服务,即提供https服务。这时监听的端口应指定为443 |
4.1.2 server_name定义方式
定义server_name,用于使用户可根据主机名访问不同主机4。
定义主机名有以下几种方式:
方式 | 示例 |
精确主机名 | www.test.com |
左侧使用通配 | *.test.com |
右侧使用通配 | www.test.* |
正则表达式匹配 | ~www.test.com$~。要使用波浪线括起来 |
如果客户端在浏览器键入的主机名可以被上述方式中的多种匹配到,则优先级是:精确主机名–>左侧使用通配–>右侧使用通配–>正则表达式匹配。
4.2 location
使用格式:location [ = | ~ | ~* | ^~ ] uri { ... }
。表示对匹配到的URI,做指定配置。
使用范围:server段、location段
匹配URI的方式,按优先级依次为:
方式 | 意义 |
=PATH | 精确匹配路径 |
^~PATH | 使用正则表达式匹配URI的前半段 |
~PATH | 使用正则表达式匹配URI,区分大小写 |
~*PATH | 使用正则表达式匹配URI,不区分大小写 |
PATH | 直接使用PATH匹配,表示在PATH路径下的资源 |
即优先级是先匹配小范围,再匹配大范围。
官方文档的示例:
location = / { # 仅当URI为"/"时,使用A配置
[ configuration A ]
}
location / { # URI为"/"下包含的路径时,使用B配置
[ configuration B ]
}
location /documents/ { # URI为"/documents/"下包含的路径时,使用C配置
[ configuration C ]
}
location ^~ /images/ { # URI靠前部分为"/images/",使用D配置
[ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ { # URI结尾是gif、jpg或jpeg时,使用E配置
[ configuration E ]
}
按上述定义,和优先级规则。比如:
“www.test.com/”匹配A配置;
“www.test.com/test”、”www.test.com/example”匹配B配置,因为URI都在”/”下;
“www.test.com/documents/test.html”匹配C配置,虽然它也符合B配置,但能匹配到的越长优先级越高;
“www.test.com/images/test.html”匹配D配置,虽然它也符合B配置,但正则表达式匹配前端字符优先级高;
“www.test.com/documents/test.jpg”匹配E配置,虽然它也符合C配置,但正则表达式匹配高于普通字符串。
4.2.1 alias定义路径别名
指令alias,仅能定义在location段中,用于把location指定的URI定义别名。
比如:
location /test/ {
alias /data/test/;
}
表示把uri中的”/test”替换为”/data/test”。
以具体请求为例,上述的效果是把”http://www.test.com/test/index.html“访问的资源替换为服务端文件系统路径的”/data/test/index.html”,而不是”http://www.test.com/data/test/index.html“。也就是,alias的参数是服务端资源的文件系统路径,不是在原URI上做的替换5。
以106主机为例,配置server:
server{
listen 80;
server_name www.host.com;
root /var/www/;
access_log /var/log/nginx/access_log;
}
分别有文件:
[root@node1 ~]% cat /var/www/host1/host.html
@host1
[root@node1 ~]% cat /var/www/host2/host.html
@host2
访问效果:
修改配置文件,添加路径别名:
server{
listen 80;
server_name www.host.com;
root /var/www/;
access_log /var/log/nginx/access_log;
location /host1/ {
alias /var/www/host2/;
}
}
效果,访问host1,返回的是host2目录下的host.html:
特别地,当alias定义的某路径A的别名为/PATH/A,即把某路径A的别名定义为指定路径下的A,则功能同root。比如:
location /test/ {
alias /var/www/test/;
}
相当于:
location /test/ {
root /var/www;
}
即把指定路径作为location的URI的根路径。如果出现这种情况,最好就使用root了。
4.3 index定义主页
指令index来自模块ngx_\http_index_module。用于定义主页。
使用范围:http段、server段、location段。定义在不同的段有不同的生效范围。默认值是index.html
比如server段配置为:
server{
listen 80;
server_name www.host.com;
root /var/www/;
access_log /var/log/nginx/access_log;
index host1/host.html;
}
[root@node1 ~]% cat /var/www/host1/host.html
@host1
直接访问IP,效果:
4.4 error_page定义错误页面
指令error_page,用于自定义错误页面。
使用格式:error_page code ... [=[response]] uri
。表示根据响应码code(可指定多个),返回给客户端uri指定的页面。
使用范围:http段、server段、location段、location段中的if语句
比如,在106主机定义文件,用于响应码为404的情况:
[root@node1 ~]% cat /var/www/error/404.html
The 404 page from 192.168.0.106
在server段定义:
server{
listen 80;
server_name www.host.com;
root /var/www/;
error_page 404 /error/404.html; # 参数是URI,所以写的不是绝对路径
}
效果:
有的访问不到,可能是浏览器原因,360、qq浏览器都显示的是它们定义的错误页面。
1、上述的404.html也是个页面文件,且正常显示了,那么是否响应码就成200了?
打开浏览器调试界面,可以看到响应码就是404,虽然返回的是一个页面文件6。
不过如果需要专门修改错误页面的响应码,则使用error_page指令的”=response”即可。
比如server段的error_page改为:
error_page 404 =200 /error/404.html;
效果是返回自定义的404错误页面,状态码是200:
不过最好还是使用默认的,状态码乱改容易出问题。
2、error_page的实质是把对指定URI的错误请求,重定向至一个自定义页面。相当于客户端重新访问了服务端(只是相当于。其实这个过程客户端并不参与,由nginx自动进行),只不过请求的资源被重定向为了自定义的错误页面。
为此,要避免出现如下情况。比如在106主机的server段定义:
server{
listen 80;
server_name www.host.com;
root /var/www/;
location /test1 {
error_page 404 /test1_error.html;
}
}
定义错误页面:
[root@node1 ~]% cat /var/www/test1_error.html
<h1>test1_error</h1>
由于是在/test1的location中自定义了错误页面,所以访问test1路径才会有:
如果在server段中增加一个location,要避免造成循环。即:
- location1中定义的error_page符合location2指定的URI;
- location2中定义的error_page恰好又符合location1指定的URI。
如果触发location1的error_page,于是重新匹配到location2中的错误页面,而后又重定向到location1的……最终可能会返回500。
避免这种情况,主要是要定义好location指定的URI,个人认为如果能使用精确匹配,最好使用精确匹配,这样应该较不容易出现循环。
这种情况就不演示了,在下文rewrite指令中有类似情况详细说明。
4.5 长连接相关指令
指令 | 意义 |
keepalive_timeout | 用于指定长连接超时时长。默认值75秒。只要该指令指定数字不为0则表示启用了长连接,0为关闭 |
keepalive_requests | 用于指定一次长连接所能获取的资源数量,默认100个。意义同httpd的 |
keepalive_disable | 用于指定对哪些浏览器禁用长连接。因为有的浏览器不支持长连接。默认是对ie6禁用 |
tcp_nodelay on|off | 仅在长连接时生效,指定是否不延迟(因缓存小数据的tcp报文造成的延迟)tcp报文,默认开启 |
其他容易理解,主要说下tcp_nodelay。
需先说明tcp的一个机制:
- tcp连接在有用户请求小数据(比如1个字节大小)且是长连接时,如果给这种数据单独封装报文发送,往往浪费开销(各首部大小就已经大于数据大小了)。
- 默认情况下,tcp协议对这种场景有优化机制:自动将其先缓存下来,待有多个小数据报文等待发送时,把他们封装成一个报文一并发送,以节约带宽。
这种机制对于web服务器可能不适用,造成的结果是:用户如果请求的就是一个小数据,可能半天等不到,需要请求多个资源才会响应。
tcp_nodelay就是指定不使用这种机制。当然它默认是开启的,一般也无需关注。
4.6 限制响应报文发送速率
指令limit_rate,用于限制响应报文的发送速率(比如用于下载站点限速)。
使用范围:http段、server段、location段、location段中的if语句
使用格式:limit_rate NUM
,参数的单位是字节/秒。默认是0,表示不限制速率。
使用对应的变量来配置,也具有相同的意义:
server {
if ($slow) {
set $limit_rate 4k; # 这种方式通常用于,是否限速依赖于指定条件的时候
}
...
}
验证效果:
1、106主机server段配置为:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /test {
limit_rate 10k;
}
}
2、把/var/log/messages目录复制到/var/www/test下,并开启读权限。
3、使用wget下载,速率大约就限制在10k:
4.7 文件元数据信息缓存
缓存用户请求的文件的元数据信息(文件描述符、大小、最近修改时间等),作用是在再次响应相同文件时能够快速响应(尤其是根据最近修改时间,可知请求的资源是否发生过变化)。
相关指令:
指令、格式 | 意义 |
open_file_cache max=N [inactive=time] | 指定是否开启文件元数据缓存,默认off。如启用需指定max=NUM,表示指定缓存最大记录数;inactive指定非活动时长(默认60秒),经过该时长未被访问到的记录(或低于open_file_cache_min_uses指定的次数)被视为非活动缓存 |
open_file_cache_errors on|off | 如果开启文件元数据缓存,则该指令指定是否缓存访问失败的信息(包括没有指定资源、没有权限等)。默认off |
open_file_cache_min_uses | 与openssh_file_cache指令中的incative参数关系密切,它定义的是在incative定义的时间内,未达到指定访问次数的记录,均被视为非活动(即需清除)。默认为1 |
open_file_cache_valid | 设置多长周期检查一次缓存,以清除非活动缓存记录。默认60秒 |
如果启用文件元数据缓存,则这几个指令一般一起使用。官方文档示例:
open_file_cache max=1000 inactive=20s; # 最大记录数1000,非活动时长20秒
open_file_cache_valid 30s; # 每30秒检查、清理非活动缓存记录
open_file_cache_min_uses 2; # inactive期间被访问低于2次,就被视为非活动缓存记录
open_file_cache_errors on; # 缓存访问失败的文件信息
4.8 访问控制
指令allow、deny用于访问控制,来自模块ngx_http_access_module。
使用范围:http段、server段、location段、limit_except指令。
官方文档示例:
location / {
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
deny all;
}
检查次序由上而下。
上述规则允许访问请求的源IP为192.168.1.0/24网段(但拒绝192.168.1.1)、10.1.1.0/16网段。其他均拒绝。
访问控制效果同httpd的,此处不赘述了。
4.9 basic认证
nginx也支持http basic认证协议,通过用户名、密码认证用户,同httpd中的一样。基于模块ngx_http_auth_basic_module。
相关指令:
指令 | 意义 | 使用范围 |
auth_basic STRING | off | 设置是否启用basic认证。STRING表示启用,并且就是提示框中的信息 | http段、server段、location段、limit_except指令 |
auth_basic_user_file | 指定用户名、密码存储的文件(可用htpasswd或openssl passwd等生成) | http段、server段、location段、limit_except指令 |
演示效果:
1、创建用户、密码存放的认证文件,并添加用户user1:
[root@node1 ~]% htpasswd -c -m /var/www/auth_basic user1
New password:
Re-type new password:
Adding password for user user1
2、在nginx配置文件中设置:
server {
listen 80;
server_name www.test.com;
root /var/www;
location / {
auth_basic "You must input password.";
auth_basic_user_file /var/www/auth_basic; # 注意,参数是服务端的文件绝对路径,而不是相对于root的URI
}
}
3、访问时需键入用户名、密码:
输入正确后可访问:
不输入则返回401:
4.10 limit_except
使用范围:location段
可对指定请求方法的报文做限制。请求方法有很多,特别地,只要GET请求方法被允许,则HEAD也自动被允许。
注意,该命令本身不具备限制功能,要结合标题4.8、4.9的各访问控制指令、认证指令才能完成。该命令只是匹配对应请求方法的报文
使用格式:limit_except method ... { ... }
比如,在某location中定义:
limit_except GET {
allow 192.168.1.0/32;
deny all;
}
意义为,只要请求报文的请求方法不是GET(和HEAD),则:
- 仅allow网段192.168.1.0/32发来的请求
- 拒绝其他所有主机的请求
所以,命令limit_except可看作一个限制请求方法的容器。起到控制作用的是,定义在其中的访问控制或basic认证指令。
示例:
1、192.168.0.106主机运行nginx,定义server段中的location如下:
location / {
limit_except GET {
deny 192.168.0.106;
allow 192.168.0.61;
}
}
2、106主机和61主机分别访问效果:
在106主机:
[root@node106 ~]% curl http://192.168.0.106/test.html
<h1>test [email protected]</h1>
在61主机:
[root@node61 ~]% curl http://192.168.0.106/test.html
<h1>test [email protected]</h1>
因为限制的方法是除GET以外的方法,所以GET请求没有受影响,都是允许访问的
3、 改为限制除PUT以外的方法的报文,则GET请求就会受影响了:
location / {
limit_except PUT {
deny 192.168.0.106;
allow 192.168.0.61;
}
}
效果就是106被拒绝、61可访问:
[root@node106 ~]% curl http://192.168.0.106/test.html
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.8.1</center>
</body>
</html>
[root@node61 ~]% curl http://192.168.0.106/test.html
<h1>test [email protected]</h1>
在limit_except中,定义basic认证也是类似效果不再赘述。
由此也可以看出,limit_except可以通过匹配指定请求方法的报文,使访问控制更加灵活,但它本身不具备访问控制功能。
4.11 状态信息页面
指令stub_status,用于输出nginx基本状态信息。来自模块ngx_http_stub_status_module。
注意,模块ngx_http_stub_status_module默认不会装载,在编译时需指定选项”–with-http_stub_status_module”才可。
使用范围:server段、location段
使用格式很简单,只需在指定server或location中使用指令即可,比如:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /status { # 此URI可任意指定。在此URI的location中使用stub_status指令,只要访问的是这个URI,就会显示status页面
stub_status;
}
}
内容意义:
1、Active connections,活动状态的连接数。
- 活动状态包括:已经建立连接等待发送响应报文、服务端正在加载资源、正在发送响应报文等
- 活动状态连接数至少有1个,因为在访问status页面的一刻,访问status页面的连接就是处于活动状态
2、第三行的3数字分别为:服务端已经接受过的请求总数、已经处理完成的请求总数、客户端发来的请求总数。
- 3个数字统计的都是“已经发生过的”连接信息
- 第3个数字往往是最多的,因为它包含了客户端发来的所有请求数,包括服务器接受、处理的请求数,也包括未接受、或接受后拒绝处理的请求数(比如访问控制、请求方法限制等)
3、最后3数字分别为:reading为服务端正在读取请求报文首部的连接数;writing为服务端正在发送响应报文的连接数;waiting为服务端正在等待客户端发出请求的连接数(往往是长连接时间还没到,等待客户端下一个请求)。
- 3个数字统计的都是“正在发生的”连接信息。所以它们之和应等于Active connections
4.12 防盗链
指令valid_referers,来自模块ngx_http_referer_module。
用于指定请求报文中的referer首部的哪些值是合法的。从而结合内置变量invalid_referer,对从非法地址跳转而来的请求予以拒绝等操作。
使用范围:server段、location段
参数:
参数 | 意义 |
none | 请求报文中没有referer首部。一般定义为合法 |
blocked | 请求报文中有referer首部,但值为空。一般定义为合法 |
server_names | 指定哪些主机名为合法。可使用字符串(*表示任意长度任意字符),也可使用正则表达式(~开头)匹配主机名 |
使用示例:
valid_referers none blocked server_names # 定义请求报文referer首部不存在、为空或符合下面列出的两种主机名,为合法
*.example.com example.* www.example.org/galleries/ # 字符串匹配主机名
~\.google\.; # 正则表达式匹配主机名
if ($invalid_referer) { # 定义合法referer首部之后,可使用内置变量invalid_referer作为条件,从而对非法referer首部的请求报文拒绝处理,比如返回403或重定向至另一位置
return 403;
}
通过referer首部值的设定,就可限制某些请求报文是从不希望的站点跳转而来,从而限制盗链。
但是referer请求首部也是容易伪装的,所以不会达到彻底地过滤。
4.13 作为https服务端
同httpd配置https服务端类似,只是指令不同。单IP主机也是只能配置一个https服务端。
ngx_http_ssl_module模块也是不会默认装载,编译时需指定”–with-http_ssl_module”。
常用指令:
指令 | 意义 |
ssl on|off | 指定是否启用ssl。类似httpd中的”ssl engine on|off” |
ssl_certificate FILE | 指定当前server使用的证书文件 |
ssl_certificate_key FILE | 指定当前server使用的私钥文件 |
ssl_protocols | 指定使用的ssl协议版本,一般使用默认即可 |
ssl_session_cache | 指定是否启用ssl会话缓存。由于ssl会话比较消耗资源,所以可使用缓存把各密钥信息缓存下来,同一客户端再访问时可节约资源 |
ssl_session_timeout | 指定ssl会话缓存的超时时长(虽然字面意思像是ssl会话超时时长),默认5分钟。 |
其他命令容易理解,主要说明下ssl_session_cache的各参数:
- off:不使用ssl缓存,并明确告知客户端ssl会话不可重复用
- none:“礼貌”地不使用ssl缓存,告知客户端ssl会话可重复用,实际不缓存会话参数。默认是none
- builtin [:size]:openssl的内建缓存机制,每个worker进程独占一段内存空间作为缓存。
- 可指定大小,默认大小是20480条会话
- 这种机制会产生内存碎片,而且如果不同worker进程先后处理同一请求,先前worker进程缓存的内容就无法命中了。
- shared [name:size],与builtin相对,shared表示各worker进程共享一段内存空间作为缓存。
- 使用shared,要指定缓存名称
- 可指定大小,以字节为单位。1M空间可缓存4000个会话
- 虽然shared和builtin可同时使用,比如
ssl_session_cache builtin:1000 shared:SSL:10m
,但单独使用shared效果更好(官网文档描述)。 - 所以如需缓存ssl会话就是用参数shared吧
下面演示nginx作为https服务端效果。
由106主机扮演https服务端,116主机扮演CA:
1、在116主机,创建CA所需要的数据库文件index.txt和序列号文件serial,并输入起始序列号01:
[root@node116 ~]% ls /etc/pki/CA
certs crl newcerts private
[root@node116 ~]% touch /etc/pki/CA/{index.txt,serial} # 创建这两个文件
[root@node116 ~]% ls /etc/pki/CA
certs crl index.txt newcerts private serial
[root@node116 ~]% echo 01 > /etc/pki/CA/serial # 输入起始序号01
2、在116主机,生成自己作为CA的私钥文件cakey.pem:
[root@node116 ~]% openssl genrsa -out /etc/pki/CA/private/cakey.pem 1024
Generating RSA private key, 1024 bit long modulus
.......................................................++++++
...................................................................++++++
e is 65537 (0x10001)
3、在116主机,生成自签证书cacert.pem:
[root@node116 ~]% openssl req -new -x509 -key /etc/pki/CA/private/cakey.pem -out /etc/pki/CA/cacert.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Taiwan
Locality Name (eg, city) [Default City]:Taipei
Organization Name (eg, company) [Default Company Ltd]:hah
Organizational Unit Name (eg, section) []:heh
Common Name (eg, your name or your server's hostname) []:www.CA.com
Email Address []:
4、在106主机生成私钥,生成证书申请,并复制给116主机:
[root@node106 ~]% openssl genrsa -out 106_key 1024 # 生成私钥
Generating RSA private key, 1024 bit long modulus
............++++++
......++++++
e is 65537 (0x10001)
[root@node106 ~]% openssl req -new -key 106_key -out 106.csr # 生成证书申请
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Taiwan
Locality Name (eg, city) [Default City]:Taipei
Organization Name (eg, company) [Default Company Ltd]:hah
Organizational Unit Name (eg, section) []:heh
Common Name (eg, your name or your server's hostname) []:www.node106.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@node106 ~]% scp 106.csr 192.168.0.116:/tmp # 复制申请至116主机
5、在116主机,签署请求并复制回106主机:
[root@node116 ~]% openssl ca -in /tmp/106.csr -out /tmp/106.crt # 签署请求
……
[root@node116 ~]% scp /tmp/106.crt 192.168.0.106:/root # 复制回106主机
6、在106主机,配置nginx为https服务端:
为方便起见,把证书和私钥都放在一个目录:
[root@node106 ~]% mkdir /var/ssl
[root@node106 ~]% mv 106.crt 106_key /var/ssl
nginx编译时只要编译了ssl模块,配置文件中就会有对应配置模板。这里配置一个server段为如下配置:
server {
listen 443 ssl; # 监听端口为443,且必须使用选项ssl
server_name www.test.com;
root /var/www;
ssl_certificate /var/ssl/106.crt;
ssl_certificate_key /var/ssl/106_key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
}
此时就已经能进行https通信了,不过只是通信过程加密,无法验证身份。因为客户端还没有信任116这个CA。
7、导入116主机的自签证书至客户端浏览器(很简单不再截图赘述了);并且要把www.node106.com解析为192.168.0.106的记录导入值windows的hosts文件中(因为笔者客户端使用的是windows),因为证书申请上填写的是这个主机名,访问时要使用主机名,不能使用IP地址。
查看访问效果:
4.14 日志相关
ngx_http_log_module引入指令access_log、log_format、open_log_file_cache。
4.14.1 access_log
使用范围:http段、server段、location段、location段中的if语句、limit_except指令中
使用格式:
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
各参数意义:
参数 | 意义 |
path | 指定访问存放位置。不同server当然应该存放在不同位置,否则难以查看 |
format | 指定日志格式 |
buffer=size | 指定缓冲区大小。当有大量并发时,来不及把日志内容写入磁盘,则先写在缓冲区,周期刷写至磁盘 |
flush=time | 指定缓冲区内容刷写至磁盘的周期时间 |
gzip[=level] | 使用压缩并指定级别 |
syslog:server= | 指定日志存放在syslog服务器。如syslog:server=192.168.1.1等。如需使用syslog,该参数需放在最前端 |
默认值:
access_log logs/access.log combined # 默认,路径定义在编译安装时--prefix指定的路径下的logs/access.log下,格式为combined(这个格式定义同httpd的)
4.14.2 log_format
使用格式:log_format name string...
,定义日志格式名name,并为此格式名定义各字段。
类似httpd中的定义方式,只不过httpd中用的是各种宏,这里用的是内置变量。
使用范围:http段
各内置变量基本见名知意,比如combined格式的默认定义:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
各内置变量具体含义可在官网查询,不赘述了。
4.14.3 open_log_file_cache
使用格式:
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
指定是否缓存日志文件的文件描述符,提高打开日志文件的效率以更新日志文件。默认关闭。
参数意义:
参数 | 意义 |
max=N | 指定最大缓存条目数。数量达到最大时,使用最近最少使用算法(LRU)清理多余条目 |
inactive=time | 指定非活动时长。默认10秒 |
min_uses=N | 指定在非活动时长内,至少要被访问多少次,否则就视为非活动缓存(需清理) |
valid=time | 指定有效性检查时间,即多久检查一次缓存的文件描述符对应的文件是否还存在或是否改名。默认60秒 |
4.14.4 error_log
该指令在nginx核心模块Core functionality,上述访问日志的各指令在ngx_http_log_module模块。
写在这里是因为它们都是记录日志。error_log用于定义错误日志。
使用范围:main段、http段、mail段、stream段、server段、location段
使用格式:
error_log file | [syslog] | [stderr] | [memory] level # file表示错误日至记录在哪个文件;
# 也可记录在指定的syslog服务器
# 也可输出至标准错误输出(stderr)
# 也可记录在内存中(memory)。性能好但不会永久保存,一般调试时使用
日志级别严重程度由低到高依次为:debug, info, notice, warn, error, crit, alert, emerg
每个级别输出的信息是当前级别以及比它更严重级别的信息。不过如要使用debug级别,编译时要指定–with-debug选项才可。
默认配置:
error_log logs/error.log error
4.15 模块ngx_http_rewrite_module
4.15.1 rewrite
指令rewrite用于重定向请求的URL。
使用范围:server段、location段、if语句
使用格式:rewrite regex replacement [flag]
,把regex(可以是正则表达式)匹配到的URI,重定向为replacement指定的URL。
重定向的路径可以是任何其他路径,如本server段的其他资源路径、当前服务端的其他server段的资源路径、其他web服务端的资源路径等
4.15.1.1 机制
1、重定向规则可定义多条,由上而下依次生效,所有规则都检查完才会返回指定资源给客户端(类似iptables的检查方式)
2、重定向后的URI要和服务端各location重新匹配一次,就像处理一个新请求,不过都是在服务端内部自动进行,客户端并不知道资源被重定向了。服务端把重定向路径对应的资源返回给客户端。
以下示例说明这两规则。
比如在某server段中定义(为演示方便,只是抽象地定义):
server {
listen ...;
server-name ...;
root ...;
location uri-[123] { # 此处使用uri-[12],用于代表该location能匹配到uri-1、2、3
rewrite uri-1 uri-10;
rewrite uri-2 uri-20;
rewrite uri-3 uri-30;
}
location uri-30 {
rewrite uri-30 uri-300;
}
}
用户访问的是uri-1,则过程为:
- uri-1先被第1个location匹配到,并重定向为uri-10。该location中后续的重定向规则依然会执行,直至最后一条uri-3的重定向规则结束(虽然后两条没有起作用)
- uri-10会重新匹配这两location(就像一个新请求),这时没有location会匹配到。于是服务端要取得的就是uri-10对应的资源,并返回给客户端
用户访问的是uri-3,则过程为:
- uri-3先被第1个location匹配到,前两条规则无效,被重定向为uri-30
- uri-30会重新匹配这两location(就像一个新请求),于是uri-30又会被第2个location匹配到,从而又重定向为uri-300
- uri-300再次匹配,没有locaton会匹配到。于是服务端最终返回的是uri-300对应的资源
注意,上述重定向过程客户端并不知道。
3、当替换为的URI使用的是”http://……”、”https://……”等指定的(相对于URI,这看上去就像是“绝对路径”),则直接把替换为的URL【返回给客户端】,不再进行后续规则检查。客户端浏览器会根据得到的重定向的URL自动【重新访问】
比如在server段定义:
server {
listen ...;
server-name www.test.com;
root ...;
location uri-[123] {
rewrite uri-1 uri-10;
rewrite uri-2 http://www.test.com/uri-20;
rewrite uri-3 uri-30;
}
location uri-30 {
rewrite uri-30 uri-300;
}
}
客户端访问的是uri-2,则过程为:
- uri-2被第一个location匹配到,并重定向为 http://www.test.com/uri-20
- 不再处理后续的uri-3重定向规则(虽然这里该规则也不会起作用,但就算有起作用的规则也没用,因为直不再处理了),直接把URL http://www.test.com/uri-20 返回给客户端浏览器
- 浏览器自动根据新的URL访问服务器
上述重定向过程客户端是知道的,因为浏览器的地址栏会自动变为重定向后的URL。
4、要避免循环重定向
比如,server段配置为:
server {
listen ...;
server-name www.test.com;
root ...;
location uri-1 {
rewrite uri-1 uri-2;
}
location uri-2 {
rewrite uri-2 uri-1;
}
}
客户端如果访问uri-1,则服务端会不断地循环重定向,而无法返回客户端
为避免循环重定向或过多的无谓的重定向,nginx执行重定向最多10次,超过10次的均响应500
4.15.1.2 基本效果
1、在106主机/var/www下定义两文件:
[root@node106 ~]% cat /var/www/test1.html
<h1>test1</h1>
[root@node106 ~]% cat /var/www/test2.html
<h1>test2</h1>
nginx配置文件中定义:
server {
listen 80;
server_name www.test.com;
root /var/www;
}
访问效果及对应的响应码:
2、在106主机的server段定义如下规则:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /test1.html {
rewrite /test1.html /test2.html;
}
}
访问效果及响应码:
可以看到内容确实重定向了。
如上所述,客户端并不知道访问的资源被重定向了,因为键入的地址栏内容并没有变化,且响应码仍是200。
4.15.1.3 循环重定向问题
如果把上述的server段改为:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /test1.html {
rewrite /test1.html /test2.html;
}
location /test2.html {
rewrite /test2.html /test1.html;
}
}
访问则报错500:
此时从错误日志也可看出出现了循环重定向(笔者的客户端地址是105):
[root@node106 ~]% tail -n 1 /usr/local/nginx/logs/error.log
2017/12/07 11:27:38 [error] 1952#0: *21 rewrite or internal redirection cycle while processing "/test2.html", client: 192.168.0.105, server: www.test.com, request: "GET /test1.html HTTP/1.1", host: "192.168.0.106"
- 1
- 2
过程分析:
1、http://192.168.0.106/test1.html 被第一个location匹配到,重定向为http://192.168.0.106/test2.html;
2、http://192.168.0.106/test2.html 与服务端各location重新匹配,被第2个location匹配到,重定向为test1.html7;
3、不断重复1、2,无法返回客户端。
所以要注意,重定向后的资源路径,不要被其他规则直接或间接地再重定向为初始的URI。
上述示例较为明显,因为location后跟的是字符串,如果是正则表达式,则更要注意是否会出现循环匹配(个人认为在确定的情况下,可使用rewrite规则的flag为break,减少uri被各location不停匹配)。
4.15.1.4 替换为的路径是”http://……”、”https://……”
如标题4.14.1.1机制所述,替换为的路径是带协议、地址、URI的完整路径,则会直接返回给客户端浏览器,客户端浏览器根据新URL自动重新访问。
演示效果:
1、把106主机配置改为:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /test1.html {
rewrite /test1.html http://192.168.0.106/test2.html;
}
}
2、此时仍访问 http://192.168.0.106/test1.html,注意键入浏览器地址栏的是test1.html。访问结果及响应码:
可以看到,访问结果不仅被重定向为了test2.html,且地址栏键入的URI也自动变为了test2.html。
这里的响应码应为302(可使用curl验证),不过笔者使用的浏览器还一直是200,不知为何。
4.15.1.5 各flag
rewrite指令格式中,可看到每个rewrite规则可以在最后加上flag。
flag | 意义 |
last | 重定向规则后如果有last,表示被此重定向规则处理后,不再执行后续的重定向规则,直接以新的uri当作一个新请求重新被nginx处理(即会被nginx各locaton重新匹配处理)。这就像循环语句中的continue |
break | 重定向规则后如果有break,表示被此重定向规则处理后,不再执行后续的重定向规则。服务端直接响应给客户端新uri对应的资源。这就像循环语句中的break |
redirect | 重定向规则后如果有redirect,就和直接重定向至带协议的完整URL一样(同标题4.14.1.4中效果),直接把URL返回给客户端 |
permanent | 同redirect,只是响应码为301(永久重定向) |
说明last作用,假设server段配置如下:
server {
listen ...;
server-name www.test.com;
root ...;
location uri-[123] {
rewrite uri-1 uri-10;
rewrite uri-2 uri-20 last;
rewrite uri-20 uri-200;
}
location uri-20 {
rewrite uri-20 uri-22;
}
}
客户端如果访问的是uri-2,则过程为:
- 被第1个location匹配到,重定向为uri-20
- 不执行后续的规则
rewrite uri-20 uri-200
,而是重新匹配各location - 被第2个location匹配到,重定向为uri-22
- uri-22重新被处理,但不会被各location匹配到,于是服务端返回uri-22对应的资源给客户端(而不是uri-200)
说明break作用,假设server段配置如下:
server {
listen ...;
server-name www.test.com;
root ...;
location uri-[123] {
rewrite uri-1 uri-10;
rewrite uri-2 uri-20 break;
rewrite uri-20 uri-200;
………… # 虽然break跳过了重定向的其他规则,但location中如果有其他配置,还是要执行的
}
location uri-20 {
rewrite uri-20 uri-22;
}
}
客户端访问的是uri-2,过程:
- uri-2被第1个location匹配,重定向为uri-20
- 不再执行后续规则,也不再作为一个新请求重新被各location匹配。要返回的资源就是uri-20对应的资源
- 虽然break跳过了重定向的其他规则,但location中如果有其他配置,还是要执行的
说明redirect、permanent作用:
在106主机定义:
server {
listen 80;
server_name www.test.com;
root /var/www;
location /test1.html {
rewrite /test1.html /test2.html redirect;
}
}
访问效果:
[root@node106 ~]% curl http://192.168.0.106/test1.html
<html>
<head><title>302 Found</title></head> # 响应码302
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.8.1</center>
</body>
</html>
改为permanent:
[root@node106 ~]% curl http://192.168.0.106/test1.html
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center> # 响应码301
<hr><center>nginx/1.8.1</center>
</body>
</html>
可能是笔者的浏览器有问题,响应码一直是200。这里就用curl验证了。
4.15.2 rewrite_log
用于指定是否开启重定向日志,默认关闭。
如开启,则记录位置是在error_log中,级别为notice
4.15.3 if
if指令用于根据指定条件是否满足,执行相关配置。
使用格式:if (condition) { ... }
使用范围:server段、location段
4.15.3.1 常用条件
条件 | 意义 |
变量名8 | 变量值为空或0时,表示“假” |
=或!= | 比较两变量相等或不等 |
~或!~ | 正则表达式匹配或不匹配,区分大小写。如有“}”、“;”等字符,则需使用双引号或单引号引起来 |
~*或!~* | 同上,只是不区分大小写 |
-f或!-f | 一个文件存在或不存在 |
-d或!-d | 一个目录存在或不存在 |
-e或!-e | 一个文件、目录、链接文件存在或不存在 |
-x或!-x | 一个文件是否可执行 |
4.15.3.2 示例
官方文档部分示例:
if ($request_method = POST) { # 请求方法是POST时,返回405
return 405;
}
if ($slow) { # 内置变量slow为真时,限速10k
limit_rate 10k;
}
if ($invalid_referer) { # 内置变量invalid_referer为真(即referer请求首部非法),返回403
return 403;
}
比如,在106主机,配置nginx令访问的uri包含test字符串的,均返回403:
server {
listen 80;
server_name www.test.com;
root /var/www;
location / {
if ($uri ~ .*test.*) { # 内置变量uri表示当前请求的uri
return 403;
}
}
}
访问结果:
4.15.4 return
指令return表示停止当前处理,直接返回客户端。
使用格式:
return code [text]; # 返回指定状态码和原因短语(即状态码解释)
return code URL; # 返回指定状态码和一个URL。所以这个状态码一般指定为301、302等
return URL; # 直接返回一个URL,类似于重定向了
使用范围:server段、location段、if语句
这个容易理解,不示例赘述了。
4.15.5 set
用于自定以变量。
使用格式:set $variable value;
,注意,同bash不同,这里引用和定义都要加上“$”。
使用范围:server段、location段、if语句
4.16 响应报文压缩
模块ngx_http_gzip_module用于压缩响应报文,相关指令:
指令 | 范围 | 意义 |
gzip on|off | http段、server段、location段、location中的if语句 | 是否启用压缩响应报文,默认off |
gzip_comp_level NUM | http段、server段、location段 | 指定压缩级别,默认为1 |
gzip_disable regex | http段、server段、location段 | 对指定浏览器类型的响应报文,不进行压缩。因为有的浏览器也根本不支持压缩 |
gzip_min_length length | http段、server段、location段 | 指定最小压缩数据长度,默认20。因为对过小的数据压缩效果不明显。该指令是根据响应报文首部“Content-Length”判断报文长度的 |
gzip_http_version | http段、server段、location段 | 指定启用压缩功能的最低http协议版本,默认1.1 |
gzip_type | http段、server段、location段 | 指定对哪些MIME类型的资源压缩,默认text/html |
这个也容易理解,不赘述演示了。
4.17 模块ngx_http_fastcgi_module
同httpd,nginx也是只能响应静态资源。如果要响应php动态资源,需要与php结合。
httpd和php有3种结合方式:cgi、php作为模块、fastcgi。nginx只有fastcgi。
模块ngx_http_fastcgi_module就是用于让nginx作为客户端与后端的php-fpm通信。类似httpd的类似于httpd的proxy_fcgi_module。不过它还包含缓存等功能。
4.17.1 lnmp
如果编译时指定了fastcgi模块,则配置文件中会有样例:
location ~ \.php$ {
root html; # 因为要把请求转发给后端php-fpm服务器,所以此处的root不会起到作用
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
include fastcgi_params;
}
表示以“.php”结尾的uri请求,交由php-fpm服务端处理,实际使用时,参数根据实际需要修改即可。
该location配置在一个server段中就可令一个server具备处理“.php”资源的能力。
各指令意义:
指令 | 意义 |
fastcgi_pass | 指定php-fpm服务端的地址和端口。也可以是主机名 |
fastcgi_index | 主页。如果请求的uri是以“/”结尾,则会自动补在后面 |
fastcgi_param | 用于指定向后端的php-fpm服务端传递的参数和值 |
fastcgi_pass指令容易理解,主要说明下后两个。
4.17.1.1 fastcgi_param
当用户访问php动态资源时,nginx代理用户访问后端的php-fpm服务端。
nginx必须向php-fpm服务端传递一些参数(存储在变量中),使php-fpm服务端按要求响应资源
fastcgi_param使用格式:fastcgi_param parameter value [if_not_empty]
比如最主要的,nginx要告知php-fpm,客户端访问的php资源名(即php脚本名)是什么。如上述样例语句中的:
location ~ \.php$ {
……
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
……
}
其中:
- “SCRIPT_FILENAME”是要传递给php-fpm的变量名9,表示客户端要请求的php资源名。
- 把该变量赋值为”/scripts$fastcgi_script_name”,其中”$fastcgi_script_name”是内置变量。这里意义就是把内置变量”$fastcgi_script_name”的值结合前面的字符串“/scripts”作为值,传递给”SCRIPT_FILENAME”
- 前面的“/scripts”表示该资源在php-fpm服务器上的文件系统的路径,这个可根据实际情况调整
可以看到这个指令和lamp中,httpd向php-fpm的代理请求配置异曲同工:
ProxyPassMatch ^/(.*\.php)$ fcgi://Server:Port/FILE_PATH/$1
作用都是把符合要求的uri(比如以.php结尾),转发给后端的php-fpm服务端,并需要指明请求资源在php-fpm服务端上的路径。
不同仅在于形式上:
- httpd在ProxyPassMatch指令中筛选要发往php-fpm服务端的uri,nginx是利用location筛选;
- httpd在ProxyPassMatch指令中就说明了资源在php-fpm服务端的哪个路径下。nginx是在location中使用fastcgi_param告知php-fpm服务端的。
不过nginx除了要“告知”php-fpm服务端访问的资源名是什么,还有很多其他参数要传递,不过这些也大多无需修改,直接写在文件fastcgi_params中了:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
……
这就是为什么fastcgi样例配置中include fastcgi_params;
。
4.17.1.2 fastcgi_index
如4.16.1.1中示例,传递请求的资源名要使用内置变量$fastcgi_script_name的值,传递给后端php-fpm服务端。
设置fastcgi_index作用为,若有如下设置(fastcgi其他设置省略):
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;
当用户请求的uri是/page.php
,则实际请求的是php-fpm服务端上的/home/www/scripts/php/page.php
当用户请求的uri以“/”结尾,比如就是/
,则实际请求的是php-fpm服务端上的/home/www/scripts/php/index.php
只要最后以“/”结尾,就用定义的主页文件名补全。就这意思。
4.17.1.3 lnmp安装discuz
由于在博文lamp中已说明过安装配置步骤,此处只是把httpd换成nginx构建lnmp。所以直接演示配置操作,不做额外说明了。以安装php程序discuz为例:
1、环境说明:
3台CentOS 6.8主机:
node106,IP为192.168.0.106,运行nginx;
node61,IP为192.168.0.61,运行php-fpm;
node62,IP为192.168.0.62,运行mysql;
2、在106主机,配置nginx为:
server {
listen 80;
server_name www.test.com;
root /var/www;
location ~ \.php$ {
root /var/www/php;
fastcgi_pass 192.168.0.61:9000; # php-fpm服务端是61主机的9000端口
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/php$fastcgi_script_name; # 资源在61主机的/var/www/php目录下
include fastcgi_params;
}
}
3、在61主机,配置/etc/php-fpm.d/www.conf:
listen = 192.168.0.61:9000 # 监听地址、端口
……
listen.allowed_clients = 192.168.0.106 # 允许访问的主机
……
4、访问discuz:
注意,此处访问地址必须是 http://192.168.0.106/discuz/upload/install/index.php 。
如果最后不跟index.php,uri后缀就没有”.php”,根据配置文件中指定的条件,不会转发至php-fpm服务端,会在web服务器本地查找对应资源,报错404。
5、点击同意后继续进行,看到有的权限需打开,且需两配置文件:
6、在61主机。这两配置文件是有模板的,复制、改下文件名即可;其他按要求开放写权限:
[root@node61 ~]% cd /var/www/php/discuz/upload/config/
[root@node61 config]% ls
config_global_default.php config_ucenter_default.php index.htm
[root@node61 config]% cp config_global_default.php config_global_.php
[root@node61 config]% cp config_ucenter_default.php config_ucenter.php
[root@node61 ~]% cd /var/www/php/discuz/upload/
[root@node61 upload]% ls
admin.php archiver crossdomain.xml forum.php index.php member.php portal.php source uc_client
api config data group.php install misc.php robots.txt static uc_server
api.php connect.php favicon.ico home.php m plugin.php search.php template
[root@node61 upload]% chmod -R a+w data
[root@node61 upload]% chmod -R a+w uc_client
[root@node61 upload]% chmod -R a+w uc_server
[root@node61 upload]% chmod -R a+w config
7、全新安装,下一步:
8、因为下面要连接数据库了,所以在62主机,先创建对应的帐号、密码、数据库,并授予权限:
mysql> CREATE USER discuz_user IDENTIFIED BY 'discuz_pass';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE DATABASE discuz_db;
Query OK, 1 row affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON discuz_db.* TO discuz_user;
Query OK, 0 rows affected (0.00 sec)
9、在浏览器,继续安装过程:
数据库信息要与62主机上创建的保持一致。
这里方便起见,论坛管理员帐号也使用discuz_user了,实际可随意自定义。
10、安装完成效果:
显示了部分内容,但显示得不正常。
笔者用3台主机分别运行nginx、php-fpm、mysql,安装过程正常,但访问页面总是不显示图片等内容。如果不把这3这分别运行于3主机,就不会出现此问题,不只何解。
不过这里能显示内容,说明lnmp还是起作用了。。。
4.17.2 为fastcgi的提供缓存功能
fastcgi缓存的目的,是令nginx把运行的php资源结果缓存下来,当再需响应该资源时,无需交由php-fpm再次运行,直接把结果响应给用户,就像响应静态资源一样,提高响应效率。
常用相关指令:
指令 | 意义 |
fastcgi_cache_path | 定义一个缓存 |
fastcgi_cache | 调用指定缓存 |
fastcgi_cache_key | 定义缓存以什么数据为键 |
fastcgi_cache_valid | 定义对各响应码对应的资源,缓存多长时间 |
fastcgi_cache_methods | 指定对哪些请求方法的数据进行缓存。默认只对GET、HEAD方法请求的数据缓存 |
fastcgi_cache_min_uses | 定义资源在缓存时间内,至少要被命中多少次,否则视为非活动缓存(需清除)。默认为1 |
下面分别说明:
4.17.2.1 fastcgi_cache_path
要使用fastcgi缓存,需要先定义。要定义,需理解nginx的缓存机制。
缓存方式如图示10:
- 内存中的空间,缓存的不是数据内容,而是请求的URL被md5算法提取出的特征码(换算为16进制数)。同时,这个特征码也作为磁盘上存储数据的文件的名字;
- 不同URL的数据内容缓存在磁盘上的不同文件;
- 当缓存文件过多时,会影响查找速度。所以把各文件存放在不同层级的目录下;
- 层数为1至3,每层设置级别1或2。级别表示该层以多少位16进制数字作为目录名;
- 一个文件的位置就是根据其URL特征码的前几位16进制数来决定。从而达到分层查找的目的。
于是,命令fastcgi_cache_path使用格式就容易理解了:
fastcgi_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];
常用参数意义:
参数 | 意义 |
path | 定义缓存数据在磁盘上的哪个目录存放 |
max_size | 磁盘上最多使用多大空间存放缓存内容 |
levels=[12]:[12]:[12] | 缓存目录的层级定义,使用多少数字就表示定义几层(范围1至3);且每层用多少位16进制数命名该层的文件名(范围1至2) |
keys_zone=name:size | 定义缓存的名称和大小(这是针对那段内存空间的)。nginx中的缓存要定义名字,因为引用时就是按名字引用 |
inactive=time | 定义缓存内容的非活动时长 |
其中path和keys_zone是必须的。该命令的其他选项一般使用默认即可。
比如,定义如下缓存:
fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m
表示在磁盘上设置的缓存路径为/data/nginx/cache,缓存设置2个层级,级别分别为1、2。
则如果某资源URL的特征码划算为16进制数为”c29b7f54b2df7773722d382f4809d65029c”,则它在磁盘上对应的缓存数据文件路径为“/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c”。
这样,查找缓存数据时:
1、根据URL的特征码,找到缓存目录下的c目录(查找范围缩小至1/16,因为每个16进制数对应一个目录);
2、再查找c目录下的29目录(查找范围又缩小至1/256,因为2位16进制数有256种变化,对应256各目录);
3、最后找到余下的字符串对应的文件名,该文件就是缓存数据内容了。
这种机制使每个目录下文件数量大大减少,查找时按名缩小查找范围即可,无需遍历所有文件。
注意,定义缓存只能在http段。
4.17.2.2 fastcgi_cache_key
用于定义缓存的键。
最容易理解的,就是使用uri(nginx内置变量$request_uri)作为键了。键对应的值,就是uri对应的数据内容。
fastcgi_cache_key $request_uri # 一般也就是这么用的
使用范围:http段、server段、location段
fastcgi_cache_key虽然没有默认值,但要使用fastcgi缓存则必须使用此命令定义键,否则会报错。
4.17.2.3 fastcgi_cache_valid
定义对指定响应码的资源,缓存多长时间。
默认是没有值的,这个也必须定义,缓存才会生效,且不定义并不报错
使用范围:http段、server段、location段
比如:
fastcgi_cache_valid 200 302 10m; # 响应码是200或302的资源,缓存10分钟
fastcgi_cache_valid 404 1m; # 响应码是404的资源,缓存1分钟
fastcgi_cache_valid 5m # 仅定义时间,表示仅缓存200、301、302
fastcgi_cache_valid any 1m # any表示缓存所有响应码对应的资源
4.17.2.4 fastcgi_cache
要使用定义好的缓存,需使用该指令调用。
使用范围:http段、server段、location段
使用格式:
fastcgi_cache zone | off # zone为指定的缓存名(就是fastcgi\_cache\_path指令定义的);如关闭(或不继承更大范围所引用的缓存),使用off
4.17.2.4 压测效果
综前所述,使用缓存要先定义:
fastcgi\_cache\_path ……
再调用:
fastcgi_cache …… # 必须
fastcgi_cache_key …… # 必须
fastcgi_cache_valid …… # 必须
fastcgi\_cache\_method ……
fastcgi\_cache\_min\_uses ……
……
下面使用ab命令压测,查看fastcgi缓存效果:
1、环境说明:
192.168.0.106主机运行nginx;
192.168.0.61主机运行php-fpm。
2、在61主机,有php页面:
[root@node61 ~]% cat /var/www/php/test.php
<?php
phpinfo();
?>
启动php-fpm并配置允许106主机访问
3、在106主机,配置(只是把php请求交由后端,未配置缓存):
server {
listen 80;
server_name www.test.com;
root /var/www;
location ~ \.php {
root /var/www/php;
fastcgi_pass 192.168.0.61:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/php$fastcgi_script_name;
include fastcgi_params;
}
}
4、压测结果:
[root@node106 php]% ab -c 100 -n 1000 http://192.168.0.106/test.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.0.106 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: nginx/1.8.1
Server Hostname: 192.168.0.106
Server Port: 80
Document Path: /test.php
Document Length: 47568 bytes
Concurrency Level: 100
Time taken for tests: 0.991 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 47714000 bytes
HTML transferred: 47568000 bytes
Requests per second: 1009.44 [#/sec] (mean)
Time per request: 99.065 [ms] (mean)
Time per request: 0.991 [ms] (mean, across all concurrent requests)
Transfer rate: 47035.48 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.0 0 4
Processing: 7 94 15.4 95 147
Waiting: 2 88 12.6 91 111
Total: 7 95 14.6 95 149
5、在106主机,定义缓存并启用:
在http段:
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m;
在server段:
server {
listen 80;
server_name www.test.com;
root /var/www;
location ~ \.php {
root /var/www/php;
fastcgi_pass 192.168.0.61:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/php$fastcgi_script_name;
include fastcgi_params;
fastcgi_cache my_cache;
fastcgi_cache_key $request_uri;
fastcgi_cache_valid any 3m;
fastcgi_cache_methods GET;
fastcgi_cache_min_uses 1;
}
}
6、压测效果:
[root@node106 php]% ab -c 100 -n 1000 http://192.168.0.106/test.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 192.168.0.106 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: nginx/1.8.1
Server Hostname: 192.168.0.106
Server Port: 80
Document Path: /test.php
Document Length: 47568 bytes
Concurrency Level: 100
Time taken for tests: 0.126 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 47714000 bytes
HTML transferred: 47568000 bytes
Requests per second: 7954.25 [#/sec] (mean)
Time per request: 12.572 [ms] (mean)
Time per request: 0.126 [ms] (mean, across all concurrent requests)
Transfer rate: 370633.74 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 1.0 0 5
Processing: 2 11 2.5 12 15
Waiting: 0 11 2.6 11 14
Total: 7 12 1.6 12 15
Percentage of the requests served within a certain time (ms)
50% 12
66% 12
75% 12
80% 13
90% 13
95% 14
98% 14
99% 15
100% 15 (longest request)
每秒响应提高了很多倍,效果非常明显。
7、此时定义的缓存路劲下就有数据了:
[root@node106 php]% ls /var/cache/nginx/
b
[root@node106 php]% ls /var/cache/nginx/b/
7d
[root@node106 php]% ls /var/cache/nginx/b/7d/bf6813c2bc0becb369a8d8367b6b77db
/var/cache/nginx/b/7d/bf6813c2bc0becb369a8d8367b6b77db
可以看到,目录层级是按http段中定义进行的:
- 第1级目录的文件是1个字符命名的;
- 第2级目录的文件名是2个字符;
- 目录一共2级(因为仅定义了2级),出去作为目录名的剩下的字符串作为最底层的文件名。该文件在内存中的名字就是把它们连起来:b7dbf6813c2bc0becb369a8d8367b6b77db。
附带说下,httpd也是支持这种缓存功能的。
且笔者的另一博文xcache中可看到,xcache缓存也可提高php访问效率。不过这应该是不同的机制:xcache是令php的opcode缓存;这里是把动态资源结果缓存。
4.18 sendfile
这牵扯到使用异步IO。这里只是提一下,详细的需再写一篇,因为完成异步IO需要一系列指令定义各种属性(如aio、directio、sendfile_max_chunk等)、使用场景等。
这里主要是说下sendfile的意义,因为该指令默认值是off,但很多时候需要开启。
作用为:
用户访问web服务过程本为:web服务进程向内核发起调用获取磁盘上的某资源,内核获取后本应该是返还给web服务进程,由web服务进程响应用户(即把内容写入套接字文件),但sendfile表示内核获取到资源后直接把内容写入套接字,响应给用户,以提高效率。
使用范围:http段、server段、location段、location段中的if语句
(完)
- 通常为CPU核心数或核心数减一(因为还要有个核心运行操作系统)。这样如果使用命令worker_cpu_affinity配合,就能绑定一个核心仅运行一个worker进程,避免来回切换worker进程导致影响性能 ↩
- 注意,只是worker进程能在指定CPU上运行,而不是该CPU仅能运行worker进程,CPU还可以运行其他进程。
CPU掩码用来指定CPU,有多少CPU就用多少位二进制数字,比如一共4颗,用掩码表示就是0001、0010、0100、1000,通过不同的1所在位置,表示不同CPU。注意这里虽是从0001开始,但系统会把它视为0号CPU,因为CPU从0开始计数。 ↩ - 注意,它受限于上述的main段指令worker_rlimit_nofile,因为worker进程要针对每个连接打开一个套接字文件进行通信(这还不包括其他可能打开的文件数),所以worker_connections不能多于worker_rlimit_nofile定义的值。
此外,由此属性意义,易知一个nginx服务器,所能响应的最大并发连接数是:worker_processes乘以worker_connections ↩ - 虽然请求报文到达服务端,主机名已经是被解析为IP过了。但报文首部中的Host记录了用户在浏览器键入的主机名,所以可以根据不同主机名访问不同虚拟主机 ↩
- 如果只是在原URI上替换,则不管怎么替换,路径都还是在server段的root目录指定的路径下,而无法映射到服务端的其他文件路径。 ↩
- 也应该如此,否则所有错误页面返回都会导致和它们对应的响应码不一致就没有意义了。 ↩
- 由此也可看出,重定向后的URI被当作一个新请求,被nginx各配置重新处理。不过这都是在nginx服务端内部进行的,客户端不可见 ↩
- 最常用的也就是用内置变量、自定义变量作为判断条件,因为nginx有丰富的内置变量。大部分在http核心模块 ↩
- 这些变量名和对应的意义都是系统定义好的,直接使用即可 ↩
- nginx的缓存很多使用了这种机制。
比如指令client_body_temp_path,用于指定服务端在接收请求报文时,如果报文的body中的数据过大,将body数据暂存至指定文件。它的参数也是路径和级别 ↩