摘要:例如設置響應狀態碼并退出注意,設置狀態碼僅在響應頭發送前有效,并且該函數調用之后該函數后面的將被忽略掉,因為已經了。
1. 介紹一、介紹
二、安裝
三、運行
四、開發
Tengine:輕量級、高性能、高并發、配置化、模塊化、可擴展、可移植的Web和反向代理 服務器,Tengine是nginx超集,但做了很多優化,包含了很多比較有用的模塊,比如直接包含了lua、proc等很有用的模塊。
Lua:一個很輕量級的 腳本,也號稱性能最高的 腳本。代碼總共不到600k,32個C文件,23個頭文件:
root@j9 ~/lua-5.1.5/src# du -sh ./ 572K ./ root@j9 ~/lua-5.1.5/src# ls *.c | wc -l 32 root@j9 ~/lua-5.1.5/src# ls *.h | wc -l 23 root@j9 ~/lua-5.1.5/src#
可以非常容易的嵌入C和C++工程中,也比較容易與C和C++互動,這也是目前Lua主要的用法。
ngx_lua:一個nginx很重要的第三方模塊,作者:章亦春(agentzh、春哥),結合了nginx和Lua各自優點,把Lua嵌入nginx中,使其支持Lua來快速開發基于nginx下的業務邏輯。
https://github.com/openresty/lua-nginx-module
2.1、LuaJIT
wget -c http://luajit.org/download/LuaJIT-2.0.4.tar.gz tar xzvf LuaJIT-2.0.4.tar.gz cd LuaJIT-2.0.4 make install PREFIX=/usr/local/luajit #注意環境變量! export LUAJIT_LIB=/usr/local/luajit/lib export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
2.2、Tengine
tengine最新代碼中已經包含lua模塊了,直接git clone下來就可以
git clone https://github.com/alibaba/tengine.git cd tengine ./configure --prefix=/opt/tengine --with-http_lua_module make make install
如果是原生nginx的話,得自行下載lua模塊代碼:
wget http://nginx.org/download/nginx-1.7.8.tar.gz tar xvf nginx-1.7.8.tar.gz cd nginx-1.7.8 mkdir modules cd modules git clone https://github.com/openresty/lua-nginx-module.git cd .. ./configure --prefix=/opt/nginx --add-module=./modules/lua-nginx-module/ make make install3. 運行
修改/opt/tengine/conf/nginx.conf:
worker_processes 1; error_log logs/error.log; pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; 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; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } location /hello_lua { content_by_lua ngx.say("Lua: hello world!") ; } } }
運行tengine:
root@j9 ~/tengine# /opt/tengine/sbin/nginx
curl訪問一下hello_lua:
root@j9 ~/tengine# curl http://localhost/hello_lua Lua: hello world!
運行ok。
4、開發語法
入門
深入
4.1、語法
參考:
Lua簡明教程
Lua在線lua學習教程
4.2、入門
4.2.1、API
ngx.print
輸出響應內容體;
例如:ngx.print("a", "b", "c")
ngx.say
跟ngx.print的區別只是最后會多輸出一個換行符;
例如:ngx.say("a", "b", "c")
ngx.status
設置響應HTTP狀態碼;
注意,設置狀態碼僅在響應頭發送前有效。當調用ngx.say或者ngx.print時自動發送響應狀態碼(默認為200);可以通ngx.headers_sent來判斷是否發送了響應狀態碼。
例如:ngx.status = 200
ngx.exit
設置響應HTTP狀態碼并退出;
注意,設置狀態碼僅在響應頭發送前有效,并且該函數調用之后該函數后面的lua將被忽略掉,因為已經exit了。
例如:ngx.exit(200)
ngx.header
輸出響應頭;
注意,頭部字段中含有橫杠(-)的要轉換成下劃線(_),ngx_lua模塊自動將_轉換成-。
例如:ngx.header["X-Cache"] = "HIT" 或者 ngx.header.X_Cache = "HIT"或者ngx.header.X_Cache = {"AA", "BB"}
ngx.redirect
301或者302重定向
例如:ngx.redirect("http://www.taobao.org", 301)
ngx.log
打印nginx錯誤日志,日志級別有:ngx.STDERR、ngx.EMERG、ngx.ALERT、ngx.CRIT、ngx.ERR、ngx.WARN、ngx.NOTICE、ngx.INFO、ngx.DEBUG
例如:ngx.log(ngx.ERR, "test: ", "ok")
例子:
server { listen 9898; location / { default_type "text/html"; content_by_lua local headers_sent_1 = ngx.headers_sent ngx.header["X-Cache"] = "HIT" ngx.header.Y_Cache = "MISS" ngx.header.Z_Cache = {"AA", "BB"} ngx.status = 602 local headers_sent_2 = ngx.headers_sent ngx.print("a", "b") local headers_sent_3 = ngx.headers_sent ngx.say("c", "d") ngx.say("e", "f") ngx.say("headers_sent_1: ", tostring(headers_sent_1)) ngx.say("headers_sent_2: ", tostring(headers_sent_2)) ngx.say("headers_sent_3: ", tostring(headers_sent_3)) ngx.log(ngx.ERR, "ngx.log test ok") ngx.exit(601) ngx.say("g", "h") ; } location ^~ /redirect { content_by_lua ngx.redirect("http://www.taobao.org", 301) ; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9898/" -i HTTP/1.1 602 Server: Tengine/2.2.0 Date: Mon, 19 Oct 2015 16:10:42 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive X-Cache: HIT Y-Cache: MISS Z-Cache: AA Z-Cache: BB abcd ef headers_sent_1: false headers_sent_2: false headers_sent_3: true root@j9 ~# curl "http://127.0.0.1:9898/redirect" -i HTTP/1.1 301 Moved Permanently Server: Tengine/2.2.0 Date: Mon, 19 Oct 2015 16:18:16 GMT Content-Type: text/html Content-Length: 284 Connection: keep-alive Location: http://www.taobao.org301 Moved Permanently 301 Moved Permanently
The requested resource has been assigned a new permanent URI.
Powered by Tengine/2.2.0 root@j9 ~#
ngx.var
讀取nginx變量,如nginx變量為$a,則在Lua中通過ngx.var.a獲取,也可以給nginx變量賦值如ngx.var.a = "aa",前提是該變量在nginx中必須存在,不能在Lua中創建nginx變量。另外,對于nginx location中使用正則捕獲的捕獲組可以使用ngx.var[捕獲組數字]獲取。
例子
server { listen 9898; location ~ /var/([^/]*)/([^/]*) { default_type "text/html"; set $a "aaa"; set $b $host; content_by_lua ngx.say("$a: ", ngx.var.a) ngx.say("$b: ", ngx.var.b) ngx.say("$host: ", ngx.var.host) ngx.say("$arg_id: ", ngx.var.arg_id) ngx.say("$1: ", ngx.var[1]) ngx.say("$2: ", ngx.var[2]) ; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9898/var/aaaa/bbbb?id=22" -H "Host: www.taobao.org" $a: aaa $b: www.taobao.org $host: www.taobao.org $arg_id: 22 $1: aaaa $2: bbbb root@j9 ~#
ngx.req.raw_header
未解析的請求頭字符串; 例如:ngx.req.raw_header()
ngx.req.get_headers
獲取請求頭,默認只獲取前100個頭部,如果想要獲取所有頭部可以調用ngx.req.get_headers(0);獲取帶中劃線的請求頭時要把中劃線轉換成下劃線使用如headers.user_agent這種方式;如果一個請求頭有多個值,則返回的是table; 例如:ngx.req.get_headers()
ngx.req.get_uri_args
獲取url請求參數,其用法與ngx.req.get_headers類似;
ngx.req.get_post_args
獲取post請求body參數,其用法與ngx.req.get_uri_args類似,但必須提前調用ngx.req.read_body();
ngx.req.read_body
如果要獲取請求的body,則需要調用ngx.req.read_body(),否則獲取不到body數據,(ps:也可以在nginx配置文件中加入指令lua_need_request_body on;來開啟讀取body,但官方不推薦)
ngx.req.discard_body
忽略請求的body 注意,如果處理一個包含body的請求且需要ngx.exit時,需要調用此函數來忽略body,否則nginx可能將body當成header來解析,從而導致400錯誤;
ngx.req.get_body_data
獲取請求body數據
例子
location ^~ /req { content_by_lua ngx.say("===========ngx.req.raw_header=") ngx.say(ngx.req.raw_header()) local headers = ngx.req.get_headers() ngx.say("===========headers============") ngx.say("Host: ", headers["Host"]) ngx.say("user-agent: ", headers.user_agent) ngx.say("===========all headers========") for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, ": ", table.concat(v, ",")) else ngx.say(k, ": ", v) end end ngx.say("===========args===============") local args = ngx.req.get_uri_args() for k,v in pairs(args) do ngx.say(k, ": ", v) end ngx.say("===========body===============") ngx.say("body data: ", ngx.req.get_body_data()) ngx.req.read_body() local post_args = ngx.req.get_post_args() for k,v in pairs(post_args) do ngx.say(k, ": ", v) end ngx.say("body data: ", ngx.req.get_body_data()) ; }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9898/req?a=11&b=22&c=33" --data "d=11&e=22&f=33" ===========ngx.req.raw_header= POST /req?a=11&b=22&c=33 HTTP/1.1 User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3 Host: 127.0.0.1:9898 Accept: */* Content-Length: 14 Content-Type: application/x-www-form-urlencoded ===========headers============ Host: 127.0.0.1:9898 user-agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3 ===========all headers======== host: 127.0.0.1:9898 content-type: application/x-www-form-urlencoded accept: */* content-length: 14 user-agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3 ===========args=============== b: 22 a: 11 c: 33 ===========body=============== body data: nil d: 11 f: 33 e: 22 body data: d=11&e=22&f=33 root@j9 ~#
ngx.escape_uri/ngx.unescape_uri
uri編碼解碼
ngx.encode_args/ngx.decode_args
參數編碼解碼
ngx.encode_base64/ngx.decode_base64
BASE64編碼解碼
ngx.md5
md5加密
例子
location ^~ /code { content_by_lua local request_uri = ngx.var.request_uri local args = {a=11, b=22} ngx.say("request uri: ", request_uri) ngx.say("unescape request uri: ", ngx.unescape_uri(request_uri)) ngx.say("encode args: ", ngx.encode_args(args)) ngx.say("encode base64 request uri: ", ngx.encode_base64(request_uri)) ngx.say("md5(123456): ", ngx.md5("123456")) ; }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9898/code?name=%E9%87%91%E4%B9%9D" request uri: /code?name=%E9%87%91%E4%B9%9D unescape request uri: /code?name=金九 encode args: a=11&b=22 encode base64 request uri: L2NvZGU/bmFtZT0lRTklODclOTElRTQlQjklOUQ= md5(123456): e10adc3949ba59abbe56e057f20f883e root@j9 ~#
ngx.shared.DICT
共享內存接口,其中DICT為共享內存zone名稱,在nginx.conf中通過指令lua_shared_dict配置,而且lua_shared_dict指令配置的共享內存大小最小值為8k。
例子
lua_shared_dict cc_shared_data 16k; server { listen 9999; default_type "text/html"; location ^~ /shared_data { content_by_lua local shared_data = ngx.shared.cc_shared_data local i = shared_data:get("i") if not i then shared_data:set("i", 1) end i = shared_data:incr("i", 1) ngx.say("i: ", i) ; } }
測試結果
root@j9 ~# curl "http://127.0.0.1:9999/shared_data" i: 2 root@j9 ~# curl "http://127.0.0.1:9999/shared_data" i: 3 root@j9 ~#
ngx.shared.DICT詳細說明:http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT
4.2.2、指令
指令 | 階段 | 范圍 | 說明 |
---|---|---|---|
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 rewrite | 階段處理,可以實現復雜的轉發/重定向邏輯 |
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 | 階段處理,比如記錄訪問量/統計平均響應時間 |
更詳細的解釋請參考官網:http://wiki.nginx.org/HttpLua...
init_by_lua
每次nginx重新加載配置時執行,可以用它來完成一些耗時模塊的加載,或者初始化一些全局配置;
例子:
init_by_lua cjson = require("cjson") ngx.log(ngx.ERR, "init_by_lua ok") ; server { listen 9292; default_type "text/html"; location / { content_by_lua local arg_json = cjson.decode(ngx.var.arg_json) ngx.say("aa: ", arg_json.aa) ; } }
測試結果:
root@j9 ~# curl http://127.0.0.1:9292/?json={"aa":111,"bbb":222} aa: 111 root@j9 ~#
init_worker_by_lua
每個worker啟動之后初始化時執行,通常用于每個worker都要做的工作,比如啟動定時任務
例子:
worker_processes 2; http { #這里省略了其他配置 init_worker_by_lua ngx.log(ngx.ERR, "test init_worker_by_lua") -- TODO: 啟動定時任務 ; }
grep一下error.log,會發現兩條包含"test init_worker_by_lua"關鍵字的log,說明每個worker都會執行這個Lua代碼。
set_by_lua
語法:set_by_lua resluascriptstr
arg1 $arg2...; 在Lua代碼中可以實現所有復雜的邏輯,但是要執行速度很快,不要阻塞;
需要注意的是,這個指令需要加入模塊ngx_devel_kit,否則不支持這個指令。
這個指令的Lua代碼中不支持以下API:
1、輸出(ngx.say、ngx.send_headers……)
2、控制(ngx.exit……)
3、子請求(ngx.location.capture、ngx.location.capture_multi……)
4、cosocket(ngx.socket.tcp、ngx.req.socket……)
5、ngx.sleep
例子:
server { listen 9393; default_type "text/html"; location /add { set $diff ; set $double_c ; set_by_lua $sum local a = ngx.var.arg_a local b = ngx.var.arg_b ngx.var.diff = a - b ngx.var.double_c = 2 * tonumber(ngx.arg[1]) return a + b; $arg_c; return 200 "a + b = $sum, a - b = $diff, 2 * c = $double_c"; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9393/add?a=11&b=22&c=88" a + b = 33, a - b = -11, 2 * c = 176 root@j9 ~#
rewrite_by_lua
執行內部URL重寫或者外部重定向(301或者302),典型的如偽靜態化的URL重寫。其默認執行在rewrite處理階段的最后。
需要注意的是:
1、在長連接中如果調用了ngx.exit(200)一個請求,則需要調用ngx.req.discard_body(),否則nginx可能會把當前請求的body當成header解析,從而導致400錯誤返回碼并且長連接被關閉。
2、如果該階段調用了ngx.exit(ngx.OK),content_by_lua階段仍然能得到執行。
例子:
server { listen 9494; default_type "text/html"; location /rewrite_by_lua { set $a 11; rewrite_by_lua ngx.var.a = "aa" if ngx.var.arg_exit == "ok" then ngx.exit(ngx.OK) else ngx.exit(200) end ; content_by_lua ngx.say("a: ", ngx.var.a) ; } }
測試結果
root@j9 ~# curl "http://127.0.0.1:9494/rewrite_by_lua?exit=ok" a: aa root@j9 ~# curl "http://127.0.0.1:9494/rewrite_by_lua?exit=200" root@j9 ~# access_by_lua
用于訪問控制,比如IP黑白名單限制、鑒權。
例子:
server { listen 9595; default_type "text/html"; location / { access_by_lua local auth = ngx.var.arg_auth; local key = "alicdnj9"; if ngx.md5(key) ~= auth then return ngx.exit(403) end ; content_by_lua ngx.say("access ok") ; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:9595/?auth=xx"403 Forbidden 403 Forbidden
You dont have permission to access the URL on this server. Sorry for the inconvenience.
Please report this message and include the following information to us.
Thank you very much!
URL: | http://127.0.0.1:9595/?auth=xx |
Server: | j9 |
Date: | 2015/10/27 16:47:20 |
注意,如果在access_by_lua中調用ngx.exit(ngx.OK),content階段仍然能得到執行。
content_by_lua
content階段,注意在同一個Location中不要和其他content階段指令一起使用,比如proxy_pass。
例子:略
header_filter_by_lua和body_filter_by_lua
分別為header_filter階段和body_filter階段,其中body_filter可能會被執行多次。
不支持以下API:
輸出 (ngx.say、ngx.send_headers)
控制 (ngx.exit、ngx.exec)
子請求 (ngx.location.capture、ngx.location.capture_multi)
Cosocket (ngx.socket.tcp、ngx.req.socket).
比如對后端chunked長度做限制:
server { listen 9696; default_type "text/html"; set $content_len 0; location / { header_filter_by_lua -- 先去掉Content-Length頭部,轉成Chunked傳輸 ngx.header.content_length = nil ; body_filter_by_lua local content_length = #ngx.arg[1] content_length = ngx.var.content_len + content_length ngx.var.content_len = content_length -- 最多只能傳輸10字節的body,否則直接關掉連接 if content_length > 10 then return ngx.ERROR end ; content_by_lua for i=1, ngx.var.arg_len do ngx.print("a") end ; } }
測試結果
root@j9 ~# curl "http://127.0.0.1:9696/?len=10" -i HTTP/1.1 200 OK Server: Tengine/2.2.0 Date: Mon, 26 Oct 2015 01:48:23 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive aaaaaaaaaa root@j9 ~# curl "http://127.0.0.1:9696/?len=11" -i curl: (52) Empty reply from server root@j9 ~#
可以看出當參數len為11時,服務器就直接不返回數據了。
4.3、深入
1、content_by_lua中的代碼一定要注意單引號或者雙引號,一般用法是外單內雙,或者外雙內單。
2、在nginx_lua中值為nil的變量不能與字符串或者數字相加,否則nginx會報500錯誤。
3、lua調試: ngx.log(ngx.ERR,xx)。(tail -f logs/error.log)
4、*_by_lua_file指令指定的文件支持絕對路徑和相對路徑,其中相對路徑是相對nginx工作目錄。
5、lua文件的require函數指定的lua模塊路徑查找順序,可以從出錯信息中看出來:
no file /opt/libs/lua/a.lua no file ./a.lua no file /usr/local/luajit/share/luajit-2.0.4/a.lua no file /usr/local/share/lua/5.1/a.lua no file /usr/local/share/lua/5.1/a/init.lua no file /usr/local/luajit/share/lua/5.1/a.lua no file /usr/local/luajit/share/lua/5.1/a/init.lua no file ./a.so no file /usr/local/lib/lua/5.1/a.so no file /usr/local/luajit/lib/lua/5.1/a.so no file /usr/local/lib/lua/5.1/loadall.so
其中,第一個/opt/libs/lua/a.lua為lua_package_path指定的路徑:lua_package_path /opt/libs/lua/?.lua;;;
第二個./a.lua為相對路徑,相對于nginx.conf配置文件,而非包含它的lua文件。
so模塊查找順序類似,但是先查找.lua再查找.so,查找.so時先在lua_package_cpah指定的路徑查找:lua_package_cpath /opt/libs/lua_shared/?.so;;;
可以從出錯信息中看出來:
no field package.preload[a] no file /opt/libs/lua/a.lua no file ./a.lua no file /usr/local/luajit/share/luajit-2.0.4/a.lua no file /usr/local/share/lua/5.1/a.lua no file /usr/local/share/lua/5.1/a/init.lua no file /usr/local/luajit/share/lua/5.1/a.lua no file /usr/local/luajit/share/lua/5.1/a/init.lua no file /opt/libs/lua_shared/a.so no file ./a.so no file /usr/local/lib/lua/5.1/a.so no file /usr/local/luajit/lib/lua/5.1/a.so no file /usr/local/lib/lua/5.1/loadall.so
6、lua代碼一定要健壯,否則不管lua產生什么錯,nginx都會返回500錯誤,這時可以從error.log中查看錯誤信息來定位。
7、編寫lua代碼時最好用local局部變量,不要用全局變量。
8、實現worker級別的全局變量:
server { listen 9797; default_type "text/html"; location / { content_by_lua local a = 1 local b = {b = 1} local status = require("status") ngx.say("a: ", a, ", b: ", b.b, " counter: ", status.counter) a = a + 1 b.b = b.b + 1 status.counter = (status.counter or 0) + 1 ; } }
其中status.lua為:
local m = {} m.counter = 1 return m
測試結果:
root@j9 ~# curl "http://127.0.0.1:9797/" a: 1, b: 1 counter: 1 root@j9 ~# curl "http://127.0.0.1:9797/" a: 1, b: 1 counter: 2 root@j9 ~# curl "http://127.0.0.1:9797/" a: 1, b: 1 counter: 3 root@j9 ~#
可以看出status.counter的值一直是累加的,這是因為require一個模塊只load第一次,后續require該模塊都會先看全局表中是否已經load過,load過則就不需要再load了,所以status.counter累加其實是累加m.counter。
9、定時任務
API: ok, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)
例子:
local delay = 5 local handler handler = function (premature) -- do some routine job in Lua just like a cron job if premature then return end local ok, err = ngx.timer.at(delay, handler) if not ok then ngx.log(ngx.ERR, "failed to create the timer: ", err) return end end local ok, err = ngx.timer.at(delay, handler) if not ok then ngx.log(ngx.ERR, "failed to create the timer: ", err) return end
注意:在timer處理函數的上下文中不能調用ngx.var.、ngx.req.、子請求API、輸出API,因為這些API只能在請求上下文中生效。
10、子請求
API:res = ngx.location.capture(uri, options?)
上下文:rewrite_by_lua, access_by_lua, content_by_lua*
例子:
正向代理,當源站返回301或者302時代理客戶端跳轉
server { listen 8181; default_type "text/html"; location /test { content_by_lua local res = ngx.location.capture("/get" .. ngx.var.request_uri, { method = ngx.HTTP_HEAD }) if res.status == 200 then ngx.exec("/get" .. ngx.var.request_uri) elseif res.status == 301 or res.status == 302 then location = res.header["Location"] local m, err = ngx.re.match(location, "http://([^/]+)(/.*)") if not m then ngx.exit(500) end host = m[1] uri = m[2] ngx.exec("/redirect/" .. host .. "/" .. ngx.var.request_uri) else ngx.exit(res.status) end ; } location ~ /redirect/([^/]*)/([^/]*) { proxy_pass http://$1/$2?$args; } location /get { if ($arg_tag = "1") { return 302 "http://127.0.0.1:8282/$request_uri"; } return 200 "ok"; } } server { listen 8282; default_type "text/html"; location / { return 200 "redirect ok, args: $args"; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:8181/test?tag=0" -i HTTP/1.1 200 OK Server: Tengine/2.2.0 Date: Mon, 26 Oct 2015 15:17:10 GMT Content-Type: text/html Content-Length: 2 Connection: keep-alive ok root@j9 ~# curl "http://127.0.0.1:8181/test?tag=1" -i HTTP/1.1 200 OK Server: Tengine/2.2.0 Date: Mon, 26 Oct 2015 15:17:14 GMT Content-Type: text/html Content-Length: 19 Connection: keep-alive redirect ok, args: tag=1 root@j9 ~#
可見,當傳tag為1時,返回的值就是想要的值,不需要再302重定向了。
注意,子請求只能請求本server的非@location。
另外一個需要注意的是,發起子請求之前修改的變量在子請求的location中是獲取不到的,這是因為變量的上下文是在請求結構體r中,而子請求是掛在主請求下面,是兩個不同的請求。
實驗:
server { listen 8383; default_type "text/html"; set $cc "cc"; location /test { content_by_lua ngx.var.cc = "11" local res = ngx.location.capture("/get" .. ngx.var.request_uri) if res.status == 200 then ngx.say(res.body) ngx.say("test cc: ", ngx.var.cc) else ngx.exit(res.status) end ; } location /get { content_by_lua ngx.say("get cc: ", ngx.var.cc) ngx.var.cc = "22" ; } }
結果:
root@j9 ~# curl "http://127.0.0.1:8383/test" get cc: cc test cc: 11 root@j9 ~#
11、location @xx
server { listen 8484; default_type "text/html"; set $cc "2"; location / { content_by_lua ngx.var.cc = "5"; if ngx.var.arg_location == "at" then ngx.exec("@cc") else ngx.exec("/cc") end ; } location @cc { return 200 "this is @cc location, cc: $cc"; } location /cc { return 200 "this is /cc location, cc: $cc"; } }
測試結果:
root@j9 ~# curl "http://127.0.0.1:8484/?location=at" this is @cc location, cc: 5 root@j9 ~# curl "http://127.0.0.1:8484/" this is /cc location, cc: 2 root@j9 ~#
在ngx.exec跳轉之前已經把變量cc的值改成5了,但可以看出這兩種跳轉方式變量cc的值不一樣,這是因為ngx.exec跳轉到@cc這個location時,從location rewrite階段開始執行,而跳轉到/cc這個location時是從server rewrite階段開始執行,而set指令是在server塊,就是在這個階段得到執行的,所以$cc又被賦值成2了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/33785.html
摘要:例如設置響應狀態碼并退出注意,設置狀態碼僅在響應頭發送前有效,并且該函數調用之后該函數后面的將被忽略掉,因為已經了。 一、介紹 二、安裝 三、運行 四、開發 1. 介紹 Tengine:輕量級、高性能、高并發、配置化、模塊化、可擴展、可移植的Web和反向代理 服務器,Tengine是nginx超集,但做了很多優化,包含了很多比較有用的模塊,比如直接包含了lua、proc等很...
摘要:內容主要有四個方面趨勢基礎實踐調試。一趨勢這一章節主要介紹近幾年和未來的趨勢,包括兩大瀏覽器和對的態度,以及淘寶天貓和阿里云的實踐情況。完整性是指為了避免網絡中傳輸的數據被非法篡改,使用算法來保證消息的完整性。 摘要: 本文邀請阿里云CDN HTTPS技術專家金九,分享Tengine的一些HTTPS實踐經驗。內容主要有四個方面:HTTPS趨勢、HTTPS基礎、HTTPS實踐、HTTPS...
摘要:是由淘寶網發起的服務器項目。回源監控是內容分發網絡的簡稱,其分發的內容來自用戶源站,負責回源的模塊是最重要組成部分之一,使跨越單機的限制,完成網絡數據的接收處理和轉發。這部分主要介紹的一些調試技巧和回源資源監控的內容,以及相應的實例分享。 摘要: Tengine是由淘寶網發起的Web服務器項目。它在Nginx的基礎上,針對大訪問量網站的需求,提供更強大的流量負載均衡能力、全站HTTPS...
閱讀 713·2023-04-25 19:43
閱讀 3910·2021-11-30 14:52
閱讀 3784·2021-11-30 14:52
閱讀 3852·2021-11-29 11:00
閱讀 3783·2021-11-29 11:00
閱讀 3869·2021-11-29 11:00
閱讀 3557·2021-11-29 11:00
閱讀 6105·2021-11-29 11:00