文章目錄
一、NGINX簡介
Nginx 是一個高性能的 Web 和反向代理服務器, 它具有有很多非常優越的特性:
- 作爲 Web 服務器:相比 Apache,Nginx 使用更少的資源,支持更多的併發連接,體現更高的效率, 這點使 Nginx 尤其受到虛擬主機提供商的歡迎。能夠支持高達 50,000 個併發連接數的響應
- 作爲負載均衡服務器:Nginx 既可以在內部直接支持 Rails 和 PHP,也可以支持作爲 HTTP代理服務器 對外進行服務。Nginx 用 C 編寫, 不論是系統資源開銷還是 CPU 使用效率都比 Perlbal 要好的多。
- 作爲郵件代理服務器: Nginx 同時也是一個非常優秀的郵件代理服務器(最早開發這個產品的目的之一也是作爲郵件代理服務器)
Nginx 安裝非常的簡單,配置文件 非常簡潔(還能夠支持perl語法),Bugs非常少,Nginx 啓動特別容易,並且幾乎可以做到7*24不間斷運行,即使運行數個月也不需要重新啓動。你還能夠在 不間斷服務的情況下進行軟件版本的升級。佔有內存少,併發能力強,中國大陸使用nginx網站用戶有:百度、京東、新浪、網易、騰訊、淘寶等.
安裝源碼
root@centos-03 ~]# cd /usr/local/src/
[root@centos-03 src]# wget http://nginx.org/download/nginx-1.14.0.tar.gz
[root@centos-03 src]# tar zxvf nginx-1.14.0.tar.gz
### 編譯安裝可以查看配置參數
[root@centos-03 nginx-1.14.0]# ./configure --help
配置文件結構
- 全局變量(user、worker_proccesses、error_log、pid)
user nobody;
定義運行nginx服務的用戶,還可以加上組,如 user nobody nobody;
worker_processes 1;
定義nginx子進程數量,即提供服務的進程數量,該數值建議和服務cpu核數保持一致。
除了可以定義數字外,還可以定義爲auto,表示讓系統自動調整。
error_log logs/error.log;
定義錯誤日誌的路徑,可以是相對路徑(相對prefix路徑的),也可以是絕對路徑。
該配置可以在此處定義,也可以定義到http、server、location裏
error_log logs/error.log notice;
定義錯誤日誌路徑以及日誌級別.
錯誤日誌級別:常見的錯誤日誌級別有[debug|info|notice|warn|error|crit|alert|emerg],級別越高記錄的信息越少。
如果不定義默認是error
pid logs/nginx.pid;
定義nginx進程pid文件所在路徑,可以是相對路徑,也可以是絕對路徑。
worker_rlimit_nofile 100000;
定義nginx最多打開文件數限制。如果沒設置的話,這個值爲操作系統(ulimit -n)的限制保持一致。
把這個值設高,nginx就不會有“too many open files”問題了。
- events(網絡連接相關、worker_connections)
worker_connections 1024;
定義每個work_process同時開啓的最大連接數,即允許最多只能有這麼多連接。
accept_mutex on;
當某一個時刻只有一個網絡連接請求服務器時,服務器上有多個睡眠的進程會被同時叫醒,這樣會損耗一定的服務器性能。
Nginx中的accept_mutex設置爲on,將會對多個Nginx進程(worker processer)接收連接時進行序列化,防止多個進程爭搶資源。
默認就是on。
multi_accept on;
nginx worker processer可以做到同時接收多個新到達的網絡連接,前提是把該參數設置爲on。
默認爲off,即每個worker process一次只能接收一個新到達的網絡連接。
use epoll;
Nginx服務器提供了多個事件驅動器模型來處理網絡消息。
其支持的類型有:select、poll、kqueue、epoll、rtsing、/dev/poll以及eventport。
* select:只能在Windows下使用,這個事件模型不建議在高負載的系統使用
* poll:Nginx默認首選,但不是在所有系統下都可用
* kqueue:這種方式在FreeBSD 4.1+, OpenBSD2.9+, NetBSD 2.0, 和 MacOS X系統中是最高效的
* epoll: 這種方式是在Linux 2.6+內核中最高效的方式
* rtsig:實時信號,可用在Linux 2.2.19的內核中,但不適用在高流量的系統中
* /dev/poll: Solaris 7 11/99+,HP/UX 11.22+, IRIX 6.5.15+, and Tru64 UNIX 5.1A+操作系統最高效的方式
* eventport: Solaris 10最高效的方式
- http
官方文檔 http://nginx.org/en/docs/
參考鏈接: https://segmentfault.com/a/1190000012672431
參考鏈接: https://segmentfault.com/a/1190000002797601
參考鏈接:http的header https://kb.cnblogs.com/page/92320/
MIME-Type
include mime.types; //cat conf/mime.types
定義nginx能識別的網絡資源媒體類型(如,文本、html、js、css、流媒體等)
default_type application/octet-stream;
定義默認的type,如果不定義改行,默認爲text/plain.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" $http_host'
'"$http_user_agent" "$http_x_forwarded_for"';
log_format elklog '{"timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"client":"$remote_addr",'
'"size":$body_bytes_sent,'
'"responsetime": $request_time,'
'"upstreamtime":"$upstream_response_time",'
'"domain":"$http_host",'
'"url":"$request_uri",'
'"referer": "$http_referer",'
'"agent": "$http_user_agent",'
'"status": "$status",'
'"body":"$request_body"}';
其中main爲日誌格式的名字,後面的爲nginx的內部變量組成的一串字符串。
access_log logs/access.log main;
定義日誌的路徑以及採用的日誌格式,該參數可以在server配置塊中定義。
sendfile on;
是否調用sendfile函數傳輸文件,默認爲off,使用sendfile函數傳輸,可以減少user mode和kernel mode的切換,從而提升服務器性能。
對於普通應用設爲 on,如果用來進行下載等應用磁盤IO重負載應用,可設置爲off,以平衡磁盤與網絡I/O處理速度,降低系統的負載。
sendfile_max_chunk 128k;
該參數限定Nginx worker process每次調用sendfile()函數傳輸數據的最大值,默認值爲0,如果設置爲0則無限制。
tcp_nopush on;
當tcp_nopush設置爲on時,會調用tcp_cork方法進行數據傳輸。
使用該方法會產生這樣的效果:當應用程序產生數據時,內核不會立馬封裝包,而是當數據量積累到一定量時纔會封裝,然後傳輸。這樣有助於解決網絡堵塞問題。
默認值爲on。舉例:快遞員收快遞、發快遞,包裹累積到一定量纔會發,節省運輸成本。
keepalive_timeout 65 60;
該參數有兩個值,第一個值設置nginx服務器與客戶端會話結束後仍舊保持連接的最長時間,單位是秒,默認爲75s。
第二個值可以省略,它是針對客戶端的瀏覽器來設置的,可以通過curl -I看到header信息中有一項Keep-Alive: timeout=60,如果不設置就沒有這一項。
第二個數值設置後,瀏覽器就會根據這個數值決定何時主動關閉連接,Nginx服務器就不操心了。但有的瀏覽器並不認可該參數。
send_timeout
這個超時時間是發送響應的超時時間,即Nginx服務器向客戶端發送了數據包,但客戶端一直沒有去接收這個數據包。
如果某個連接超過send_timeout定義的超時時間,那麼Nginx將會關閉這個連接。
client_max_body_size 10m;
瀏覽器在發送含有較大HTTP包體的請求時,其頭部會有一個Content-Length字段,
client_max_body_size是用來限制Content-Length所示值的大小的。
這個限制包體的配置不用等Nginx接收完所有的HTTP包體,就可以告訴用戶請求過大不被接受。會返回413狀態碼。
例如,用戶試圖上傳一個1GB的文件,Nginx在收完包頭後,發現Content-Length超過client_max_body_size定義的值,
就直接發送413(Request Entity Too Large)響應給客戶端。
gzip on;
是否開啓gzip壓縮。
gzip_min_length 1k;
設置允許壓縮的頁面最小字節數,頁面字節數從header頭得content-length中進行獲取。默認值是20。建議設置成大於1k的字節數,小於1k可能會越壓越大。
gzip_buffers 4 16k;
設置系統獲取幾個單位的buffer用於存儲gzip的壓縮結果數據流。4 16k代表分配4個16k的buffer。
gzip_http_version 1.1;
用於識別 http 協議的版本,早期的瀏覽器不支持 Gzip 壓縮,用戶會看到亂碼,所以爲了支持前期版本加上了這個選項。
如果你用了Nginx反向代理並期望也啓用Gzip壓縮的話,由於末端通信是http/1.1,故請設置爲 1.1。
gzip_comp_level 6;
gzip壓縮比,1壓縮比最小處理速度最快,9壓縮比最大但處理速度最慢(傳輸快但比較消耗cpu)
gzip_types mime-type ... ;
匹配mime類型進行壓縮,無論是否指定,”text/html”類型總是會被壓縮的。
在conf/mime.conf裏查看對應的type。
示例:gzip_types text/plain application/x-javascript text/css text/html application/xml;
gzip_proxied any;
Nginx作爲反向代理的時候啓用,決定開啓或者關閉後端服務器返回的結果是否壓縮,匹配的前提是後端服務器必須要返回包含”Via”的 header頭。
以下爲可用的值:
off - 關閉所有的代理結果數據的壓縮
expired - 啓用壓縮,如果header頭中包含 "Expires" 頭信息
no-cache - 啓用壓縮,如果header頭中包含 "Cache-Control:no-cache" 頭信息
no-store - 啓用壓縮,如果header頭中包含 "Cache-Control:no-store" 頭信息
private - 啓用壓縮,如果header頭中包含 "Cache-Control:private" 頭信息
no_last_modified - 啓用壓縮,如果header頭中不包含 "Last-Modified" 頭信息
no_etag - 啓用壓縮 ,如果header頭中不包含 "ETag" 頭信息
auth - 啓用壓縮 , 如果header頭中包含 "Authorization" 頭信息
any - 無條件啓用壓縮
gzip_vary on;
和http頭有關係,會在響應頭加個 Vary: Accept-Encoding ,可以讓前端的緩存服務器緩存經過gzip壓縮的頁面,例如,用Squid緩存經過Nginx壓縮的數據。
gzip on;
#該指令用於開啓或關閉gzip模塊(on/off)
gzip_buffers 16 8k;
#設置系統獲取幾個單位的緩存用於存儲gzip的壓縮結果數據流。16 8k代表以8k爲單位,安裝原始數據大小以8k爲單位的16倍申請內存
gzip_comp_level 6;
#gzip壓縮比,數值範圍是1-9,1壓縮比最小但處理速度最快,9壓縮比最大但處理速度最慢
gzip_http_version 1.1;
#識別http的協議版本
gzip_min_length 256;
#設置允許壓縮的頁面最小字節數,頁面字節數從header頭得content-length中進行獲取。默認值是0,不管頁面多大都壓縮。這裏我設置了爲256
gzip_proxied any;
#這裏設置無論header頭是怎麼樣,都是無條件啓用壓縮
gzip_vary on;
#在http header中添加Vary: Accept-Encoding ,給代理服務器用的
gzip_types
text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
text/javascript application/javascript application/x-javascript
text/x-json application/json application/x-web-app-manifest+json
text/css text/plain text/x-component
font/opentype font/ttf application/x-font-ttf application/vnd.ms-fontobject
image/x-icon;
#進行壓縮的文件類型,這裏特別添加了對字體的文件類型
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
#禁用IE 6 gzip
- server(虛擬主機)
server{} 包含在http{}內部,每一個server{}都是一個虛擬主機(站點)。
以下爲nginx.conf配置文件中server{}部分的內容。
server {
listen 80; //監聽端口爲80,可以自定義其他端口,也可以加上IP地址,如,listen 127.0.0.1:8080;
server_name localhost; //定義網站域名,可以寫多個,用空格分隔。
#charset koi8-r; //定義網站的字符集,一般不設置,而是在網頁代碼中設置。
#access_log logs/host.access.log main; //定義訪問日誌,可以針對每一個server(即每一個站點)設置它們自己的訪問日誌。
##在server{}裏有很多location配置段
location / {
root html; //定義網站根目錄,目錄可以是相對路徑也可以是絕對路徑。
index index.html index.htm; //定義站點的默認頁。
}
#error_page 404 /404.html; //定義404頁面
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html; //當狀態碼爲500、502、503、504時,則訪問50x.html
location = /50x.html {
root html; //定義50x.html所在路徑
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#定義訪問php腳本時,將會執行本location{}部分指令
#location ~ \.php$ {
# proxy_pass http://127.0.0.1; //proxy_pass後面指定要訪問的url鏈接,用proxy_pass實現代理。
#}
# 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服務器監聽端口與地址,支持兩種形式,1 IP:Port, 2 unix:/path/to/sockt
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; //定義SCRIPT_FILENAME變量,後面的路徑/scripts爲上面的root指定的目錄
# include fastcgi_params; //引用prefix/conf/fastcgi_params文件,該文件定義了fastcgi相關的變量
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht { //訪問的url中,以/.ht開頭的,如,www.example.com/.htaccess,會被拒絕,返回403狀態碼。
# deny all; //這裏的all指的是所有的請求。
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000; //監聽8000端口
# listen somename:8080; //指定ip:port
# server_name somename alias another.alias; //指定多個server_name
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl; //監聽443端口,即ssl
# server_name localhost;
### 以下爲ssl相關配置
# ssl_certificate cert.pem; //指定pem文件路徑
# ssl_certificate_key cert.key; //指定key文件路徑
# ssl_session_cache shared:SSL:1m; //指定session cache大小
# ssl_session_timeout 5m; //指定session超時時間
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2; //指定ssl協議
# ssl_ciphers HIGH:!aNULL:!MD5; //指定ssl算法
# ssl_prefer_server_ciphers on; //優先採取服務器算法
# location / {
# root html;
# index index.html index.htm;
# }
#}
架構分析
Nginx涉及到的模塊分爲核心模塊、標準HTTP模塊、可選HTTP模塊、郵件服務模塊以及第三方模塊等五大類。
核心模塊
核心模塊是指Nginx服務器正常運行時必不可少的模塊,它們提供了Nginx最基本最核心的服務,如進程管理、權限控制、錯誤日誌記錄等。
主要包含對兩類功能的支持,一類是主體功能,包括進程管理、權限控制、錯誤日誌記錄、配置解析等,
另一類是用於響應請求事件必需的功能,包括事件驅動機制、正則表達式解析等。
ngx_core_module
ngx_errlog_module
ngx_conf_module
ngx_regex_module
ngx_events_module
ngx_event_core_module
ngx_epoll_module
- 標準HTTP模塊
標準HTTP模塊是編譯Nginx後包含的模塊,其支持Nginx服務器的標準HTTP功能。
模塊 | 功能 |
---|---|
ngx_http_core | 配置端口,URI分析,服務器響應錯誤處理,別名控制以及其他HTTP核心事務 |
ngx_http_access_module | 基於IP地址的訪問控制(允許/拒絕) |
ngx_http_auth_basic_module | 基於HTTP的身份認證 |
ngx_http_autoindex_module | 處理以“/”結尾的請求並自動生成目錄列表 |
ngx_http_browser_module | 解析HTTP請求頭中的“User-Agent”域的值 |
ngx_http_charset_module | 指定網頁編碼 |
ngx_http_empty_gif_module | 從內存創建一個1 x 1的透明gif圖片,可以快速調用 |
ngx_http_fastcgi_module | 對FastCGI的支持 |
ngx_http_geo_module | 將客戶端的IP轉化爲鍵值對變量,該模塊主要用來針對客戶的的IP來定義變量 |
ngx_http_gzip_module | 壓縮請求響應,可以減少數據傳輸 |
ngx_http_headers_filter_module | 設置HTTP響應頭 |
ngx_http_index_module | 處理以“/”結尾的請求,如果沒有找到該目錄下的index頁,就將請求轉給ngx_http_autoindex_module模塊處理 |
ngx_http_limit_req_module | 限制來自客戶端的請求的響應和處理速率 |
ngx_http_limit_conn_module | 限制來自客戶端的連接的響應和處理速率 |
ngx_http_log_module | 自定義access日誌 |
ngx_http_map_module | 創建任意鍵值對變量 |
ngx_http_memcached_module | 對Memcached的支持 |
ngx_http_proxy_module | 支持代理事務 |
ngx_http_referer_module | 對HTTP頭中的"referer"進行過濾處理,比如,實現防盜鏈功能 |
ngx_http_rewrite_module | 實現nginx的rewrite功能 |
ngx_http_scgi_module | 對SCGI的支持 |
ngx_http_upstream_module | 定義一組服務器,可以接收來自代理、Fastcgi、Memcached的中重定向,主要用於負載均衡 |
- 可選HTTP模塊
可選HTTP模塊主要用於擴展標準的HTTP功能,使其能夠處理一些特殊的HTTP請求。在編譯Nginx時,如果不指定這些模塊,默認是不會安裝的。
模塊 | 功能 |
---|---|
ngx_http_addition_module | 在響應請求的頁面開始或者結尾添加文本信息 |
ngx_http_degradation_module | 在低內存的情形下允許Nginx服務器返回444錯誤或204錯誤 |
ngx_http_perl_module | 在Nginx的配置文件中可以使用Perl腳本 |
ngx_http_flv_module | 支持將Flash多媒體信息按照流文件傳輸,可以根據客戶端指定的開始位置返回Flash |
ngx_http_geoip_module | 支持解析基於GeoIP數據庫的客戶端請求 |
ngx_google_perflools_module | 支持Google Performance Tools的一套用於C++Profile的工具集 |
ngx_http_image_filter_module | 支持將H.264/AAC編碼的多媒體信息(後綴名通常爲mp4、m4v或m4a)按照流文件傳輸,常與ngx_http_flv_module模塊一起使用 |
ngx_http_random_index_module | Nginx接收到以“/”結尾的請求時,在對應的目錄下隨機選擇一個文件作爲index文件 |
ngx_http_secure_link_module | 支持對請求鏈接的有效性檢查 |
ngx_http_ssl_module | 對HTTPS/SSL支持 |
ngx_http_stub_status_module | 支持返回Nginx服務器的統計信息,一般包括處理連接的數量、連接成功的數量、處理的請求數、讀取和返回的Header信息數等信息 |
ngx_http_sub_module | 使用指定的字符串替換響應信息中的信息 |
ngx_http_dav_module | 支持HTTP協議和WebDAV協議中PUT、DELETE、MKCOL、COPY和MOVE方法 |
ngx_http_xslt_module | 將XML響應信息使用XSLT(拓展樣式錶轉換語言)進行轉換 |
- 郵件服務模塊
主要用於支持Ningx的郵件服務。
- 第三方模塊
並非有Nginx官方提供,而是由第三方機構或者個人開發的模塊,用於實現某種特殊功能。
echo-nginx-module 支持在Nginx配置文件中使用echo、sleep、time以及exec等類shell命令
lua-nginx-module 使Nginx支持lua腳本語言
WEB請求機制
- 同步機制
同步、異步發生在當客戶端發起請求後,服務端處理客戶端的請求時。
同步機制,是指客戶端發送請求後,需要等待服務端(內核)返回信息後,再繼續發送下一個請求。
在同步機制中,所有的請求在服務器端得到同步,即發送方和接收方對請求的處理步調是一致的。
- 異步機制
異步機制,是指客戶端發出一個請求後,不等待服務端(內核)返回信息,就繼續發送下一個請求。
在異步機制中,所有來自發送方的請求形成一個隊列,接收方處理完後再通知發送方。
舉例:一家酒店前臺,在旺季的高峯時間段會接很多預定酒席的電話。
如果是同步機制情況下,前臺每接一個電話後先不掛掉電話,而是去查詢有無剩餘酒席,查到結果後,告訴客戶。
如果是異步機制情況下,前臺每接一個預定電話直接回復客戶,一會回覆,此時前臺把查詢這件事情交給了另外的同事,
該前臺掛掉電話後,繼續處理其他客戶的事情,當另外的同事查詢到結果後再通知給前臺,前臺再通知客戶。
- 阻塞
阻塞與非阻塞發生在IO調度中,比如內核到磁盤IO。
阻塞方式下,進程/線程在獲取最終結果之前,被系統掛起了,也就是所謂的阻塞了,在阻塞過程中該進程什麼都幹不了,
直到最終結果反饋給它時,它才恢復運行狀態。
- 非阻塞
非阻塞方式和阻塞相反,進程/線程在獲取最終結果之前,並沒有進入被掛起的狀態,而是該進程可以繼續執行新的任務。
當有最終結果反饋給該進程時,它再把結果交給客戶端。
舉例:依然是酒店前臺接待預定酒席電話的案例。
此時角色不再是前臺,而是她的查詢有無剩餘酒席的同事。如果是阻塞方式,該同事在查詢有無剩餘酒席的過程中,需要傻傻地
等待酒店管理系統給他返回結果,在此期間不能做其他事情。
如果是非阻塞,該同事在等待酒店管理系統給他返回結果這段時間,可以做其他事情,比如可以通知前臺剩餘酒席的情況。
- Nginx的請求機制
Nginx之所以可以支持高併發,是因爲Nginx用的是異步非阻塞的機制,而Nginx是靠事件驅動模型來實現這種機制的。
在Nginx的事件驅動模型下,客戶端發起的所有請求在服務端都會被標記爲一個事件,Nginx會把這些事件收集到“事件收集器”裏,
然後再把這些事件交給內核去處理。
-
線程是進程的一個子單元,線程比進程好的地方是可以節省更多的資源,假如我開一個進程耗費的資源是20兆,那我開10個線程也是佔用20兆,這樣我同樣的內存可以開更多的線程出來,每個線程也是處理一個請求,線程也有弊端,需要和其他的線程共享內存,穩定性不是很好
-
同步:同步、異步發生在當客戶端發起請求後,服務端處理客戶端的請求時。 同步機制,是指客戶端發送請求後,需要等待服務端(內核)返回信息後,再繼續發送下一個請求。 在同步機制中,所有的請求在服務器端得到同步,即發送方和接收方對請求的處理步調是一致的。
-
異步:異步機制,是指客戶端發出一個請求後,不等待服務端(內核)返回信息,就繼續發送下一個請求。 在異步機制中,所有來自發送方的請求形成一個隊列,接收方處理完後再通知發送方。
-
阻塞:阻塞與非阻塞發生在IO調度中,比如內核到磁盤IO。 阻塞方式下,進程/線程在獲取最終結果之前,被系統掛起了,也就是所謂的阻塞了,在阻塞過程中該進程什麼都幹不了, 直到最終結果反饋給它時,它才恢復運行狀態。
-
非阻塞:非阻塞方式和阻塞相反,進程/線程在獲取最終結果之前,並沒有進入被掛起的狀態,而是該進程可以繼續執行新的任務。 當有最終結果反饋給該進程時,它再把結果交給客戶端。
事件驅動模型
- 事件驅動模型
事件驅動模型是實現異步非阻塞的一個手段。事件驅動模型中,一個進程(線程)就可以了。
對於web服務器來說,客戶端A的請求連接到服務端時,服務端的某個進程(Nginx worker process)會處理該請求,
此進程在沒有返回給客戶端A結果時,它又去處理了客戶端B的請求。
服務端把客戶端A以及客戶端B發來的請求作爲事件交給了“事件收集器”,
而“事件收集器”再把收集到的事件交由“事件發送器”發送給“事件處理器”進行處理。
最後“事件處理器”處理完該事件後,通知服務端進程,服務端進程再把結果返回給客戶端A、客戶端B。
在這個過程中,服務端進程做的事情屬於用戶級別的,而事件處理這部分工作屬於內核級別的。
也就是說這個事件驅動模型是需要操作系統內核來作爲支撐的。
- Nginx的事件驅動模型
Nginx的事件驅動模型,支持select、poll、epoll、rtsig、kqueue、/dev/poll、eventport等。
最常用的是前三種,其中kqueue模型用於支持BSD系列平臺的事件驅動模型。kqueue是poll模型的一個變種,本質上和epoll一樣。
/dev/poll是Unix平臺的事件驅動模型,其主要在Solaris7及以上版本、HP/UX11.22及以上版本、IRIX6.5.15及以上版本、
Tru64 Unix 5.1A及以上版本的平臺使用。
eventport是用於支持Solaris10及以上版本的事件驅動模型。
- select模型
Linux和Windows都支持,使用select模型的步驟是:
1. 創建所關注事件的描述符集合,對於一個描述符,可以關注其上面的讀(Read)事件、寫(Write)事件以及異常發生(Exception)事件。
在select模型中,要創建這3類事件描述符集合。
2. 調用底層提供的select()函數,等待事件發生。
3. 輪詢所有事件描述符集合中的每一個事件描述符,檢查是否有相應的事件發生,如果有就進行處理。
- poll模型
poll模型是Linux平臺上的事件驅動模型,在Linux2.1.23中引入的,Windows平臺不支持該模型。
poll模型和select模型工作方式基本相同,區別在於,select模型創建了3個描述符集合,而poll模型只創建一個描述符集合。
- epoll模型
epoll模型屬於poll模型的變種,在Linux2.5.44中引入。epoll比poll更加高效,原因在於它不需要輪詢整個描述符集合,
而是Linux內核會關注事件集合,當有變動時,內核會發來通知。
Nginx架構
Nginx服務器使用 master/worker 多進程模式。
主進程(Master process)啓動後,會接收和處理外部信號;
主進程啓動後通過fork() 函數產生一個或多個子進程(work process),每個子進程會進行進程初始化、
模塊調用以及對事件的接收和處理等工作。
- 主進程
主要功能是和外界通信和對內部其他進程進行管理,具體來說有以下幾點:
-
讀取Nginx配置文件並驗證其有效性和正確性
-
建立、綁定和關閉socket
-
按照配置生成、管理工作進程
-
接收外界指令,比如重啓、關閉、重載服務等指令
-
日誌文件管理
- 子進程(worker process)
是由主進程生成,生成數量可以在配置文件中定義。該進程主要工作有:
-
接收客戶端請求
-
將請求依次送入各個功能模塊進行過濾處理
-
IO調用,獲取響應數據
-
與後端服務器通信,接收後端服務器處理結果
-
數據緩存,訪問緩存索引,查詢和調用緩存數據
-
發送請求結果,響應客戶端請求
-
接收主進程指令,如重啓、重載、退出等
rewrite用法
rewrite配置-if
- if指令
格式:if (條件判斷) { 具體的rewrite規則 }
- 條件舉例
條件判斷語句由Nginx內置變量、邏輯判斷符號和目標字符串三部分組成。
其中,內置變量是Nginx固定的非自定義的變量,如,$request_method, $request_uri等。
邏輯判斷符號,有=, !=, ~, ~*, !~, !~*
!表示相反的意思,~爲匹配符號,它右側爲正則表達式,區分大小寫,而~*爲不區分大小寫匹配。
目標字符串可以是正則表達式,通常不用加引號,但表達式中有特殊符號時,比如空格、花括號、分號等,需要用單引號引起來。
示例1
if ($request_method = POST) //當請求的方法爲POST時,直接返回405狀態碼
{
return 405; //在該示例中並未用到rewrite規則,if中支持用return指令。
}
示例2
if ($http_user_agent ~ MSIE) //user_agent帶有MSIE字符的請求,直接返回403狀態碼
{
return 403;
}
如果想同時限制多個user_agent,還可以寫成這樣
if ($http_user_agent ~ "MSIE|firefox|spider")
{
return 403;
}
示例3
if(!-f $request_filename) //當請求的文件不存在,將會執行下面的rewrite規則
{
rewrite 語句;
}
示例4
if($request_uri ~* 'gid=\d{9,12}/') //\d表示數字,{9,12}表示數字出現的次數是9到12次,如gid=123456789/就是符合條件的。
{
rewrite 語句;
}
示例5
if ($request_filename ~* ^.*?.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)$){
add_header Content-Disposition attachment;
}
即對所有的訪問,響應頭中增加Content-Disposition。
chrome的表現:直接下載login.jpg。
ie9表現:提示“您是要打開還是保存來自file.test.com的login.jpg”
爲文件換個名字
有時你希望用戶側使用另外的名字保存文件,這時只要增加filename字段即可。
Content-Disposition:attachment;filename=NewnName
location /50018/ {
alias /AECDN/appcdn/50018/;
add_header Content-MD5 $file_md5; //給HTTP頭部增加文件md5標識;
expires 30d;
add_header Content-Disposition “attachment;filename=xfz.apk”; //重定向文件名爲指定名稱;
}
}
rewrite中的break和last
兩個指令用法相同,但含義不同,需要放到rewrite規則的末尾,用來控制重寫後的鏈接是否繼續被nginx配置執行(主要是rewrite、return指令)。
示例1(連續兩條rewrite規則):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html ;
rewrite /2.html /3.html ;
}
當我們請求1.html時,最終訪問到的是3.html,兩條rewrite規則先後執行。
break和last在location {}外部
格式:rewrite xxxxx break;
示例2(增加break):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
當我們請求1.html時,最終訪問到的是2.html
說明break在此示例中,作用是不再執行break以下的rewrite規則。
但,當配置文件中有location時,它還會去執行location{}段的配置(請求要匹配該location)。
示例3(break後面還有location段):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
location /2.html {
return 403;
}
}
當請求1.html時,最終會返回403狀態碼,說明它去匹配了break後面的location{}配置。
以上2個示例中,可以把break替換爲last,它們兩者起到的效果一模一樣。
當break和last在location{}裏面
示例4(什麼都不加):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
當請求/1.html,最終將會訪問/b.html,連續執行location /下的兩次rewrite,跳轉到了/3.html,然後又匹配location /3.html
示例5(增加break):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
當請求/1.html,最終會訪問/2.html
在location{}內部,遇到break,本location{}內以及後面的所有location{}內的所有指令都不再執行。
示例6(增加last):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
當請求/1.html,最終會訪問/a.html
在location{}內部,遇到last,本location{}內後續指令不再執行,而重寫後的url再次從頭開始,從頭到尾匹配一遍規則。
結論
- 當rewrite規則在location{}外,break和last作用一樣,遇到break或last後,其後續的rewrite/return語句不再執行。但後續有location{}的話,還會近一步執行location{}裏面的語句,當然前提是請求必須要匹配該location。
- 當rewrite規則在location{}裏,遇到break後,本location{}與其他location{}的所有rewrite/return規則都不再執行。
- 當rewrite規則在location{}裏,遇到last後,本location{}裏後續rewrite/return規則不執行,但重寫後的url再次從頭開始執行所有規則,哪個匹配執行哪個。
nginx中的return用法
1.直接返回狀態碼·
[root@centos-03 conf]# cat vhost/default.conf
server {
listen 80 default_server;
return 403;
}
[root@centos-03 conf]# curl -x127.0.0.1:80 fjldsfjdsajfl
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.14.0</center>
</body>
</html>
curl -x127.0.0.1:80 fjldsfjdsajfl -I
HTTP/1.1 403 Forbidden
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:42:18 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
2.在if中使用return直接返回404
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 404;
rewrite /(.*) /aaa.txt; #該行配置不會被執行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 404 Not Found
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:50:11 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
3.返回字符串
[root@centos-03 conf]# vim vhost/1.conf ^C
[root@centos-03 conf]# cat vhost/1.conf
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 200 "ok";
rewrite /(.*) /aaa.txt; #該行配置不會被執行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:53:53 GMT
Content-Type: application/octet-stream
Content-Length: 2
Connection: keep-alive
curl -x127.0.0.1:80 www.1.com/12/.htpasswd
ok
4.返回url
[root@centos-03 conf]# vim vhost/1.conf ^C
[root@centos-03 conf]# cat vhost/1.conf
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 200 "<html><script>window.location.href='//$host$request_uri';</script></html>";
rewrite /(.*) /aaa.txt; #該行配置不會被執行。
}
}
[root@centos-03 conf]# curl -x127.0.0.1:80 www.1.com/12/.htpasswd
<html><script>window.location.href='//www.1.com/12/.htpasswd';</script></html>[root@centos-03 conf]# curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 13:07:28 GMT
Content-Type: application/octet-stream
Content-Length: 78
Connection: keep-alive
5.301跳轉
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 301 http://www.baidu.com;
rewrite /(.*) /aaa.txt; #該行配置不會被執行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 13:12:38 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: http://www.baidu.com
rewrite規則語法
rewrite規則
格式:rewrite regex replacement [flag]
* rewrite配置可以在server、location以及if配置段內生效
* regex是用於匹配URI的正則表達式,其不會匹配到$host(域名)
* replacement是目標跳轉的URI,可以以http://或者https://開頭,也可以省略掉$host,直接寫$request_uri部分(即請求的鏈接)
* flag,用來設置rewrite對URI的處理行爲,其中有break、last、rediect、permanent,其中break和last在前面已經介紹過,
rediect和permanent的區別在於,前者爲臨時重定向(302),而後者是永久重定向(301),對於用戶通過瀏覽器訪問,這兩者的效果是一致的。
但是,對於搜索引擎蜘蛛爬蟲來說就有區別了,使用301更有利於SEO。所以,建議replacemnet是以http://或者https://開頭的flag使用permanent。
示例1
location / {
rewrite /(.*) http://www.test.com/$1 permanent;
}
說明:.*爲正則表達式,用()括起來,在後面的URI中可以調用它,第一次出現的()用$1調用,第二次出現的()用$2調用,以此類推。
示例2
location / {
rewrite /.* http://www.test.com$request_uri permanent;
}
說明:在replacement中,支持變量,這裏的$request_uri就是客戶端請求的鏈接
示例3
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
rewrite /(.*) /abc/$1 redirect;
}
說明:本例中的rewrite規則有問題,會造連續循環,最終會失敗,解決該問題有兩個方案。
關於循環次數,經測試發現,curl 會循環50次,chrome會循環80次,IE會循環120次,firefox會循環20次。
示例4
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
rewrite /(.*) /abc/$1 break;
}
說明:在rewrite中使用break,會避免循環。
示例5
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
if ($request_uri !~ '^/abc/')
{
rewrite /(.*) /abc/$1 redirect;
}
}
說明:加一個條件限制,也可以避免產生循環
rewrite實例
域名跳轉(域名重定向)
示例1(不帶條件的):
server{
listen 80;
server_name www.testlinux.com;
rewrite /(.*) http://www.test.com/$1 permanent;
.......
}
示例2(帶條件的):
server{
listen 80;
server_name www.testlinux.com testlinux.com;
if ($host != 'www.testlinux.com')
{
rewrite /(.*) http://www.testlinux.com/$1 permanent;
}
.......
}
示例3(http跳轉到https):
server{
listen 80;
server_name www.testlinux.com;
rewrite /(.*) https://www.testlinux.com/$1 permanent;
.......
}
示例4(域名訪問二級目錄)
server{
listen 80;
server_name bbs.testlinux.com;
rewrite /(.*) http://www.testlinux.com/bbs/$1 last;
.......
}
示例5(靜態請求分離)
server{
listen 80;
server_name www.testlinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js)$
{
rewrite /(.*) http://img.testlinux.com/$1 permanent;
}
.......
}
或者:
server{
listen 80;
server_name www.testlinux.com;
if ( $uri ~* 'jpg|jpeg|gif|css|png|js$')
{
rewrite /(.*) http://img.testlinux.com/$1 permanent;
}
.......
}
示例6防盜鏈
server{
listen 80;
server_name www.testlinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|flv)$
{
valid_referers none blocked server_names *.testlinux.com testlinux.com *.test.com test.com;
if ($invalid_referer)
{
rewrite /(.*) http://img.testlinux.com/images/forbidden.png;
}
}
.......
}
說明:*這裏是通配,跟正則裏面的*不是一個意思,none指的是referer不存在的情況(curl -e 測試),
blocked指的是referer頭部的值被防火牆或者代理服務器刪除或者僞裝的情況,
該情況下,referer頭部的值不以http://或者https://開頭(curl -e 後面跟的referer不以http://或者https://開頭)。
或者:
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|flv)$
{
valid_referers none blocked server_names *.testlinux.com *.test.com testlinux.com test.com;
if ($invalid_referer)
{
return 403;
}
}
rewrite多個條件的並且
示例7:
location /{
set $rule 0;
if ($document_uri !~ '^/abc')
{
set $rule "${rule}1";
}
if ($http_user_agent ~* 'ie6|firefox')
{
set $rule "${rule}2";
}
if ($rule = "012")
{
rewrite /(.*) /abc/$1 redirect;
}
}
指定安卓設備匹配URL跳轉
set $flag 0;
if ( $host = "appcdn.haowanyou.com" ){
set $flag "1";
}
if ( $request_uri = "/60018/1.0.0/xxx.apk" ){
set $flag "${flag}1";
}
if ( $http_user_agent ~* "mobile" ){
set $flag "${flag}1";
}
if ( $http_user_agent ~* "android" ){
set $flag "${flag}1";
}
if ( $flag = "1111" ){
rewrite ^/(.*)$ https://appcdn.haowanyou.com/1.0.1/default.apk redirect;
}