Nginx是俄羅斯人編寫(xiě)的一款高性能的HTTP和反向代理服務(wù)器,在高連接并發(fā)的情況下,它能夠支持高達(dá)50000個(gè)并發(fā)連接數(shù)的響應(yīng),但是內(nèi)存、CPU等系統(tǒng)資源消耗卻很低,運(yùn)行很穩(wěn)定。
Nginx的優(yōu)勢(shì)
為什么選擇Nginx?因?yàn)樗哂幸韵绿攸c(diǎn):
- 速度快,先天的事件驅(qū)動(dòng)型設(shè)計(jì)、全異步的網(wǎng)絡(luò)I/O處理機(jī)制、極少的進(jìn)程間切換。
- 擴(kuò)展性強(qiáng),它支持很多第三方模塊。
- 可靠性好,Nginx是基于多進(jìn)程設(shè)計(jì),每個(gè)worker進(jìn)程相對(duì)獨(dú)立,master進(jìn)程在1個(gè)worker進(jìn)程出錯(cuò)時(shí)可以快速“拉起”新的worker子進(jìn)程提供服務(wù)。
- 低內(nèi)存消耗,10000個(gè)非活躍的HTTP Keep-Alive連接在Nginx中僅消耗2.5MB的內(nèi)存。
- 高并發(fā),單機(jī)支持10萬(wàn)以上的并發(fā)連接。
- 熱部署,master管理進(jìn)程與worker工作進(jìn)程的分離設(shè)計(jì),使得Nginx能夠提供熱部署功能。
- 最自由的BSD許可協(xié)議,BSD許可協(xié)議不只是允許用戶免費(fèi)使用Nginx,它還允許用戶在自己的項(xiàng)目中直接使用或修改Nginx源碼,然后發(fā)布。
Nginx先天的事件驅(qū)動(dòng)型設(shè)計(jì)、全異步的網(wǎng)絡(luò)I/O處理機(jī)制、極少的進(jìn)程間切換以及許多優(yōu)化設(shè)計(jì),都使得Nginx天生善于處理高并發(fā)壓力下的互聯(lián)網(wǎng)請(qǐng)求,同時(shí)Nginx降低了資源消耗,可以把服務(wù)器硬件資源“壓榨”到極致。
Nginx的作用
Nginx一般處于應(yīng)用的網(wǎng)關(guān)位置,常用來(lái)做靜態(tài)資源服務(wù)器、負(fù)載均衡和反向代理(路由功能)。
- 靜態(tài)資源服務(wù)器:在mvvm模式中,充當(dāng)文件讀取職責(zé),一般用來(lái)做動(dòng)靜分離。
- 負(fù)載均衡:對(duì)后臺(tái)服務(wù)器集群進(jìn)行負(fù)載。
- 反向代理:域名/路徑,進(jìn)行路由選擇后臺(tái)服務(wù)器。
Nginx基礎(chǔ)概念
nginx是基于多進(jìn)程設(shè)計(jì),通過(guò)ps -ef|grep nginx我們可以查看nginx的進(jìn)程:
[root@localhost sbin]# ps -ef|grep nginx
root 7400 1 0 16:53 ? 00:00:00 nginx: master process ./nginx
nobody 9841 7400 0 17:29 ? 00:00:00 nginx: worker process
nobody 9842 7400 0 17:29 ? 00:00:00 nginx: worker process
通過(guò)命令我們可以發(fā)現(xiàn),nginx對(duì)應(yīng)了多個(gè)進(jìn)程,主進(jìn)程master和工作進(jìn)程worker一般情況下,worker進(jìn)程的數(shù)量與服務(wù)器上的CPU核心數(shù)相等,如果配置了緩存還會(huì)有緩存加載器進(jìn)程(cache loader)和緩存管理器進(jìn)程(cache manager)等。但是所用進(jìn)程都只包含一個(gè)線程,并且進(jìn)程之間主要通過(guò)共享內(nèi)存的方式來(lái)進(jìn)行通信。主進(jìn)程以root用戶權(quán)運(yùn)行,其他進(jìn)程是以nginx.config配置文件配置的用戶運(yùn)行,模式是nobody。
主進(jìn)程主要完成如下工作:
- 讀取并驗(yàn)正配置信息;
- 創(chuàng)建、綁定及關(guān)閉套接字;
- 啟動(dòng)、終止及維護(hù)worker進(jìn)程的個(gè)數(shù),管理worker進(jìn)程;
- 無(wú)須中止服務(wù)而重新配置工作特性;
- 重新打開(kāi)日志文件;
worker進(jìn)程主要完成的任務(wù)包括:
- 接收、傳入并處理來(lái)自客戶端的連接;
- 提供反向代理及過(guò)濾功能;
- nginx任何能完成的其它任務(wù);
nginx采用多進(jìn)程的方式主要是因?yàn)橐WCNginx的高可用和高可靠性。如果Nginx 使用了多線程,當(dāng)某一個(gè)第三方模塊引發(fā)了一個(gè)錯(cuò)誤,如:地址越界時(shí),就會(huì)導(dǎo)致整個(gè)Nginx全部掛掉; 而采用多進(jìn)程來(lái)實(shí)現(xiàn)時(shí),卻能很好的規(guī)避這個(gè)問(wèn)題,類(lèi)似于微服務(wù)的思想。
配置文件結(jié)構(gòu)
nginx.conf配置文件結(jié)構(gòu)如圖:

