摘要:例如響應部分通過的形式獲取或設置響應頭信息。以某個狀態(tài)碼返回響應內容,狀態(tài)碼常量對應關系見部分,也支持數(shù)字形式的狀態(tài)碼。重定向當前請求到新的,響應狀態(tài)碼可選列表為默認。具體內容如下相關文章進入的世界在的應用
首發(fā)于 樊浩柏科學院
當 Nginx 標準模塊和配置不能靈活地適應系統(tǒng)要求時,就可以考慮使用 Lua 擴展和定制 Nginx 服務。OpenResty 集成了大量精良的 Lua 庫、第三方模塊,可以方便地搭建能夠處理超高并發(fā)、擴展性極高的 Web 服務,所以這里選擇 OpenResty 提供的 lua-nginx-module 方案。
安裝Lua環(huán)境lua-nginx-module 依賴于 LuaJIT 和 ngx_devel_kit。LuaJIT 需要安裝,ngx_devel_kit 只需下載源碼包,在 Nginx 編譯時指定 ngx_devel_kit 目錄。
系統(tǒng)依賴庫首先確保系統(tǒng)已安裝如下依賴庫。
$ yum install readline-devel pcre-devel openssl-devel gcc安裝LuaJIT
首先,安裝 LuaJIT 環(huán)境,如下所示:
$ wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz $ tar zxvf LuaJIT-2.0.5.tar.gz $ cd LuaJIT-2.0.5 $ make install # 安裝成功 ==== Successfully installed LuaJIT 2.0.5 to /usr/local ====
設置 LuaJIT 有關的環(huán)境變量。
$ export LUAJIT_LIB=/usr/local/lib $ export LUAJIT_INC=/usr/local/include/luajit-2.0 $ echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf $ ldconfig下載相關模塊
下載 ngx_devel_kit 源碼包,如下:
$ wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz $ tar zxvf v0.3.0.tar.gz # 解壓縮后目錄名 ngx_devel_kit-0.3.0
接下來,下載 Lua 模塊 lua-nginx-module 源碼包,為 Nginx 編譯作準備。
$ wget https://github.com/openresty/lua-nginx-module/archive/v0.10.10.tar.gz $ tar zxvf v0.10.10.tar.gz # 解壓縮后目錄名 lua-nginx-module-0.10.10加載Lua模塊
Nginx 1.9 版本后可以動態(tài)加載模塊,但這里由于版本太低只能重新編譯安裝 Nginx。下載 Nginx 源碼包并解壓:
$ wget http://nginx.org/download/nginx-1.13.5.tar.gz $ tar zxvf nginx-1.13.5.tar.gz
編譯并重新安裝 Nginx:
$ cd nginx-1.13.5 # 增加--add-module=/usr/src/lua-nginx-module-0.10.10 --add-module=/usr/src/ngx_devel_kit-0.3.0 $ ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_v2_module --with-http_stub_status_module --with-pcre --add-module=/usr/src/lua-nginx-module-0.10.10 --add-module=/usr/src/ngx_devel_kit-0.3.0 $ make $ make install # 查看是否安裝成功 $ nginx -v配置Nginx環(huán)境
現(xiàn)在只需配置 Nginx,即可嵌入 Lua 腳本。首先,在 http 部分配置 Lua 模塊和第三方庫路徑:
# 第三方庫(cjson)地址luajit-2.0/lib lua_package_path "/home/www/lua/?.lua;;"; lua_package_cpath "/usr/local/include/luajit-2.0/lib/?.so;;";
接著,配置一個 Lua 腳本服務:
# hello world測試 server { location /lua_content { # 定義MIME類型 default_type "text/plain"; content_by_lua_block { ngx.say("Hello,world!") } } }
測試安裝和配置是否正常:
$ service nginx test $ service nginx reload # 訪問地址/lua_content輸出 Hello,world!Lua調用Nginx
lua-nginx-module 模塊中已經為 Lua 提供了豐富的 Nginx 調用 API,每個 API 都有各自的作用環(huán)境,詳細描述見 Nginx API for Lua。這里只列舉基本 API 的使用 。
先配一個 Lua 腳本服務,配置文件如下:
location?~?/lua_api?{?? ????# 示例用的Nginx變量?? ????set?$name?$host; ????default_type?"text/html";?? ????# 通過Lua文件進行內容處理 ????content_by_lua_file?/home/www/nginx-api.lua;?? }請求部分
ngx.var
可以通過ngx.var.var_name形式獲取或設置 Nginx 變量值,例如 request_uri、host、request 等。
-- ngx.say打印內容 ngx.say(ngx.var.request_uri) ngx.var.name = "www.fanhaobai.com"
ngx.req.get_headers()
該方法會以表的形式返回當前請求的頭信息。查看請求的頭信息:
ngx.say("Host : ", ngx.req.get_headers().host, "
") for k,v in pairs(ngx.req.get_headers()) do if type(v) == "table" then ngx.say(k, "?:?", table.concat(v, ","), "
") else ngx.say(k,"?:?", v, "
") end end
當然,通過 ngx.req.set_header() 也可以設置頭信息。
ngx.req.set_header("Content-Type", "text/html")
ngx.req.get_uri_args()
該方法以表形式返回當前請求的所有 GET 參數(shù)。查看請求 query 為?name=fhb的 GET 參數(shù):
ngx.say("name : ", ngx.req.get_uri_args().name, "
") for k,v in pairs(ngx.req.get_uri_args()) do if type(v) == "table" then ngx.say(k, "?:?", table.concat(v, ","), "
") else ngx.say(k,"?:?", v, "
") end end
同樣,可以通過 ngx.req.set_uri_args() 設置請求的所有 GET 參數(shù)。
ngx.req.set_uri_args({name="fhb"}) --{name="fhb"}可以為query形式name=fhb
get_post_args()
該方法以表形式返回當前請求的所有 POST 參數(shù),POST 數(shù)據(jù)必須是 application/x-www-form-urlencoded 類型。查看請求curl --data "name=fhb" localhost/lua_api的 POST 參數(shù):
--必須先讀取body體 ngx.req.read_body() ngx.say("name : ", ngx.req.get_post_args().name, "
") for k,v in pairs(ngx.req.get_post_args()) do if type(v) == "table" then ngx.say(k, "?:?", table.concat(v, ","), "
") else ngx.say(k,"?:?", v, "
") end end
通過 ngx.req.get_body_data() 方法可以獲取未解析的請求 body 體內容字符串。
ngx.req.get_method()
獲取請求的大寫字母形式的請求方式,通過 ngx.req.set_method() 可以設置請求方式。例如:
ngx.say(ngx.req.get_method())響應部分
ngx.header
通過ngx.header.header_name的形式獲取或設置響應頭信息。如下:
ngx.say(ngx.header.content_type) ngx.header.content_type = "text/plain"
ngx.print()
ngx.print() 方法會填充指定內容到響應 body 中。如下所示:
ngx.print(ngx.header.content_type)
ngx.say()
如上述使用,ngx.say() 方法同 ngx.print() 方法,只是會在后追加一個換行符。
ngx.exit()
以某個狀態(tài)碼返回響應內容,狀態(tài)碼常量對應關系見 HTTP status constants 部分,也支持數(shù)字形式的狀態(tài)碼。
ngx.exit(403)
ngx.redirect()
重定向當前請求到新的 url,響應狀態(tài)碼可選列表為 301、302(默認)、303、307。
ngx.redirect("http://www.fanhaobai.com")其他
ngx.re.match
該方法提供了正則表達式匹配方法。請求?name=fhb&age=24匹配 GET 參數(shù)中的數(shù)字:
local m, err = ngx.re.match(ngx.req.set_uri_args, "[0-9]+") if m then ngx.say(m[0]) else ngx.say("match not found") end
ngx.log()
通過該方法可以將內容寫入 Nginx 日志文件,日志文件級別需同 log 級別一致。
ngx.md5() | ngx.encode_base64() | ngx.decode_base64()
它們都是字符串編碼方式。ngx.md5() 可以對字符串進行 md5 加密處理,而 ngx.encode_base64() 是對字符串 base64 編碼, ngx.decode_base64() 為 base64 解碼。
Nginx中嵌入Lua上面講述了怎么在 Lua 中調用 Nginx 的 API 來擴展或定制 Nginx 的功能,那么編寫好的 Lua 腳本怎么在 Nginx 中得到執(zhí)行呢?其實,Nginx 是通過模塊指令形式在其 11 個處理階段做插入式處理,指令覆蓋 http、server、server if、location、location if 這幾個范圍。
模塊指令列表這里只列舉基本的 Lua 模塊指令,更多信息參考 Directives 部分。
指令 | 所在階段 | 使用范圍 | 說明 |
---|---|---|---|
init_by_lua init_by_lua_file |
加載配置文件 | http | 可以用于初始化全局配置 |
set_by_lua set_by_lua_file |
rewrite | server location location if |
復雜邏輯的變量賦值,注意是阻塞的 |
rewrite_by_lua rewrite_by_lua_file |
rewrite | http server location location if |
實現(xiàn)復雜邏輯的轉發(fā)或重定向 |
content_by_lua content_by_lua_file |
content | location location if |
處理請求并輸出響應 |
header_filter_by_lua header_filter_by_lua_file |
響應頭信息過濾 | http server location location if |
設置響應頭信息 |
body_filter_by_lua body_filter_by_lua_file |
輸出過濾 | http server location location if |
對輸出進行過濾或修改 |
注意到,每個指令都會有*_lua和*_lua_file兩個指令,*_lua指令后為 Lua 代碼塊,而*_lua_file指令后為 Lua 腳本文件路徑。下面將只對*_lua指令進行說明。
init_by_lua
該指令會在 Nginx 的 Master 進程加載配置時執(zhí)行,所以可以完成 Lua 模塊初始化工作,Worker 進程同樣會繼承這些。
nginx.conf配置文件中的 http 部分添加如下代碼:
-- 所有worker共享的全局變量 lua_shared_dict?shared_data?1m;?? init_by_lua_file?/usr/example/lua/init.lua;
init.lua初始化腳本為:
local?cjson?=?require?"cjson" local?redis?=?require?"resty.redis" local?shared_data?=?ngx.shared.shared_data
set_by_lua
我們直接使用 set 指令很難實現(xiàn)很復雜的變量賦值邏輯,而 set_by_lua 模塊指令就可以解決這個問題。
nginx.conf配置文件 location 部分內容為:
location /lua { set_by_lua_file $num /home/www/set.lua; default_type "text/html"; echo $num; }
set.lua腳本內容為:
local?uri_args?=?ngx.req.get_uri_args() local?i?=?uri_args.a?or?0 local?j?=?uri_args.b?or?0 return?i?+?j
上述賦值邏輯,請求 query 為?a=10&b=2時響應內容為 12。
rewrite_by_lua
可以實現(xiàn)內部 URL 重寫或者外部重定向。nginx.conf配置如下:
location /lua { default_type?"text/html"; rewrite_by_lua_file?/home/www/rewrite.lua; }
rewrite.lua腳本內容:
if?ngx.req.get_uri_args()["type"]?==?"app"?then ngx.req.set_uri("/m_h5",?false); end
access_by_lua
用于訪問權限控制。例如,只允許帶有身份標識用戶訪問,nginx.conf配置為:
location?/lua?{?? default_type?"text/html"; access_by_lua_file?/home/www/access.lua; }??
access.lua腳本內容為:
if?ngx.req.get_uri_args()["token"]?==?"fanhb"?then return?ngx.exit(403) end
content_by_lua
該指令在 Lua 調用 Nginx 部分已經使用過了,用于輸出響應內容。
案例 訪問權限控制使用 Lua 模塊對本站的 ES 服務做受信操作控制,即非受信 IP 只能查詢操作。nginx.conf配置如下:
location / { set $allowed "115.171.226.212"; access_by_lua_block { if ngx.re.match(ngx.req.get_method(), "PUT|POST|DELETE") and not ngx.re.match(ngx.var.request_uri, "_search") then start, _ = string.find(ngx.var.allowed, ngx.var.remote_addr) if not start then ngx.exit(403) end end } proxy_pass http://127.0.0.1:9200$request_uri; }訪問頻率控制
在 Nginx 配置文件的 location 部分配置 Lua 腳本基本參數(shù),并配置 Lua 模塊指令:
default_type?"text/html"; set rate_per 300 access_by_lua_file?/home/www/access.lua;
Lua 腳本實現(xiàn)頻率控制邏輯,使用 Redis 對單位時間內的訪問次數(shù)做緩存,key 為訪問 uri 拼接 token 后的 md5 值。具體內容如下:
local redis = require "resty.redis" local red = redis:new() local limit = tonumber(ngx.var.rate_per) or 200 local expire_time = tonumber(ngx.var.rate_expire) or 1000 local key = "rate.limit:string:" red:set_timeout(500) local ok, err = red:connect("www.fanhaobai.com", 6379) if not ok then ngx.log(ngx.ERR, "failed to connect redis: " .. err) return end key = key .. ngx.md5(ngx.var.request_uri .. (ngx.req.get_uri_args()["token"] or ngx.req.get_post_args()["token"])) local times, err = red:incr(key) if not times then ngx.log(ngx.ERR, "failed to exec incr: " .. err) return elseif times == 1 then ok, err = red:expire(key, expire_time) if not ok then ngx.log(ngx.ERR, "failed to exec expire: " .. err) return end end if times > limit then return ngx.exit(403) end return
相關文章 ?
進入Lua的世界(2017-09-03)
Lua在Redis的應用(2017-09-04)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/40404.html
摘要:入門是為和編寫的框架。使用來安裝創(chuàng)建一個應用命令行工具附帶了一個命令行工具,可幫助您創(chuàng)建新項目和啟動服務器。在生產環(huán)境中,應當啟用緩存以獲得最佳性能。指令指定一個代碼塊,它將處理與其他不匹配的任何請求。將忽略常規(guī)的二進制文件。 lapis入門 Lapis 是為 Lua 和 MoonScript 編寫的 Web 框架。 Lapis 很有趣,因為它建立在Nginx 發(fā)行的 OpenRest...
摘要:目前最常用的軟件反向代理服務器有和?;趯崿F(xiàn)動態(tài)代理為了實現(xiàn)動態(tài)代理方案,需要在反向代理服務器中增加定制的功能。同時,由于反向代理服務器需要處理大量的代理請求,因此會頻繁的讀取反向代理配置數(shù)據(jù)。 基于 Nginx 的動態(tài)代理 作者:趙波日期:2016 年 8 月 4 日 在實際應用中,遇到了這樣一個場景: 已有一個手機 APP 客戶端,需要在該 APP 客戶端中實現(xiàn)通過 Web 的形式...
摘要:用于方便地搭建能夠處理超高并發(fā)擴展性極高的動態(tài)應用服務和動態(tài)網關。安裝安裝依賴庫下載及安裝激活組件被用于構建。大部組件默認是激活的,也有部件不是。您需要通過以下選項在編譯的時候將它們各自激活,和。 OpenResty簡介 OpenResty 是一個基于 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數(shù)的依賴項。用于方便地搭建能夠處...
閱讀 2633·2021-11-19 09:59
閱讀 2124·2019-08-30 15:55
閱讀 1008·2019-08-29 13:30
閱讀 1405·2019-08-26 10:18
閱讀 3150·2019-08-23 18:36
閱讀 2450·2019-08-23 18:25
閱讀 1230·2019-08-23 18:07
閱讀 499·2019-08-23 17:15