玩轉 Nginx 之:使用 Lua 擴展 Nginx 功能

一、Nginx 簡介

Nginx 做爲一款面向性能設計的HTTP服務器,相較於Apache、lighttpd具備佔有內存少,穩定性高等優點。其流行度愈來愈高,應用也愈來愈普遍,常見的應用有:網頁服務器、反向代理服務器以及電子郵件(IMAP/POP3)代理服務器,高併發大流量站點經常使用來作接入層的負載均衡,還有很是常見的用法是做爲日誌採集服務器等。html

Nginx 總體採用模塊化設計,有豐富的模塊庫和第三方模塊庫,配置靈活。其中模塊化設計是nginx的一大賣點,甚至http服務器核心功能也是一個模塊。要注意的是:nginx的模塊是靜態的,添加和刪除模塊都要對nginx進行從新編譯,這一點與Apache的動態模塊徹底不一樣。不事後來淘寶作了二次開發開源的 tengine 是支持 官方全部的 HTTP 模塊動態加載而沒必要從新編譯 Nginx,除非是第三方模塊才須要從新編譯。所以,在生產環境中,推薦用淘寶開源的 tengine,本文也以 tengine 做爲示例。linux

雖然 Nginx 有如此強大的性能以及衆多的三方模塊支持,但每次從新編譯以及尋找三方模塊對生產環境來講仍是不可接受的,幸運的是,Nginx 它是支持客戶本身 Lua 腳本編程擴展相應的功能的,並且能夠熱加載,這就給生產環境帶來了無限可能。好比我如今想要直接用Nginx + redis 作反爬蟲和頻率限制,Nginx + Kafka 作日誌的實時流處理等等。nginx

注:lvs 和 nginx 的負載均衡區別:c++

LVS:Linux Virtual Server,基於IP的負載均衡和反向代理技術,因此它幾乎能夠對全部應用作負載均衡,包括http、數據庫、在線聊天室等等,LVS工做在4層,在Linux內核中做四層交換,只花128個字節記錄一個鏈接信息,不涉及到文件句柄操做,故沒有65535最大文件句柄數的限制。LVS性能很高,能夠支持100~400萬條併發鏈接。抗負載能力強、是工做在網絡4層之上僅做分發之用,沒有流量的產生,這個特色也決定了它在負載均衡軟件裏的性能最強的,對內存和cpu、IO資源消耗比較低。 git

Nginx:基於HTTP的負載均衡和反向代理服務器,Nginx工做在網絡的7層,因此它能夠針對http應用自己來作分流策略,好比針對域名、URL、目錄結構等,相比之下LVS並不具有這樣的功能,可以很好地支持虛擬主機,可配置性很強,大約能支持3~5萬條併發鏈接。github

二、Lua 簡介

Lua 是一個簡潔、輕量、可擴展的腳本語言,也是號稱性能最高的腳本語言,用在不少須要性能的地方,好比:遊戲腳本,nginx,wireshark的腳本,當你把他的源碼下下來編譯後,你會發現解釋器竟然不到200k,很是變態。。。不少應用程序使用Lua做爲本身的嵌入式腳本語言,以此來實現可配置性、可擴展性。正則表達式

Lua原生支持的數據類型很是之少,它只提供了nil、數字(缺省是雙精度浮點數,可配置)、布爾量、字符串、表、子程序、協程(coroutine)以及用戶自定義數據這8種。可是其處理表和字符串的效率很是之高,加上元表的支持,開發者能夠高效的模擬出須要的複雜數據類型(好比集合、數組等)。Lua是一個動態弱類型語言,支持增量式垃圾收集策略。有內建的,與操做系統無關的協做式多線程(coroutine)支持。它還能夠用於嵌入式硬件,不只能夠嵌入其餘編程語言,並且能夠嵌入微處理器中。redis

三、nginx執行步驟

nginx在處理每個用戶請求時,都是按照若干個不一樣的階段依次處理的,與配置文件上的順序沒有關係,詳細內容能夠閱讀《深刻理解nginx:模塊開發與架構解析》這本書,這裏只作簡單介紹。nginx實際把http請求處理流程劃分爲了11個階段,這樣劃分的緣由是將請求的執行邏輯細分,以模塊爲單位進行處理,各個階段能夠包含任意多個HTTP模塊並以流水線的方式處理請求。這樣作的好處是使處理過程更加靈活、下降耦合度。這11個HTTP階段以下所示:數據庫