| 類(lèi)型 | 說(shuō)明 |
|---|---|
| main | 全局設(shè)置 |
| events | 設(shè)定nginx的工作模式及連接數(shù)上限 |
| http | 服務(wù)器相關(guān)屬性 |
| server | 虛擬主機(jī)設(shè)置 |
| upstream | 上游服務(wù)器設(shè)置,主要為反向代理、負(fù)載均衡相關(guān)配置 |
| location | URL匹配特定位置后的設(shè)置,路由分發(fā) |
# ----------------全局模塊-------------------
#user nobody; # 配置worker進(jìn)程的用戶權(quán)限
worker_processes 2; # 配置啟動(dòng)worker進(jìn)程數(shù)量
#error_log logs/error.log; # 錯(cuò)誤日志文件
#error_log logs/error.log notice; # 錯(cuò)誤日志文件和日志級(jí)別的配置
#error_log logs/error.log info;
#pid logs/nginx.pid; # Nginx進(jìn)程PID存放路徑,做日志切割用
events {
use epoll; # 設(shè)定nginx的工作模式,epoll是多路復(fù)用,可選值select ,poll,kqueue,epoll,rtsig,/dev/poll
worker_connections 1024; # 連接數(shù)上限
}
http {
include mime.types; # 引入外部文件,降低主文件的復(fù)雜度
default_type application/octet-stream; # 設(shè)置mime_types
# 設(shè)置訪問(wèn)日志的格式和地址
#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; # 配置允許sendfile方式傳輸文件,開(kāi)啟高效文件傳輸模式(zero copy 方式),避免內(nèi)核態(tài)數(shù)據(jù)和用戶態(tài)區(qū)數(shù)據(jù)之間的拷貝。
#tcp_nopush on; # 開(kāi)啟TCP_NOPUSH套接字(sendfile開(kāi)啟時(shí)有用)
#keepalive_timeout 0; # 配置連接超時(shí)時(shí)間
keepalive_timeout 65;
#gzip on; # 設(shè)置是否開(kāi)啟gzip壓縮模塊
# 負(fù)載均衡配置,默認(rèn)使用輪詢,下面是基于權(quán)重。
upstream nginx {
# ip_hash;
server 172.17.0.4:8081 weight=2;
server 172.17.0.5:8081 weight=1;
}
# 虛擬主機(jī)的配置
server {
listen 80; # 虛擬主機(jī)的服務(wù)端口
server_name localhost; # 用來(lái)指定ip或者域名,多個(gè)域名用空格分開(kāi)??梢允褂?通配符或正則表達(dá)式,如:server_name ~^www\.(.+)\.com$;
#charset koi8-r; # 字符集配置
#access_log logs/host.access.log main;
# 地址匹配設(shè)置,支持正則匹配,也支持條件匹配,這里是默認(rèn)請(qǐng)求地址,用戶可以location命令對(duì)nginx進(jìn)行動(dòng)態(tài)和靜態(tài)網(wǎng)頁(yè)過(guò)濾處理
location / {
root html; # 虛擬主機(jī)的網(wǎng)頁(yè)根目錄
index index.html index.htm; # 默認(rèn)訪問(wèn)首頁(yè)文件
}
#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;
}
location /proxy_pass_uri {
# 返反向代理配置
proxy_pass http://www.xxx.com;
}
# 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;
# }
#}
}
location 規(guī)則
語(yǔ)法:location[=|~|~*|^~|@]/uri/{...}
location 規(guī)則如下:
-
=表示把URI作為字符串,以便與參數(shù)中的uri做完全的精準(zhǔn)匹配,如:
location = /aaa {
# 只有用戶請(qǐng)求/aaa時(shí)才會(huì)匹配這個(gè)規(guī)則
}
-
~表示匹配URI時(shí)是字母大小寫(xiě)敏感的。 -
~*表示匹配URI時(shí)忽略字母大小寫(xiě)問(wèn)題。 -
^~表示匹配URI時(shí)只需要其前半部分與uri參數(shù)匹配即可。例如:
location ^~ /aaa {
# 表示以/aaa開(kāi)頭的請(qǐng)求都會(huì)匹配這個(gè)規(guī)則
}
-
~*表示使用正則表達(dá)式,如:
location ~* \.(gif|jpg|png)$ {
# 表示以.gif、.jpg和.png 結(jié)尾的請(qǐng)求都會(huì)匹配這個(gè)規(guī)則
}

