Jade Dungeon

Nginx 配置

配置文件

主配置文件nginx.conf一般位于/etc/nginx/中,基本结构为:

# 全局配置

events { ...  }

http {

	# HTTP服务配置

	# 各个模块配置
	# ...

	# 每个服务实例的配置
	server {
		# 
	}
	server { ...  }
	# ...

}

一般会把配置文件分开,主文件在/etc/nginx/nginx.conf,然后各个server的配置 放在/etc/nginx/site-allow/下面的各个文件中。

实际的例子:

user                  www www;             # 使用的启用和组
worker_processes      4;                   # 线程数量,一般为CPU数量,或是CPU数量的2倍
pid                   /var/run/nginx.pid;  # 存放 pid 的文件
worker_rlimit_nofile  512000;              # 文件描述符数量

events {
	use                  epoll;  # 网络IO模型,Linux为epoll,FreeBSD为kqueue
	worker_connections   768;    # 最大连接数
	# multi_accept       on;                                                            
}

http {                                                                          
	# Basic Settings                         
	include                /etc/nginx/mime.types;           
	default_type           application/octet-stream;   
	sendfile               on;
	keepalive_timeout      65;
	tcp_nopush             on; 
	tcp_nodelay            on;
	types_hash_max_size    2048;                
	
	# Gzip Settings                          
	gzip                   on;                                 
	gzip_vary              on;                          
	gzip_http_version      1.1;                 
	gzip_comp_level        6;                     
	gzip_min_length        1k
	gzip_buffers           16 8k;                    
	gzip_types             text/plain text/html text/xml text/css text/javascript application/xml application/xml+rss application/json application/javascript application/x-javascript;
	# gzip_disable         "msie6";                    
	# gzip_proxied         any;                      
	
	# http_proxy设置
	client_max_body_size        8m;        # 客户端要上传最大文件大小
	client_max_body_size        10m;
	client_body_buffer_size     128k;
	proxy_connect_timeout       75;
	proxy_send_timeout          75;
	proxy_read_timeout          75;
	proxy_buffer_size           4k;
	proxy_buffers               4 32k;
	proxy_busy_buffers_size     64k;
	proxy_temp_file_write_size  64k;
	proxy_temp_path             /usr/local/nginx/proxy_temp 1 2;	
	
	##                                       
	# Logging Settings                       
	##                                       
	access_log              /var/log/nginx/access.log;    
	error_log               /var/log/nginx/error.log;      
	
	##                                              
	# nginx-naxsi config                            
	##                                              
	# Uncomment it if you installed nginx-naxsi     
	##                                              
	#include /etc/nginx/naxsi_core.rules;           
	
	##                                              
	# nginx-passenger config                        
	##                                              
	# Uncomment it if you installed nginx-passenger 
	##                                              
	#passenger_root /usr;                           
	#passenger_ruby /usr/bin/ruby;                  
	
	##                                              
	# Virtual Host Configs                          
	##                                              
	include /etc/nginx/conf.d/*.conf;               
	include /etc/nginx/sites-enabled/*;             
	
	# 设定负载均衡后台服务器列表 
	upstream  backend  { 
		#ip_hash; 
		server   192.168.10.100:8080 max_fails=2 fail_timeout=30s ;  
		server   192.168.10.101:8080 max_fails=2 fail_timeout=30s ;  
	}

	# 很重要的虚拟主机配置
	server {
		listen          80;
		server_name     itoatest.example.com;
		root            /apps/oaapp;
		
		charset         utf-8;
		access_log      logs/host.access.log  main;
		
		# 对`/`所有做负载均衡+反向代理
		location / {
			root   /apps/oaapp;
			index  index.jsp index.html index.htm;
			
			proxy_pass           http://backend;  
			proxy_redirect       off;
			# 后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
			proxy_set_header     Host  $host;
			proxy_set_header     X-Real-IP  $remote_addr;  
			proxy_set_header     X-Forwarded-For  $proxy_add_x_forwarded_for;
			proxy_next_upstream  error timeout invalid_header http_500 http_502 http_503 http_504;
		}
	
		#静态文件,nginx自己处理,不去backend请求tomcat
		location  ~* /download/ {  
			root /apps/oa/fs;  
		}
		
		location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt|js|css)$   {   
			root         /apps/oaapp;   
			expires      7d; 
		}
		
		location /nginx_status {
			stub_status  on;
			access_log   off;
			allow        192.168.10.0/24;
			deny         all;
		}
		
		location ~ ^/(WEB-INF)/ {   
			deny       all;   
		}
		
		#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;
		}
	}
	
	# 其他虚拟主机的Server指令开始
}

# mail {
# 	# See sample authentication script at:      
# 	# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# 	                                            
# 	# auth_http localhost/auth.php;             
# 	# pop3_capabilities "TOP" "USER";           
# 	# imap_capabilities "IMAP4rev1" "UIDPLUS";  
# 	                                            
# 	server {                                    
# 		listen     localhost:110;                 
# 		protocol   pop3;                          
# 		proxy      on;                            
# 	}                                           
# 	                                            
# 	server {                                    
# 		listen     localhost:143;                 
# 		protocol   imap;                          
# 		proxy      on;                            
# 	}                                           
# }  

全局配置

user                   www www;             # 使用的启用和组
worker_processes       4;                   # 线程数量,一般为CPU数量,或是CPU数量的2倍
pid                    /var/run/nginx.pid;  # 存放 pid 的文件
worker_rlimit_nofile   512000;              # 文件描述符数量

events { ... }

http {
	server { ...  }
	server { ...  }
}

nginx在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数, 比如工作进程数,运行的身份等。

进程数量

woker_processes 2:在配置文件的顶级main部分,worker角色的工作进程的个数, master进程是接收并分配请求给worker处理。这个数值简单一点可以设置为cpu的核数 cat /proc/cpuinfo | grep ^processor,也是 auto 值, 如果开启了ssl和gzip更应该设置成与逻辑CPU数量一样甚至为2倍,可以减少I/O操作。 如果nginx服务器还有其它服务,可以考虑适当减少。

CPU粘性

worker_cpu_affinity:也是写在main部分。在高并发情况下, 通过设置cpu粘性来降低由于多CPU核切换造成的寄存器等现场重建带来的性能损耗。 如worker_cpu_affinity 0001 0010 0100 1000; (四核)。

events配置

# ...

events {
	use                  epoll;  # 网络IO模型,Linux为epoll,FreeBSD为kqueue
	worker_connections   768;    # 最大连接数
	# multi_accept       on;                                                            
}

http {
	server { ...  }
	server { ...  }
}

事件模型

use epoll:写在events部分。在Linux操作系统下,nginx默认使用epoll事件模型, 得益于此,nginx在Linux操作系统下效率相当高。同时Nginx在OpenBSD或FreeBSD 操作系统上采用类似于epoll的高效事件模型kqueue。 在操作系统不支持这些高效模型时才使用select

连接限制

worker_connections 2048:写在events部分。每一个worker进程能并发处理 (发起)的最大连接数(包含与客户端或后端被代理服务器间等所有连接数)。 nginx作为反向代理服务器,计算公式: 最大连接数 = worker_processes * worker_connections/4, 所以这里客户端最大连接数是1024,这个可以增到到8192都没关系,看情况而定, 但不能超过后面的worker_rlimit_nofile。当nginx作为http服务器时, 计算公式里面是除以2。

文件句柄限制

worker_rlimit_nofile 10240:写在main部分。默认是没有设置, 可以限制为操作系统最大的限制65535。

各个模块的配置

基础HTTP模块

# ....

events { ...  }

http {

	# Basic Settings                         
	include                   /etc/nginx/mime.types;           
	default_type              application/octet-stream;   
	sendfile                  on;
	keepalive_timeout         65;
	tcp_nopush                on; 
	tcp_nodelay               on;
	types_hash_max_size       2048;                
	client_max_body_size      8m;        # 客户端要上传最大文件大小
	client_max_body_size      10m;
	client_body_buffer_size   128k;
	
	server { ...  }
	server { ...  }
}

文件传输模式

sendfile on: 开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件, 减少用户空间到内核空间的上下文切换。对于普通应用设为on, 如果用来进行下载等应用磁盘IO重负载应用,可设置为off, 以平衡磁盘与网络I/O处理速度,降低系统的负载。

连接超时

keepalive_timeout 65:长连接超时时间,单位是秒,这个参数很敏感, 涉及浏览器的种类、后端服务器的超时设置、操作系统的设置,可以另外起一片文章了。 长连接请求大量小文件的时候,可以减少重建连接的开销,但假如有大文件上传, 65s内没上传完成会导致失败。如果设置时间过长,用户又多, 长时间保持连接会占用大量资源。

响应超时

send_timeout:用于指定响应客户端的超时时间。 这个超时仅限于两个连接活动之间的时间,如果超过这个时间,客户端没有任何活动, Nginx将会关闭连接。

请求大小限制

client_max_body_size 10m:允许客户端请求的最大单文件字节数。 如果有上传较大文件,请设置它的限制值

请求缓冲区大小

client_body_buffer_size 128k:缓冲区代理缓冲用户端请求的最大字节数

日志模块

# ...
events { ...  }

http {

	##                                       
	# Logging Settings                       
	##                                       
	access_log              /var/log/nginx/access.log;    
	error_log               /var/log/nginx/error.log;      

	server { ...  }
	server { ...  }
}

加载具体服务的配置

一般会把配置文件分开,主文件在/etc/nginx/nginx.conf, 然后各个server的配置 放在/etc/nginx/site-allow/下面的各个文件中。

# ...
events { ...  }

http {

	##                                              
	# Virtual Host Configs                          
	##                                              
	include /etc/nginx/conf.d/*.conf;               
	include /etc/nginx/sites-enabled/*;             

	server { ...  }
	server { ...  }
}

压缩模块:http_gzip

  • gzip on:开启gzip压缩输出,减少网络传输。
  • gzip_min_length 1k:设置允许压缩的页面最小字节数, 页面字节数从header头得content-length中进行获取。默认值是20。 建议设置成大于1k的字节数,小于1k可能会越压越大。
  • gzip_buffers 4 16k:设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。 4 16k代表以16k为单位,安装原始数据大小以16k为单位的4倍申请内存。
  • gzip_http_version 1.0:用于识别 http 协议的版本,早期的浏览器不支持 Gzip 压缩, 用户就会看到乱码,所以为了支持前期版本加上了这个选项, 如果你用了 Nginx 的反向代理并期望也启用 Gzip 压缩的话,由于末端通信是http/1.0, 故请设置为 1.0。
  • gzip_comp_level 6:gzip压缩比,1压缩比最小处理速度最快, 9压缩比最大但处理速度最慢(传输快但比较消耗cpu)
  • gzip_types:匹配mime类型进行压缩,无论是否指定,text/html类型总是会被压缩的。
  • gzip_proxied any:Nginx作为反向代理的时候启用, 决定开启或者关闭后端服务器返回的结果是否压缩, 匹配的前提是后端服务器必须要返回包含Via的 header头。
  • gzip_vary on:和http头有关系,会在响应头加个Vary: Accept-Encoding, 可以让前端的缓存服务器缓存经过gzip压缩的页面,例如,用Squid缓存经过Nginx压缩的数据。
# ...
events { ...  }

http {

	# Gzip Settings                          
	gzip                   on;                                 
	gzip_vary              on;                          
	gzip_http_version      1.1;                 
	gzip_comp_level        6;                     
	gzip_min_length        1k
	gzip_buffers           16 8k;                    
	gzip_types             text/plain text/html text/xml text/css text/javascript application/xml application/xml+rss application/json application/javascript application/x-javascript;
	# gzip_disable         "msie6";                    
	# gzip_proxied         any;                      

	server { ...  }
	server { ...  }
}

负载均衡模块

这个模块通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡, upstream后接负载均衡器的名字,后端realserverhost:port options; 的方式组织在{}中。如果后端被代理的只有一台,也可以直接写在proxy_pass

# ...
events { ...  }

http {

	# 设定负载均衡后台服务器列表 
	upstream  backend  { 
		#ip_hash; 
		server   192.168.10.100:8080 max_fails=2 fail_timeout=30s ;  
		server   192.168.10.101:8080 max_fails=2 fail_timeout=30s ;  
	}

	server { ...  }
	server { ...  }
}

代理模块:http_proxy

# ...
events { ...  }

http {

	# http_proxy设置
	proxy_connect_timeout       75;
	proxy_send_timeout          75;
	proxy_read_timeout          75;
	proxy_buffer_size           4k;
	proxy_buffers               4 32k;
	proxy_busy_buffers_size     64k;
	proxy_temp_file_write_size  64k;
	proxy_temp_path             /usr/local/nginx/proxy_temp 1 2;	

	server { ...  }
	server { ...  }
}

这个模块实现的是nginx作为反向代理服务器的功能,包括缓存功能(另见文章)

  • proxy_connect_timeout 60: nginx跟后端服务器连接超时时间(代理连接超时)
  • proxy_read_timeout 60: 连接成功后,与后端服务器两个成功的响应操作之间超时时间(代理接收超时)
  • proxy_buffer_size 4k: 设置代理服务器(nginx)从后端realserver读取并保存用户头信息的缓冲区大小, 默认与proxy_buffers大小相同,其实可以将这个指令值设的小一点
  • proxy_buffers 4 32k: proxy_buffers缓冲区, nginx针对单个连接缓存来自后端realserver的响应,网页平均在32k以下的话,这样设置
  • proxy_busy_buffers_size 64k: 高负荷下缓冲大小(proxy_buffers*2)
  • proxy_max_temp_file_size: 当proxy_buffers放不下后端服务器的响应内容时, 会将一部分保存到硬盘的临时文件中,这个值用来设置最大临时文件大小,默认1024M, 它与proxy_cache没有关系。大于这个值,将从upstream服务器传回。设置为0禁用。
  • proxy_temp_file_write_size 64k: 当缓存被代理的服务器响应到临时文件时, 这个选项限制每次写临时文件的大小。proxy_temp_path(可以在编译的时候)指定写到哪那个目录。

例子:

    server {

        listen       80;
        server_name  localhost;

        client_max_body_size   12m;
        client_body_buffer_size 12m;

        location / {
                proxy_set_header Host htl-oct-ocr-task.chinatravel.net;
                proxy_set_header X-Real-Ip $remote_addr;
                proxy_set_header X-Forwarded-For $remote_addr;
             proxy_pass http://htl-oct-ocr-task.chinatravel.net/;
        }

       # 404时显示本地的页面
       proxy_intercept_errors on;
       error_page 404 /404.html;
       location = /404.html {
          root /usr/local/nginx/html;
       }

        location /study/notes/wiki_html/ {
             proxy_pass http://www.jade-dungeon.net;
        }
   }

proxy_passproxy_redirect见 location 部分。

各个应用实例的配置server

一般会把配置文件分开,主文件在/etc/nginx/nginx.conf, 然后各个server的配置 放在/etc/nginx/site-allow/下面的各个文件中。

  • listen: 监听端口,默认80,小于1024的要以root启动。可以为listen *:80listen 127.0.0.1:80等形式。
  • server_name: 服务器名,如localhostwww.example.com,可以通过正则匹配。
server {
        listen          80;
        server_name     itoatest.example.com;
        root            /apps/oaapp;
        charset         utf-8;
        access_log      logs/host.access.log  main;
}

对特定路径的配置location

http服务中,某些特定的URL location(即特定的路径模式)对应的一系列配置项。

location 特定的路径模式 {
	配置的内容
}
  • root /var/www/html:定义服务器的默认网站根目录位置。 如果locationURL匹配的是子目录或文件,root没什么作用,一般放在server指令里面或/下。
  • index index.jsp index.html index.htm:定义路径下默认访问的文件名,一般跟着root放
  • proxy_pass http:/backend:请求转向backend定义的服务器列表,即反向代理,对应upstream负载均衡器。也可以proxy_pass http://ip:port
  • proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;: 这四个暂且这样设,如果深究的话,每一个都涉及到很复杂的内容,也将通过另一篇文章来解读。

关于location匹配规则的写法,可以说尤为关键且基础的,在「Location与Rewrite规则」中再讨论。

路径匹配

一般静态文件路径都是与系统的路径匹配的。比如对于系统的路径与文件 /blog/index.html可以通过以下URI形式定位:

  • http://myhost/blog/
  • http://myhost/blog/index.html

但是对于有些前端框架如React或Vue,URI并不对应文件系统上具体的文件。 比如http://myhost/blog/newPost只是一个前端架构的路由。 并不代表文件系统上有一个文件/blog/newPost。 这种情况下,当页面地址为http://myhost/blog/newPost时手动刷新页面, nginx会报404。所以要用try_files参数指定路径匹配方法。

如果是在根目录则配置如下:

server {
    listen 80;
    root /usr/share/nginx/html;
    location / {
        try_files $uri $uri/ /index.html;
    }
}

如果是在特定的目录则配置如下:

server {
    listen 80;
    root /usr/share/nginx/html;
    location /xxx/xxx {
        try_files $uri $uri/ /xxx/xxx/index.html;
    }
}

访问控制 allow/deny

Nginx 的访问控制模块默认就会安装,而且写法也非常简单,可以分别有多个allow, deny,允许或禁止某个ip或ip段访问,依次满足任何一个规则就停止往下匹配。如:

location /nginx-status {
	stub_status on;
	access_log off;
	# auth_basic   "NginxStatus";
	#	auth_basic_user_file   /usr/local/nginx-1.6/htpasswd;

	allow 192.168.10.100;
	allow 172.29.73.0/24;
	deny all;
}

我们也常用httpd-devel工具的htpasswd来为访问的路径设置登录密码:

$ htpasswd -c htpasswd admin
New passwd:
Re-type new password:
Adding password for user admin

$ htpasswd htpasswd admin   # 修改admin密码
$ htpasswd htpasswd sean    # 多添加一个认证用户

这样就生成了默认使用CRYPT加密的密码文件。打开上面nginx-status的两行注释,重启nginx生效。

是否列出目录下的内容autoindex

Nginx默认是不允许列出整个目录的。如需此功能,打开nginx.conf文件, 在location,server 或 http段中加入autoindex on;,另外两个参数最好也加上去:

  • autoindex_exact_size off;: 默认为on,显示出文件的确切大小,单位是bytes。 改为off后,显示出文件的大概大小,单位是kB或者MB或者GB
  • autoindex_localtime on;: 默认为off,显示的文件时间为GMT时间。改为on后, 显示的文件时间为文件的服务器时间
location /images {
  root                   /var/www/nginx-default/images;
  autoindex              on;
	autoindex_localtime    on;   # 是否以本地时区显示时间(默认为GMT时间)
	autoindex_exact_size   on;   # 是否以B、KB、MB、GB等单位显示文件大小
}

本地缓存设置

httpserverlocation块中可以用expires标记缓存时间:

expires [time|epoch|max|off]
  • off:默认为关闭。
  • time:设定正数与负数,Expires头标通过当前系统时间。
  • epoch:指定Expires值为1 January, 1970, 00:00:01 GMT
  • max:指定Expires值为31 December, 2037, 23:59:59 GMT

例,图片,FLASH等本地缓存30天,js、css等本地缓存1小时:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; }
location ~ .*\.(js|css)$ { expires 1h; }

日志配置

在全局的http{...}与每个应用独立的server{...}块中都可以定义日志配置。

log_format设置日志格式;access_log设置存放的位置、格式与缓存大小。

日志格式

log_format设定日志的格式:

log_format <name> <format> [ <format> ... ]
  • name:格式的名称。不能有重复。
  • format:定义的格式样式。
log_format combined '$remote_addr - $remote_user [$time_local] '
	'"$request" $status $body_bytes_sent '
	'"$http_referer" "$http_user_agent"';

注意:在反射代理后面的话,就拿不到$remote_addr了,需要反射代理服务器添加 X-Forwarded-For到HTTP头部。并修改日志格式为:

log_format mylogformat '$http_x_forwarded_for - $remote_user [$time_local] '
	'"$request" $status $body_bytes_sent '
	'"$http_referer" "$http_user_agent"';
  • http_user_agent:浏览器信息。
  • remote_user:远程客户端用户名称。
  • remote_addr:远程地址。
  • http_x_forwarded_for:转发来源。
  • time_local:访问时间与时区。
  • request:访问的URL与HTTP协议。
  • http_refer:访问来源。
  • status:请求状态。
  • body_tytes_sent:响应的大小。

日志路径

access_log配置日志路径:

access_log <path> [format [buffer=size | off]]
  • path:存放日志的路径。
  • formatlog_format定义的日志格式。
  • buffer=size:缓存的大小。

例子,关闭日志:

access_log off;

例子,指定文件,以默诵的combined格式:

access_log /data1/logs/filename.log;

例子,指定格式名:

access_log /data1/logs/filename.log combined;

例子,定义格式:

log_format mylogformat '$http_x_forwarded_for - $remote_user [$time_local] '
	'"$request" $status $body_bytes_sent '
	'"$http_referer" "$http_user_agent"';

access_log /data1/logs/filename.log mylogformat buffer=32k;

Nginx 0.7.4以后,可以在路径中包含变量:

access_log /data1/logs/$server_name.log combined

如果server_name指令设置的虚拟主机名为test.domain.com,变量会代入路径。

注意:

  • 最终路径的权限。
  • 这样不能使用缓存。
  • 每一条记录都要重复打开和关闭文件,所以必须用open_log_cache缓存文件描述符。

open_log_file_cache缓存文件描述符:

open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time] | off
  • off:禁用缓存。
  • max:最大文件描述符数量。
  • inactive:删除超时不用的文件描述符的时间,默认10秒。
  • min_users:日志在超时之前被使用大于一定的次数,就缓存文件。默认是1次。
  • valid:检查文件是否还存在的时间间隔。默认60秒。

默认缓存是禁止的,相当于:

open_log_file_cache off;

另一个例子:

open_log_file_cache max=1000 inactive=20s min_users=2 valid=1m;

日志切割

这个要手动切,用crontab定时跑:

mv /data1/log/access.log /data1/log/access.20090318.log

用信号USR1让Nginx生成新日志,如果配置了pid存放的文件,从哪里拿进程号:

kill -USR1 `cat /usr/local/webserver/nginx/nginx.pid`

一个脚本的例子:

#!/bin/bash

# 此脚本在每天 00:00 运行
# 00 00 * * * /bin/bash /opt/tools/cut_nginx_log.sh

logs_path="/data1/logs/"
pid_file="/data1/logs/nginx.pid"
cut_log_time="$(date +%y-%m-%d_%H:%M:%S)"

mv ${logs_path}access.log ${logs_path}/access.${cut_log_time}.log
mv ${logs_path}error.log ${logs_path}/error.${cut_log_time}.log
kill -USR1 `cat ${pid_file}`

注意事项:

  1. 在没有执行kill -USR1 nginx_pid之前,即便已经对文件执行了mv命令也只是改变了文件的名称, nginx还是会向新命名的文件中照常写入日志数据。原因在于linux系统中,内核是根据文件描述符来找文件的。
  2. 添加定时任务后如果定时任务没有起作用,则使用ps -aux | grep cron确认下服务是否已经开启
  3. -USR1是进程间通信的信号中的一种,至于nginx进程接收到该信号做什么操作是它内部定义的, 除此之外我们可以使用kill -l确认liunx中包含哪些信号

切割以后定时检查删除体积过大的日志文件:

#!/bin/bash

logs_path="/data1/logs/"
pid_file="/data1/logs/nginx.pid"

find${logs_path}/*.log -size +1024M | xargs rm
find${logs_path}/*.log -time +7     | xargs rm
kill -USR1 `cat ${pid_file}`

虚拟主机

在同一台服务器、同一组Nginx进程作为多台虚拟的主机,最简配置:

http
{
	server
	{
		listen       80 default;
		server_name  _ *;
		access_log   logs/default.access.log combined;
		location /
		{
			index    index.html;
			root     /data0/htdocs/htdocs;
		}
	}
}

与Apache一样虚拟主机可以通过三种方式实现:基于IP、基于子域名、基于端口。

基于IP的虚拟主机

绑定两个IP地址到eth0网卡:

ifconfig eth0:1 192.168.8.43 broadcast 192.168.8.255 netmask 255.255.255.0 up
route add -host 192.168.8.43 dev eth0:1

ifconfig eth0:1 192.168.8.44 broadcast 192.168.8.255 netmask 255.255.255.0 up
route add -host 192.168.8.44 dev eth0:1

现在根据在个IP地址192.168.8.43-45三个地址搞三个虚拟主机:

http {
	server {
		listen       192.168.8.43:80
		server_name  192.168.8.43;
		assess_log   logs/server1.assess.log combined;  # log file

		location / {
			index index.html index.htm;    # 默认主页
			root /data0/htdocs/server1;    # 网站的根目录
		}
	}

	server {
		listen       192.168.8.44:80
		server_name  192.168.8.44;
		assess_log   logs/server2.assess.log combined;  # log file

		location / {
			index index.html index.htm;    # 默认主页
			root /data0/htdocs/server2;    # 网站的根目录
		}
	}

	server {
		listen       192.168.8.45:80
		server_name  192.168.8.45;
		assess_log   logs/server3.assess.log combined;  # log file

		location / {
			index index.html index.htm;    # 默认主页
			root /data0/htdocs/server3;    # 网站的根目录
		}
	}
}

基于子域名的虚拟主机

通过dns域名来节约IP地址。现在同一个地址被在个域名映射:

aaa.domain.combbb.domain.com为两个不同的目录,

www.domain.com*.domain.com或是domain.com都被映射到第三个目录。

http {
	server {
		listen 80;
		server_name aaa.domain.com;
		access_log  /var/log/nginx/aaa.domain.com.assess.log combined;
		
		location / {
			index index.html index.htm;
			root  /data0/htdocs/aaa.domain.com;
		}
	}
	
	server {
		listen 80;
		server_name bbb.domain.com;
		access_log  /var/log/nginx/bbb.domain.com.assess.log combined;

		location / {
			index index.html index.htm;
			root  /data0/htdocs/bbb.domain.com;
		}
	}
	
	server {
		listen 80;
		server_name www.domain.com domain.com *.domain.com;
		access_log  /var/log/nginx/other.domain.com.assess.log combined;
		
		location / {
			index index.html index.htm;
			root  /data0/htdocs/other.domain.com;
		}
	}
}

基于端口的虚拟主机

nginx对外提供8182两个端口监听服务。

  • 请求http://192.168.78.132:81端口则请求html81目录
  • 请求http://192.168.78.132:82端口则请求html82目录
http {
	server {
			listen       81;
			server_name  192.168.101.3;
			
			location / {
					root   /usr/local/nginx/html81;
					index  index.html index.htm;
			}
	}

	server {
			listen       82;
			server_name  192.168.101.3;

			location / {
					root   /usr/local/nginx/html82;
					index  index.html index.htm;
			}
	}
}

location和rewrite规则

location正则写法

基本语法

location  = / {
  # 精确匹配 / ,主机名后面不能带任何字符串
  [ configuration A ] 
}

location  / {
  # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
  # 但是正则和最长字符串会优先匹配
  [ configuration B ] 
}

location /documents/ {
  # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration C ] 
}

location ~ /documents/Abc {
  # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
  # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
  [ configuration CC ] 
}

location ^~ /../images/ {
  # 匹配任何以 /../images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
  [ configuration D ] 
}

location ~* \.(gif|jpg|jpeg)$ {
  # 匹配所有以 gif,jpg或jpeg 结尾的请求
  # 然而,所有请求 /../images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
  [ configuration E ] 
}

location /../images/ {
  # 字符匹配到 /../images/,继续往下,会发现 ^~ 存在
  [ configuration F ] 
}

location /../images/abc {
  # 最长字符匹配到 /../images/abc,继续往下,会发现 ^~ 存在
  # F与G的放置顺序是没有关系的
  [ configuration G ] 
}

location ~ /../images/abc/ {
  # 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,
	# 匹配到这一条正则,采用
  [ configuration H ] 
}

location ~* /js/.*/\.js
  • =开头表示精确匹配
  • A中只匹配根目录结尾的请求,后面不能带任何字符串。
  • ^~开头表示uri以某个常规字符串开头,不是正则匹配
  • ~开头表示区分大小写的正则匹配;
  • ~*开头表示不区分大小写的正则匹配
  • /通用匹配, 如果没有其它匹配,任何请求都会匹配到

顺序优先级

(location =) > (location 完整路径) > (location ^~ 路径) > 
	(location ~,~* 正则顺序) > (location 部分起始路径) > (/)

按照上面的location写法,以下的匹配示例成立:

  • / -> config A: 精确完全匹配,即使/index.html也匹配不了
  • /downloads/download.html -> config B: 匹配B以后,往下没有任何匹配,采用B
  • /../images/1.gif -> configuration D: 匹配到F,往下匹配到D,停止往下
  • /../images/abc/def -> config D: 最长匹配到G,往下匹配D,停止往下 你可以看到 任何以/../images/开头的都会匹配到D并停止,FG写在这里是没有任何意义的, H是永远轮不到的,这里只是为了说明匹配顺序
  • /documents/document.html -> config C: 匹配到C,往下没有任何匹配,采用C
  • /documents/1.jpg -> configuration E: 匹配到C,往下正则匹配到E
  • /documents/Abc.jpg -> config CC:最长匹配到C,往下正则顺序匹配到CC,不会往下到E

实际使用建议

所以实际使用中,个人觉得至少有三个匹配规则定义,如下:

第一个必选规则: 直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。 这里是直接转发给后端应用服务器了,也可以是一个静态首页:

location = / {
    proxy_pass http://tomcat:8080/index
}

第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项。 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用:

location ^~ /static/ {
    root /webroot/static/;
}

location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}