1)NGX_HTTP_POST_READ_PHASE:編程

接收到完整的HTTP頭部後處理的階段,它位於uri重寫以前,實際上不多有模塊會註冊在該階段,默認的狀況下,該階段被跳過。

2)NGX_HTTP_SERVER_REWRITE_PHASE:

URI與location匹配前,修改URI的階段,用於重定向,也就是該階段執行處於server塊內,location塊外的重寫指令,在讀取請求頭的過程當中nginx會根據host及端口找到對應的虛擬主機配置。

3)NGX_HTTP_FIND_CONFIG_PHASE:

根據URI尋找匹配的location塊配置項階段,該階段使用重寫以後的uri來查找對應的location,值得注意的是該階段可能會被執行屢次,由於也可能有location級別的重寫指令。

4)NGX_HTTP_REWRITE_PHASE:

上一階段找到location塊後再修改URI,location級別的uri重寫階段,該階段執行location基本的重寫指令,也可能會被執行屢次。

5)NGX_HTTP_POST_REWRITE_PHASE:

防止重寫URL後致使的死循環,location級別重寫的後一階段,用來檢查上階段是否有uri重寫,並根據結果跳轉到合適的階段。

6)NGX_HTTP_PREACCESS_PHASE:

下一階段以前的準備,訪問權限控制的前一階段,該階段在權限控制階段以前,通常也用於訪問控制,好比限制訪問頻率,連接數等。

7)NGX_HTTP_ACCESS_PHASE:

讓HTTP模塊判斷是否容許這個請求進入Nginx服務器,訪問權限控制階段,好比基於ip黑白名單的權限控制,基於用戶名密碼的權限控制等。

8)NGX_HTTP_POST_ACCESS_PHASE:

訪問權限控制的後一階段,該階段根據權限控制階段的執行結果進行相應處理,向用戶發送拒絕服務的錯誤碼,用來響應上一階段的拒絕。

9)NGX_HTTP_TRY_FILES_PHASE:

爲訪問靜態文件資源而設置,try_files指令的處理階段,若是沒有配置try_files指令,則該階段被跳過。

10)NGX_HTTP_CONTENT_PHASE:

處理HTTP請求內容的階段,大部分HTTP模塊介入這個階段,內容生成階段,該階段產生響應,併發送到客戶端。

11)NGX_HTTP_LOG_PHASE:

處理完請求後的日誌記錄階段,該階段記錄訪問日誌。

以上11個階段中,HTTP沒法介入的階段有4個:

3)NGX_HTTP_FIND_CONFIG_PHASE

5)NGX_HTTP_POST_REWRITE_PHASE

8)NGX_HTTP_POST_ACCESS_PHASE

9)NGX_HTTP_TRY_FILES_PHASE

剩餘的7個階段,HTTP模塊均能介入,每一個階段可介入模塊的個數也是沒有限制的,多個HTTP模塊可同時介入同一階段並做用於同一請求。

圖:Nginx 模塊執行順序與階段

Refer:

Nginx請求處理流程你瞭解嗎?

https://mp.weixin.qq.com/s/otQIhuLABU3omOLtRfJnZQ

四、ngx_lua 運行指令

ngx_lua屬於nginx的一部分,它的執行指令都包含在nginx的11個步驟之中了,相應的處理階段能夠作插入式處理,便可插拔式架構,不過ngx_lua並非全部階段都會運行的;另外指令能夠在http、server、server if、location、location if幾個範圍進行配置:

指令

所到處理階段

使用範圍

解釋

init_by_lua

init_by_lua_file

loading-config

http

nginx Master進程加載配置時執行;

一般用於初始化全局配置/預加載Lua模塊

init_worker_by_lua

init_worker_by_lua_file

starting-worker

http

每一個Nginx Worker進程啓動時調用的計時器,若是Master進程不容許則只會在init_by_lua以後調用;

一般用於定時拉取配置/數據,或者後端服務的健康檢查

set_by_lua

set_by_lua_file

rewrite

server,server if,location,location if

設置nginx變量,能夠實現複雜的賦值邏輯;此處是阻塞的,Lua代碼要作到很是快;

rewrite_by_lua

rewrite_by_lua_file

rewrite tail

http,server,location,location if