Rewrite(地址從定向)
rewrite是實(shí)現(xiàn)URL重定向的重要指令,他根據(jù)regex(正則表達(dá)式)來(lái)匹配內(nèi)容跳轉(zhuǎn)到replacement,結(jié)尾是flag標(biāo)記:
- 指令語(yǔ)法:
rewrite regex replacement [flag]; - 應(yīng)用位置:server、location、if
regex是正則表達(dá)式;replacement是替換值,新值;flag是處理標(biāo)志。
rewrite 最后一項(xiàng)flag參數(shù):
| 標(biāo)記符號(hào) | 說(shuō)明 |
|---|---|
| last | 本條規(guī)則匹配完成后繼續(xù)向下匹配新的location URI規(guī)則 |
| break | 本條規(guī)則匹配完成后終止,不在匹配任何規(guī)則 |
| redirect | 返回302臨時(shí)重定向 |
| permanent | 返回301永久重定向 |

簡(jiǎn)單例子1:
rewrite ^/(.*) http://www.baidu.com/ permanent; # 匹配成功后跳轉(zhuǎn)到百度,執(zhí)行永久301跳轉(zhuǎn)
簡(jiǎn)單例子2:
location ~ /b/d {
rewrite ^/ /c.html break;
root html/static/;
}
若rewrite正則匹配命中,則修改為path = /c.html;若不命中,就不執(zhí)行替換。命中后就變成了在html/static文件夾下查找c.html。
反向代理
server {
listen 80;
server_name process.enjoy.com;
#后臺(tái)服務(wù)原始路徑:172.17.0.4:8081/nginx/enjoy/getInfo
#無(wú)/,訪問(wèn)路徑:http://process.enjoy.com/nginx/enjoy/getInfo
location /nginx/enjoy { #匹配路徑/nginx/enjoy,剩余路徑/getInfo
proxy_pass http://172.17.0.4:8081;#此處未關(guān)閉,傳遞整個(gè)路徑/nginx/enjoy/getInfo到目標(biāo)ip:port
# proxy_pass http://172.17.0.4:8081/nginx/enjoy;
}
# 有/,訪問(wèn)路徑:http://process.enjoy.com/dynamic/nginx/enjoy/getInfo
location /dynamic {#匹配路徑/dynamic,剩余路徑/nginx/enjoy/getInfo
proxy_pass http://172.17.0.4:8081/;#此處關(guān)閉,只傳遞/nginx/enjoy/getInfo到目標(biāo)ip:port
}
#訪問(wèn)路徑:http://process.enjoy.com/static/a.html ---b.html/c.html
location /static {#匹配路徑/static,剩余路徑/a.html
# root html/;#root聲明,在html文件夾,查找/static/a.html文件
}
#訪問(wèn)路徑:http://process.enjoy.com/target/a.html ---b.html/c.html
location /target {#匹配路徑/target,剩余路徑/a.html
alias html/static/;##alias聲明,在html/static/文件夾,查找a.html文件
}
}