第三个规则就是通用规则,用来转发动态请求到后端应用服务器。 非静态文件请求就默认是动态请求,自己根据实际把握。 毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了:

location / {
    proxy_pass http://tomcat:8080/
}

Rewrite规则

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量, 结合正则表达式和标志位实现url重写以及重定向。rewrite只能放在server{}, location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用, 例如:http://seanlook.com/a/we/index.php?id=1&u=str只对/a/we/index.php重写。 语法:

rewrite regex replacement [flag];

如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。

表明看rewrite和location功能有点像,都能实现跳转, 主要区别在于rewrite是在同一域名内更改获取资源的路径, 而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。 很多情况下rewrite也会写在location里,它们的执行顺序是:

  1. 执行server块的rewrite指令
  2. 执行location匹配
  3. 执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件; 循环超过10次,则返回500 Internal Server Error错误。

flag标志位

  • last : 相当于Apache的[L]标记,表示完成rewrite
  • break : 停止执行当前虚拟主机的后续rewrite指令集
  • redirect : 返回302临时重定向,地址栏会显示跳转后的地址
  • permanent : 返回301永久重定向,地址栏会显示跳转后的地址

因为301和302不能简单的只返回状态码,还必须有重定向的URL, 这就是return指令无法返回301,302的原因了。这里 last 和 break 区别有点难以理解:

  • last一般写在server和if中,而break一般使用在location中
  • last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
  • break和last都能组织继续执行后面的rewrite指令