rrewrite階段處理,能夠實現複雜的轉發/重定向邏輯;

access_by_lua

access_by_lua_file

access tail

http,server,location,location if

請求訪問階段處理,用於訪問控制

content_by_lua

content_by_lua_file

content

location,location if

內容處理器,接收請求處理並輸出響應

header_filter_by_lua

header_filter_by_lua_file

output-header-filter

http,server,location,location if

設置header和cookie

body_filter_by_lua

body_filter_by_lua_file

output-body-filter

http,server,location,location if

對響應數據進行過濾,好比截斷、替換。

log_by_lua

log_by_lua_file

log

http,server,location,location if

log階段處理,好比記錄訪問量/統計平均響應時間

關於這部分詳細能夠參考這篇:

Refer [4] nginx與lua的執行順序和步驟說明  

Refer [5] ngx_lua用例說明

五、安裝 tengine 以及 Lua 擴展

(1)先安裝Nginx須要的一些類庫:

yum install gcc

yum install gcc-c++

注:此步驟只是在你的系統沒有安裝 gcc/gcc-c++ 的狀況下才須要自行編譯安裝。

(2)編譯安裝庫LuaJit-2.0.3:

./configure --prefix=/usr/local/luajit

make PREFIX=/usr/local/luajit

make install PREFIX=/usr/local/luajit

在/etc/profile文件中增長環境變量,並執行 source /etc/profile 使之生效(非必須):

export LUAJIT_LIB=/usr/install/luajit/lib

export LUAJIT_INC=/usr/install/luajit/include/luajit-2.0

注:此步驟只是在你的系統沒有安裝 LuaJIT 的狀況下才須要自行編譯安裝。

(3)下載模塊依賴 pcre-8.3四、zlib-1.2.八、ngx_devel_kit 和 lua-nginx-module,最後編譯Nginx:

完整的參數可能這樣:

nginx -V
Tengine version: Tengine/2.1.0 (nginx/1.6.2)
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) 
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx/ --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-cc-opt='-O2 -g -m64 -mtune=generic' --with-pcre=../pcre-8.33 --with-zlib=../zlib-1.2.8 --with-openssl=../openssl-1.0.1l

先 ./configure 各類配置參數以及模塊路徑,但我這裏只是用來測試就精簡了很多參數:

./configure --prefix=/opt/soft/nginx --with-pcre=/root/soft/pcre-8.37 --with-zlib=/root/soft/zlib-1.2.8 --with-openssl=/root/soft/openssl-1.0.1p  --add-module=/root/soft/lua-nginx-module-master --add-module=/root/soft/ngx_devel_kit-master --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp

而後 make、make install 便可

(5)啓動Nginx sbin/nginx,瀏覽器輸入http://localhost測試

須要注意的是:

(1)--with-pcre=/root/soft/pcre-8.37 --with-zlib=/root/soft/zlib-1.2.8 指向的是源碼路徑,而非編譯後的路徑,不然 make 會報:

cd /usr/local/pcre \
&& if [ -f Makefile ]; then make distclean; fi \
&& CC="gcc" CFLAGS="-O2 -fomit-frame-pointer -pipe " \
./configure --disable-shared
/bin/sh: ./configure: No such file or directory
make[1]: *** [/usr/local/pcre/Makefile] Error 127
make[1]: Leaving directory `/software/nginx-0.8.53'
make: *** [build] Error 2

由於 ./configure --help 看一下幫助說明:

--with-pcre=DIR                 set path to PCRE library sources

set path to PCRE library sources 是讓你設置到源碼目錄,而不是編譯安裝後的目錄

(2)pcre、zlib、openssl 等系統庫若是系統沒有集成自帶,必定要單獨編譯安裝,而 lua-nginx-module、ngx_devel_kit 等插件模塊只須要指源碼路徑給 nginx 一塊兒編譯便可,不須要單獨編譯

六、一個 Nginx + Lua 測試的例子

user work work;
worker_processes  7;

error_log  /opt/data1/logs/nginx/error.log;
pid        /var/run/nginx.pid;
worker_rlimit_nofile 800000;

events {
    use epoll;
    worker_connections  65535;
}

http {
    
    server {
        listen	80;

        set $idTest "idTest_11111111111" ;
		log_format  tracklog '$idTest $msec $remote_addr - $remote_user [$time_local] $request '
						'"$status" resp_body:"$resp_body" --"$ref1"-- '
						'"$http_user_agent"';
						
		location ~ /gzip/(.*) {
			default_type "text/html";
		
			set $resp_body "";
			content_by_lua  '   
			--zlib 解碼 post gzip 數據
            local zlib = require "zlib"
            local encoding = ngx.req.get_headers()["Content-Encoding"]
            
            if encoding == "gzip" then
				ngx.req.read_body()
                local body = ngx.req.get_body_data()
				ngx.say("++++++++++++++++++++++++++++body data:")
                ngx.print(body)
                if body then
					--ngx.var.resp_body = "55555555555555"
                    local stream = zlib.inflate()
                    ngx.var.resp_body = stream(body)
                end
            end
		';
			
			access_log  on;
            access_log              /opt/data1/logs/nginx/pc/track/ooxx.com.access.log               tracklog;
		}

		location ~ /post/(.*) {
			default_type "text/html";
			lua_need_request_body on;                                                                                            
			set $resp_body "";
			content_by_lua  '   
            ngx.var.resp_body = ngx.var.request_body
		';
			
			access_log  on;
            access_log              /opt/data1/logs/nginx/pc/track/ooxx.com.access.log               tracklog;
		}
		
	
        location ~ /lua/(.*) {
            default_type "text/html";  
            set $ref1 "Hello,Nginx & Lua !";    
            #設置nginx變量  
            set $a $1;   
            set $b $host;
        
        content_by_lua '   
            --nginx變量  
            local var = ngx.var  
            ngx.say("ngx.var.a : ", var.a, "<br/>")  
            ngx.say("ngx.var.b : ", var.b, "<br/>")  
            ngx.say("ngx.var[2] : ", var[2], "<br/>")  
            ngx.var.b = 2;  
            ngx.say("<br/>")  
  
            --請求頭  
           ngx.say(ngx.var.httpRef, "<br/>")
           local headers = ngx.req.get_headers()  
           for k,v in pairs(headers) do  
                if type(v) == "table" then  
                    ngx.say(k, " : ", table.concat(v, ","), "<br/>")  
                else  
                    ngx.say(k, " : ", v, "<br/>")  
                end  
            end  
            ngx.say("------------headers end-----------", "<br/><br/><br/>")  
            
            --get請求uri參數  
            ngx.say("uri args begin", "<br/>")  
            local uri_args = ngx.req.get_uri_args()  
            for k, v in pairs(uri_args) do  
                if type(v) == "table" then  
                    ngx.say(k, " : ", table.concat(v, ", "), "<br/>")  
                else  
                    ngx.say(k, ": ", v, "<br/>")  
                end  
            end  
            ngx.say("uri args end", "<br/>")  
            ngx.say("a: ",ngx.var.arg_a, "<br/>")
            ngx.say("b: ",ngx.var.arg_b, "<br/>")

            --未經解碼的請求 uri  
            local request_uri = headers["Host"] .. "/" .. ngx.var.request_uri;  
            ngx.say("request_uri : ", request_uri, "<br/>");  
            --解碼後的 uri  
            local decode_request_uri = headers["Host"] .. "/" .. ngx.unescape_uri(ngx.var.request_uri); 
            ngx.var.ref1 = decode_request_uri;
            ngx.say("decode request_uri : ", decode_request_uri, "<br/>");  
            --MD5  
            ngx.say("ngx.md5 : ", ngx.md5("123"), "<br/>")  
            --http time  
            ngx.say("ngx.http_time : ", ngx.http_time(ngx.time()), "<br/>")  
            --ngx.var.http_referer = "*********************"
            
            ';
			
			
	
			log_format  LogFormatv1 '$idTest@$msec@$remote_addr@-@$remote_user@[$time_local]@$request@'
						'"$status"@$body_bytes_sent@"$http_referer"@'
						'"$http_user_agent"';
	
			access_log  on;

            access_log              /opt/data1/logs/nginx/PCv1/track/ooxx.com.access.log               LogFormatv1;
        }

    }
}

須要注意的是線上的 Nginx 可能面對的是高併發場景,對於本身的 Lua 代碼最好作個壓力測試,好比:

tcpcopy 

或者

ab -c100 -n10000 'http://test.abc.com/lua/test%20haha/?a=3&b=4'

七、關於 64bit Cgywin 下編譯安裝 Tengine 的一些問題

(1)openSSL 庫不支持 64bit Cygwin

cryptlib.c:1:0: 錯誤:您選擇的 CPU 不支持 x86-64 指令集
 /* crypto/cryptlib.c */
 ^
cryptlib.c:1:0: 錯誤:您選擇的 CPU 不支持 x86-64 指令集
<builtin>: recipe for target 'cryptlib.o' failed
make[3]: *** [cryptlib.o] Error 1
make[3]: Leaving directory '/home/Jun/softs/openssl-1.0.1r/crypto'
Makefile:281: recipe for target 'build_crypto' failed

這種要麼本身去網上找補丁 patch,要麼換成 Cygwin 32bit,還有一種解決方案就是本身在安裝 Cygwin 包的時候把 openssl 也裝上,也就是說選擇安裝包的時候儘可能裝全一點。免得後續本身裝遇到各類問題。

這樣若是你本身已經裝了 openssl 那麼能夠在configure 的時候去掉openssl 相關的編譯依賴選項:

./configure  --with-openssl=/root/soft/openssl-1.0.1p (去掉該項) 

(2)nginx 啓動報錯:

nginx: [emerg] the maximum number of files supported by select() is 64

畢竟 Cygwin 仍是依賴 windows的,各類參數得針對 windows 優化,好比這個報錯是說默認配置文件裏的 worker_connections 不能超過 64 個,你改爲 20 便可。

events {
    use epoll;
    worker_connections  65535;
}

好比我這裏的編譯配置參數是(注意有些目錄可能須要提早本身建立):

./configure --prefix=/opt/soft/nginx --with-pcre=/home/Jun/softs/pcre-8.38 --with-zlib=/home/Jun/softs/zlib-1.2.8    --add-module=/home/Jun/softs/ngx_devel_kit-master --add-module=/home/Jun/softs/echo-nginx-module-master  --add-module=/home/Jun/softs/form-input-nginx-module-master     --add-module=/home/Jun/softs/set-misc-nginx-module-master  --add-module=/home/Jun/softs/lua-nginx-module-master --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp

最後 nginx.conf 稍加修改以下(注意有些目錄可能須要提早本身建立):

worker_processes  2;
 
#error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
 
events {
    worker_connections  20;
}
 
http {
     
    server {
        listen                          80;
 
        set $idTest "idTest_11111111111" ;
 
        log_format  tracklog '$idTest $msec $remote_addr - $remote_user [$time_local] $request '
                      '"$status" $body_bytes_sent --"$ref1"-- '
                      '"$http_user_agent"';
 
        log_format  LogFormatv1 '$idTest@$msec@$remote_addr@-@$remote_user@[$time_local]@$request@'
                      '"$status"@$body_bytes_sent@"$http_referer"@'
                      '"$http_user_agent"';
 
        access_log  on;
         
        location ~ /lua/(.*) {
            default_type "text/html";  
            set $ref1 "Hello,Nginx & Lua !";    
            #設置nginx變量  
            set $a $1;   
            set $b $host;
         
        content_by_lua '   
            --nginx變量  
            local var = ngx.var  
            ngx.say("ngx.var.a : ", var.a, "<br/>")  
            ngx.say("ngx.var.b : ", var.b, "<br/>")  
            ngx.say("ngx.var[2] : ", var[2], "<br/>")  
            ngx.var.b = 2;  
            ngx.say("<br/>")  
   
            --請求頭  
           ngx.say(ngx.var.httpRef, "<br/>")
           local headers = ngx.req.get_headers()  
           for k,v in pairs(headers) do  
                if type(v) == "table" then  
                    ngx.say(k, " : ", table.concat(v, ","), "<br/>")  
                else  
                    ngx.say(k, " : ", v, "<br/>")  
                end  
            end  
            ngx.say("------------headers end-----------", "<br/><br/><br/>")  
             
            --get請求uri參數  
            ngx.say("uri args begin", "<br/>")  
            local uri_args = ngx.req.get_uri_args()  
            for k, v in pairs(uri_args) do  
                if type(v) == "table" then  
                    ngx.say(k, " : ", table.concat(v, ", "), "<br/>")  
                else  
                    ngx.say(k, ": ", v, "<br/>")  
                end  
            end  
            ngx.say("uri args end", "<br/>")  
            ngx.say("a: ",ngx.var.arg_a, "<br/>")
            ngx.say("b: ",ngx.var.arg_b, "<br/>")
 
            --未經解碼的請求 uri  
            local request_uri = headers["Host"] .. "/" .. ngx.var.request_uri;  
            ngx.say("request_uri : ", request_uri, "<br/>");  
            --解碼後的 uri  
            local decode_request_uri = headers["Host"] .. "/" .. ngx.unescape_uri(ngx.var.request_uri); 
            ngx.var.ref1 = decode_request_uri;
            ngx.say("decode request_uri : ", decode_request_uri, "<br/>");  
            --MD5  
            ngx.say("ngx.md5 : ", ngx.md5("123"), "<br/>")  
            --http time  
            ngx.say("ngx.http_time : ", ngx.http_time(ngx.time()), "<br/>")  
            --ngx.var.http_referer = "*********************"
            ';
             
            access_log              /var/log/nginx/ooxx.com.access.log               tracklog;
        }
 
    }
}

最後 nginx -s reload 效果以下,http://localhost/lua/?a=1&b=2  請求也正常,和 linux 下結果一致:

八、關於 nginx 正則說明

(1)location 匹配語法規則

Nginx location 的正則匹配語法與優先級容易讓新同窗迷惑。

~      #波浪線表示執行一個正則匹配,區分大小寫

~*    #表示執行一個正則匹配,不區分大小寫

=      #進行普通字符精確匹配,與location在配置文件中的順序無關,= 精確匹配會第一個被處理

@     #"@" 定義一個命名的 location,使用在內部定向時,例如 error_page, try_files

^~     標識符後面跟一個字符串。表示普通字符匹配,若是該選項匹配,只匹配該選項,不匹配別的選項,Nginx將在這個字符串匹配後中止進行正則表達式的匹配(location指令中正則表達式的匹配的結果優先使用),如:location ^~ /images/,你但願對/images/這個目錄進行一些特別的操做,如增長expires頭,防盜鏈等,可是你又想把除了這個目錄的圖片外的全部圖片只進行增長expires頭的操做,這個操做可能會用到另一個location,例如:location ~* \.(gif|jpg|jpeg)$,這樣,若是有請求/images/1.jpg,nginx如何決定去進行哪一個location中的操做呢?結果取決於標識符^~,若是你這樣寫:location /images/,這樣nginx會將1.jpg匹配到location ~* \.(gif|jpg|jpeg)$這個location中,這並非你須要的結果,而增長了^~這個標識符後,它在匹配了/images/這個字符串後就中止搜索其它帶正則的location。

例如:

location  = / {
  # 只匹配"/".
  [ configuration A ] 
}
location  / {
  # 匹配任何請求,由於全部請求都是以"/"開始
  # 可是更長字符匹配或者正則表達式匹配會優先匹配
  [ configuration B ] 
}
location ^~ /images/ {
  # 匹配任何以 /images/ 開始的請求,並中止匹配 其它location
  [ configuration C ] 
}
location ~* \.(gif|jpg|jpeg)$ {
  # 匹配以 gif, jpg, or jpeg結尾的請求. 
  # 可是全部 /images/ 目錄的請求將由 [Configuration C]處理.   
  [ configuration D ] 
}

請求URI例子:

  • / -> 符合configuration A

  • /documents/document.html -> 符合configuration B

  • /images/1.gif -> 符合configuration C

  • /documents/1.jpg ->符合 configuration D

=      表示精確的查找地址,如location = /它只會匹配uri爲/的請求,若是請求爲/index.html,將查找另外的location,而不會匹配這個,固然能夠寫兩個location,location = /和location /,這樣/index.html將匹配到後者,若是你的站點對/的請求量較大,可使用這個方法來加快請求的響應速度。

@      表示爲一個location進行命名,即自定義一個location,這個location不能被外界所訪問,只能用於Nginx產生的子請求,主要爲error_page和try_files。

(2)location 優先級官方文檔

  1. =前綴的指令嚴格匹配這個查詢。若是找到,中止搜索。

  2. 全部剩下的常規字符串,最長的匹配。若是這個匹配使用^〜前綴,搜索中止。

  3. 正則表達式,在配置文件中定義的順序。

  4. 若是第3條規則產生匹配的話,結果被使用。不然,如同從第2條規則被使用。

(3)正則語法

~      爲區分大小寫的匹配。

~*     不區分大小寫的匹配(匹配firefox的正則同時匹配FireFox)。

!~     不匹配的

!~*    不匹配的

.     匹配除換行符之外的任意字符

\w     匹配字母或數字或下劃線或漢字

\s     匹配任意的空白符

\d     匹配數字

\b     匹配單詞的開始或結束

^     匹配字符串的開始

$     匹配字符串的結束

\W     匹配任意不是字母,數字,下劃線,漢字的字符

\S     匹配任意不是空白符的字符

\D     匹配任意非數字的字符

\B     匹配不是單詞開頭或結束的位置

捕獲     (exp)     匹配exp,並捕獲文本到自動命名的組裏

(?<name>exp)     匹配exp,並捕獲文本到名稱爲name的組裏,也能夠寫成(?'name'exp)

(?:exp)     匹配exp,不捕獲匹配的文本,也不給此分組分配組號

零寬斷言     (?=exp)     匹配exp前面的位置

(?<=exp)     匹配exp後面的位置

(?!exp)     匹配後面跟的不是exp的位置

(?<!exp)     匹配前面不是exp的位置

註釋     (?#comment)     這種類型的分組不對正則表達式的處理產生任何影響,用於提供註釋讓人閱讀

Refer:

[1] agentzh 的 Nginx 教程(版本 2015.03.19)

http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html

[2] HttpLuaModule

http://wiki.nginx.org/HttpLuaModule

[3] nginx lua api翻譯

http://blog.csdn.net/imlsz/article/details/42915473

[4] nginx與lua的執行順序和步驟說明

http://www.mrhaoting.com/?p=157

[5] ngx_lua用例說明

http://www.mrhaoting.com/?p=165

[6] 第二章 Nginx+Lua開發入門

http://jinnianshilongnian.iteye.com/blog/2186448

[7] 在Nginx使用Lua擴展功能

http://yikebocai.com/2014/11/generate-seqid-in-nginx-by-lua/

[8] nginx location匹配規則

http://www.nginx.cn/115.html

http://blog.csdn.net/gzh0222/article/details/7845981

[9] 平臺服務部署及Web框架

http://weibo.com/p/1001643875679132642345

[10] 我所熟悉的網站負載均衡技術

http://zyan.cc/post/307/

[11] Nginx、LVS及HAProxy負載均衡軟件的優缺點詳解

http://www.csdn.net/article/2014-07-24/2820837

[12] Nginx 與 Lua 實現一個簡單的服務端推方案

http://blogread.cn/it/article/5883?f=wb

[13] 深刻 Nginx:咱們是如何爲性能和規模作設計的

http://blog.jobbole.com/88766/

[14] 本博客 Nginx 配置之性能篇

https://imququ.com/post/my-nginx-conf-for-wpo.html

[15] 系統負載能力淺析

http://www.rowkey.me/blog/2015/09/09/load-analysis/

[16] Nginx學習總結:經常使用module(二)

http://shift-alt-ctrl.iteye.com/blog/2231359

[16] 爲最佳性能調優 Nginx

http://blog.jobbole.com/87531/

[17] Nginx 重寫規則指南

http://www.techug.com/85916-2

[18] 跟我學Nginx+Lua開發目錄貼

http://jinnianshilongnian.iteye.com/blog/2190344

[19] 第二章 Nginx+Lua開發入門

http://jinnianshilongnian.iteye.com/blog/2186448

[20] Nginx Third party modules

http://wiki.nginx.org/3rdPartyModules

[21] Tengine 新特性介紹

http://tengine.taobao.org/index_cn.html

[22] Nginx開發從入門到精通

http://tengine.taobao.org/book/

[23] Tengine dso動態加載模塊

http://www.mylinuxer.com/532.html

[24] Tengine動態模塊加載的使用方法

http://www.bkjia.com/Linux/992215.html#top

[25] OpenResty的現狀、趨勢、使用及學習方法

http://dwz.cn/2j0Dv1

[26] 使用nginx+lua+graphicmagick搭建圖片處理服務器

http://shanks.leanote.com/post/Untitled-55ca439338f41148cd000759-23

[27] How to handle gziped capture?

https://github.com/openresty/lua-nginx-module/issues/12

[28] Nginx與Gzip請求

http://huoding.com/2013/09/02/283

相關文章
相關標籤/搜索