匹配過(guò)程
url=http://172.17.0.4:8081/nginx/enjoy/getInfo?a=1&b=2=域名+端口+path+param的匹配過(guò)程:
- 根據(jù)域名/IP+端口來(lái)定位虛擬主機(jī)(server)。
- 進(jìn)行path與location的匹配,其中完整的
path=匹配path(path1)+剩余path(path2)。 - 在根據(jù)代理規(guī)則進(jìn)行代理。
代理規(guī)則
-
root:在目錄里查找path1+path2路徑。 -
alias:在目錄里找path2路徑。 -
proxy_pass= ip:port/:轉(zhuǎn)發(fā)ip+端口+path2路徑。 -
proxy_pass= ip:port:轉(zhuǎn)發(fā)ip+端口+path1+path2路徑。 - 若url以/結(jié)尾,認(rèn)為是目錄,會(huì)執(zhí)行
index命令,找文件路徑為目錄+path2;否則不執(zhí)行index,表示直達(dá)文件。
負(fù)載均衡配置
- 語(yǔ)法:upstream name {...}
- 配置塊:http
upstream塊定義了一個(gè)上游服務(wù)器的集群,便于反向代理中的proxy_pass使用。
簡(jiǎn)單例子:
upstream backend {
# ip_hash;
server backend1.example.com weight=2;
server backend2.example.com weight=1;
}
server {
location /proxy_pass_uri {
# 返反向代理配置
proxy_pass http://backend;
}
}
默認(rèn)使用輪詢,可以通過(guò)weight=2來(lái)設(shè)置權(quán)重。ip_hash表示基于IP做hash,這樣可以保證同一個(gè)IP的請(qǐng)求始終落在同一個(gè)機(jī)器上。
nginx詳細(xì)配置可以參考《深入理解Nginx:模塊開(kāi)發(fā)與架構(gòu)解析(第2版)》第二章
日志配置和及切割
編寫(xiě)shell,vim /usr/local/nginx/sbin/logcut.sh:
#!/bin/bash
#設(shè)置日志文件存放目錄
LOG_HOME="/usr/local/nginx/logs/"
#備分文件名稱(chēng)
LOG_PATH_BAK="$(date -d yesterday +%Y%m%d%H%M)"
#重命名日志文件
mv ${LOG_HOME}/access.log ${LOG_HOME}/access.${LOG_PATH_BAK}.log
mv ${LOG_HOME}/error.log ${LOG_HOME}/error.${LOG_PATH_BAK}.log
#向nginx主進(jìn)程發(fā)信號(hào)重新打開(kāi)日志
kill -USR1 `cat ${LOG_HOME}/nginx.pid`
配置cron,使用crontab -e命令,將下面的代碼加到文件最后:
*/1 * * * * /usr/local/nginx/sbin/logcut.sh
然后執(zhí)行如下命令:
/etc/init.d/rsyslog start; #系統(tǒng)日志,如不開(kāi)啟,看不到定時(shí)任務(wù)日志
/etc/rc.d/init.d/crond start; #定時(shí)任務(wù)開(kāi)啟
查看系統(tǒng)日志tail -f /var/log/cron:
[root@localhost logs]# tail -f /var/log/cron
May 8 18:01:01 localhost run-parts(/etc/cron.hourly)[12056]: starting 0anacron
May 8 18:01:01 localhost run-parts(/etc/cron.hourly)[12067]: finished 0anacron
May 8 18:01:01 localhost run-parts(/etc/cron.hourly)[12056]: starting mcelog.cron
May 8 18:01:01 localhost run-parts(/etc/cron.hourly)[12075]: finished mcelog.cron
May 8 18:10:01 localhost CROND[12654]: (root) CMD (/usr/lib64/sa/sa1 1 1)
Nginx的內(nèi)置變量
| 名稱(chēng) | 說(shuō)明 |
|---|---|
| $host | 請(qǐng)求中的主機(jī)頭(Host)字段,如果請(qǐng)求中的主機(jī)頭不可用或者空,則為處理請(qǐng)求的server名稱(chēng) |
| $http_HEADER | HTTP請(qǐng)求頭中的內(nèi)容,HEADER為HTTP請(qǐng)求中的內(nèi)容轉(zhuǎn)為小寫(xiě),-變?yōu)開(kāi)(破折號(hào)變?yōu)橄聞澗€),例如:$http_user_agent(Uaer-Agent的值) |
| $remote_addr | 客戶端的IP地址。 |
| $remote_port | 客戶端的端口。 |
| $request_method | 這個(gè)變量是客戶端請(qǐng)求的動(dòng)作,通常為GET或POST。 |
| $request_uri | 這個(gè)變量等于包含一些客戶端請(qǐng)求參數(shù)的原始URI |
| $scheme | 所用的協(xié)議,比如http或者是https |
| $server_name | 服務(wù)器名稱(chēng)。 |
| $server_port | 請(qǐng)求到達(dá)服務(wù)器的端口號(hào)。 |
| $server_protocol | 請(qǐng)求使用的協(xié)議,通常是HTTP/1.0或HTTP/1.1。 |
| $uri | 請(qǐng)求中的當(dāng)前URI(不帶請(qǐng)求參數(shù),參數(shù)位于$args) |
if語(yǔ)句,常用正則
| 正則 | 說(shuō)明 |
|---|---|
| = ,!= | 比較的一個(gè)變量和字符串。 |
| ~, ~* | 與正則表達(dá)式匹配的變量,如果這個(gè)正則表達(dá)式中包含 |
| -f,!-f | 檢查一個(gè)文件是否存在。 |
| -d, !-d | 檢查一個(gè)目錄是否存在。 |
| -e,!-e | 檢查一個(gè)文件、目錄、符號(hào)鏈接是否存在。 |
| -x, !-x | 檢查一個(gè)文件是否可執(zhí)行。 |
- 靜態(tài)資源:
location ~ /rex/.*\.(htm|js|css)$ - 域名校驗(yàn):
if ( $http_origin ~ http://(.*).enjoy.com) - 瀏覽器校驗(yàn):
if ($http_user_agent ~ Firefox)