if指令与全局变量

if判断指令

语法为:

if (condition) {...}

对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(conditon)可以是如下任何内容:

  • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
  • 直接比较变量和内容时,使用=!=
  • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配
  • -f!-f用来判断是否存在文件
  • -d!-d用来判断是否存在目录
  • -e!-e用来判断是否存在文件或目录
  • -x!-x用来判断文件是否可执行

例如:

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
} //如果UA包含"MSIE",rewrite请求到/msid/目录下

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
 } //如果cookie匹配正则,设置变量$id等于正则引用部分

if ($request_method = POST) {
    return 405;
} //如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302

if ($slow) {
    limit_rate 10k;
} //限速,$slow可以通过 set 指令设置

if (!-f $request_filename){
    break;
    proxy_pass  http://127.0.0.1; 
} //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查

if ($args ~ post=140){
    rewrite ^ http://example.com/ permanent;
} //如果query string中包含"post=140",永久重定向到example.com

location ~* \.(gif|jpg|png|swf|flv)$ {
    valid_referers none blocked www.jefflei.com www.leizhenfang.com;
    if ($invalid_referer) {
        return 404;
    } //防盗链
}
全局变量

下面是可以用作if判断的全局变量

  • \(args: #这个变量等于请求行中的参数,同\)query_string
  • $content_length: 请求头中的Content-length字段。
  • $content_type: 请求头中的Content-Type字段。
  • $document_root: 当前请求在root指令中指定的值。
  • $host: 请求主机头字段,否则为服务器名称。
  • $http_user_agent: 客户端agent信息
  • $http_cookie: 客户端cookie信息
  • $limit_rate: 这个变量可以限制连接速率。
  • $request_method: 客户端请求的动作,通常为GET或POST。
  • $remote_addr: 客户端的IP地址。
  • $remote_port: 客户端的端口。
  • $remote_user: 已经经过Auth Basic Module验证的用户名。
  • $request_filename: 当前请求的文件路径,由root或alias指令与URI请求生成。
  • $scheme: HTTP方法(如http,https)。
  • $server_protocol: 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr: 服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name: 服务器名称。
  • $server_port: 请求到达服务器的端口号。
  • $request_uri: 包含请求参数的原始URI,不包含主机名,如:/foo/bar.php?arg=baz
  • $uri: 不带请求参数的当前URI,不包含主机名,如/foo/bar.html
  • $document_uri: 与$uri相同。

例:http://localhost:88/test1/test2/test.php

$host:            localhost
$server_port:     88
$request_uri:     http://localhost:88/test1/test2/test.php
$document_uri:    /test1/test2/test.php
$document_root:   /var/www/html
$request_filename:/var/www/html/test1/test2/test.php
常用正则
  .      匹配除换行符以外的任意字符
  ?      重复0次或1次
  +      重复1次或更多次
  *      重复0次或更多次
  \d     匹配数字
  ^      匹配字符串的开始
  $      匹配字符串的介绍
  {n}    重复n次
  {n,}   重复n次或更多次
  [c]    匹配单个字符c
  [a-z]  匹配a-z小写字母的任意一个

小括号()之间匹配的内容,可以在后面通过\(1来引用,\)2表示的是前面第二个()里的内容。

正则里面容易让人困惑的是\转义特殊字符。

一个rewrite的例子

http {
    # 定义image日志格式
    log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;
    # 开启重写日志
    rewrite_log on;

    server {
        root /home/www;

        location / {
                # 重写规则信息
                error_log logs/rewrite.log notice; 
                # 注意这里要用‘’单引号引起来,避免{}
                rewrite '^/../images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;
                # 注意不能在上面这条规则后面加上「last」参数,否则下面的set指令不会执行
                set $image_file $3;
                set $image_type $4;
        }

        location /data {
                # 指定针对图片的日志格式,来分析图片类型和大小
                access_log logs/images.log mian;
                root /data/images;
                # 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
                try_files /$arg_file /image404.html;
        }
        location = /image404.html {
                # 图片不存在返回特定的信息
                return 404 "image not found\n";
        }
}

对形如/../images/ef/uh7b3/test.png的请求,重写到/data?file=test.png, 于是匹配到location /data,先看/data/../images/test.png文件存不存在, 如果存在则正常响应,如果不存在则重写tryfiles到新的image404 location, 直接返回404状态码。

另一个例子:

rewrite ^/../images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

对形如/../images/bla_500x400.jpg的文件请求, 重写到/resizer/bla.jpg?width=500&height=400地址,并会继续尝试匹配location。