nginx(一)

一、NGINX簡介

Nginx 是一個高性能的 Web 和反向代理服務器, 它具有有很多非常優越的特性:

  1. 作爲 Web 服務器:相比 Apache,Nginx 使用更少的資源,支持更多的併發連接,體現更高的效率, 這點使 Nginx 尤其受到虛擬主機提供商的歡迎。能夠支持高達 50,000 個併發連接數的響應
  2. 作爲負載均衡服務器:Nginx 既可以在內部直接支持 Rails 和 PHP,也可以支持作爲 HTTP代理服務器 對外進行服務。Nginx 用 C 編寫, 不論是系統資源開銷還是 CPU 使用效率都比 Perlbal 要好的多。
  3. 作爲郵件代理服務器: 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
 

配置文件結構

  1. 全局變量(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”問題了。
  1. 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最高效的方式
  1. 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
  1. 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
  1. 標準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的中重定向,主要用於負載均衡
  1. 可選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(拓展樣式錶轉換語言)進行轉換
  1. 郵件服務模塊

主要用於支持Ningx的郵件服務。

  1. 第三方模塊

並非有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。 阻塞方式下,進程/線程在獲取最終結果之前,被系統掛起了,也就是所謂的阻塞了,在阻塞過程中該進程什麼都幹不了, 直到最終結果反饋給它時,它才恢復運行狀態。

  • 非阻塞:非阻塞方式和阻塞相反,進程/線程在獲取最終結果之前,並沒有進入被掛起的狀態,而是該進程可以繼續執行新的任務。 當有最終結果反饋給該進程時,它再把結果交給客戶端。

事件驅動模型

  1. 事件驅動模型
事件驅動模型是實現異步非阻塞的一個手段。事件驅動模型中,一個進程(線程)就可以了。

對於web服務器來說,客戶端A的請求連接到服務端時,服務端的某個進程(Nginx worker process)會處理該請求,
此進程在沒有返回給客戶端A結果時,它又去處理了客戶端B的請求。
服務端把客戶端A以及客戶端B發來的請求作爲事件交給了“事件收集器”,
而“事件收集器”再把收集到的事件交由“事件發送器”發送給“事件處理器”進行處理。
最後“事件處理器”處理完該事件後,通知服務端進程,服務端進程再把結果返回給客戶端A、客戶端B。

在這個過程中,服務端進程做的事情屬於用戶級別的,而事件處理這部分工作屬於內核級別的。
也就是說這個事件驅動模型是需要操作系統內核來作爲支撐的。
  1. 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),每個子進程會進行進程初始化、
模塊調用以及對事件的接收和處理等工作。

在這裏插入圖片描述

  1. 主進程
    主要功能是和外界通信和對內部其他進程進行管理,具體來說有以下幾點:
  • 讀取Nginx配置文件並驗證其有效性和正確性

  • 建立、綁定和關閉socket

  • 按照配置生成、管理工作進程

  • 接收外界指令,比如重啓、關閉、重載服務等指令

  • 日誌文件管理

  1. 子進程(worker process)
    是由主進程生成,生成數量可以在配置文件中定義。該進程主要工作有:
  • 接收客戶端請求

  • 將請求依次送入各個功能模塊進行過濾處理

  • IO調用,獲取響應數據

  • 與後端服務器通信,接收後端服務器處理結果

  • 數據緩存,訪問緩存索引,查詢和調用緩存數據

  • 發送請求結果,響應客戶端請求

  • 接收主進程指令,如重啓、重載、退出等

在這裏插入圖片描述

rewrite用法

rewrite配置-if

  1. if指令
格式:if (條件判斷) { 具體的rewrite規則 }
  1. 條件舉例
條件判斷語句由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;
   }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章