當前位置:
首頁 > 知識 > 聽阿里雲CDN安防技術專家金九講tengine+lua開發

聽阿里雲CDN安防技術專家金九講tengine+lua開發

摘要: 系統介紹tengine的安裝、運行和開發,全文包含大量代碼示例,由阿里雲CDN安防技術專家金九撰寫。


1. 介紹

Tengine:輕量級、高性能、高並發、配置化、模塊化、可擴展、可移植的Web和反向代理 伺服器,Tengine是nginx超集,但做了很多優化,包含了很多比較有用的模塊,比如直接包含了lua、proc等很有用的模塊。

Lua:一個很輕量級的 腳本,也號稱性能最高的 腳本。代碼總共不到600k,32個C文件,23個頭文件:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

可以非常容易的嵌入C和C++工程中,也比較容易與C和C++互動,這也是目前Lua主要的用法。

ngx_lua:一個nginx很重要的第三方模塊,作者:章亦春(agentzh、春哥),結合了nginx和Lua各自優點,把Lua嵌入nginx中,使其支持Lua來快速開發基於nginx下的業務邏輯。

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


2. 安裝

2.1、LuaJIT

聽阿里雲CDN安防技術專家金九講tengine+lua開發

2.2、Tengine

tengine最新代碼中已經包含lua模塊了,直接git clone下來就可以

聽阿里雲CDN安防技術專家金九講tengine+lua開發

如果是原生nginx的話,得自行下載lua模塊代碼:

聽阿里雲CDN安防技術專家金九講tengine+lua開發


3. 運行

修改/opt/tengine/conf/nginx.conf:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

運行tengine:

root@j9 ~/tengine# /opt/tengine/sbin/nginx

curl訪問一下hello_lua:

root@j9 ~/tengine# curl http://localhost/hello_luaLua: 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")

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

測試結果:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

  • ngx.var 讀取nginx變數,如nginx變數為$a,則在Lua中通過ngx.var.a獲取,也可以給nginx變數賦值如ngx.var.a = "aa",前提是該變數在nginx中必須存在,不能在Lua中創建nginx變數。另外,對於nginx location中使用正則捕獲的捕獲組可以使用ngx.var[捕獲組數字]獲取。

例子

聽阿里雲CDN安防技術專家金九講tengine+lua開發

  • 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數據

例子

聽阿里雲CDN安防技術專家金九講tengine+lua開發

測試結果:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

  • ngx.escape_uri/ngx.unescape_uri

    uri編碼解碼

  • ngx.encode_args/ngx.decode_args

    參數編碼解碼

  • ngx.encode_base64/ngx.decode_base64

    BASE64編碼解碼

  • ngx.md5

    md5加密

例子

聽阿里雲CDN安防技術專家金九講tengine+lua開發

  • ngx.shared.DICT

    共享內存介面,其中DICT為共享內存zone名稱,在nginx.conf中通過指令lua_shared_dict配置,而且lua_shared_dict指令配置的共享內存大小最小值為8k。

例子

聽阿里雲CDN安防技術專家金九講tengine+lua開發

ngx.shared.DICT詳細說明:http://wiki.nginx.org/HttpLuaModule#ngx.shared.DICT

4.2.2、指令

聽阿里雲CDN安防技術專家金九講tengine+lua開發

聽阿里雲CDN安防技術專家金九講tengine+lua開發

init_by_lua

每次nginx重新載入配置時執行,可以用它來完成一些耗時模塊的載入,或者初始化一些全局配置;

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

init_worker_by_lua

每個worker啟動之後初始化時執行,通常用於每個worker都要做的工作,比如啟動定時任務

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

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

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

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階段仍然能得到執行。

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

用於訪問控制,比如IP黑白名單限制、鑒權。

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

測試結果:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

注意,如果在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:

  1. 輸出 (ngx.say、ngx.send_headers)

  2. 控制 (ngx.exit、ngx.exec)

  3. 子請求 (ngx.location.capture、ngx.location.capture_multi)

  4. Cosocket (ngx.socket.tcp、ngx.req.socket).

比如對後端chunked長度做限制:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

測試結果

聽阿里雲CDN安防技術專家金九講tengine+lua開發

可以看出當參數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模塊路徑查找順序,可以從出錯信息中看出來:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

其中,第一個/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;;";

可以從出錯信息中看出來:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

6、lua代碼一定要健壯,否則不管lua產生什麼錯,nginx都會返回500錯誤,這時可以從error.log中查看錯誤信息來定位。

7、編寫lua代碼時最好用local局部變數,不要用全局變數。

8、實現worker級別的全局變數:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

可以看出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, ...)

例子:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

注意:在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時代理客戶端跳轉

聽阿里雲CDN安防技術專家金九講tengine+lua開發

聽阿里雲CDN安防技術專家金九講tengine+lua開發

聽阿里雲CDN安防技術專家金九講tengine+lua開發

可見,當傳tag為1時,返回的值就是想要的值,不需要再302重定向了。

注意,子請求只能請求本server的非@location。

另外一個需要注意的是,發起子請求之前修改的變數在子請求的location中是獲取不到的,這是因為變數的上下文是在請求結構體r中,而子請求是掛在主請求下面,是兩個不同的請求。

實驗:

聽阿里雲CDN安防技術專家金九講tengine+lua開發

11、location @xx

聽阿里雲CDN安防技術專家金九講tengine+lua開發

在ngx.exec跳轉之前已經把變數cc的值改成5了,但可以看出這兩種跳轉方式變數cc的值不一樣,這是因為ngx.exec跳轉到@cc這個location時,從location rewrite階段開始執行,而跳轉到/cc這個location時是從server rewrite階段開始執行,而set指令是在server塊,就是在這個階段得到執行的,所以$cc又被賦值成2了。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雲棲社區 的精彩文章:

局部連接來減參,權值共享肩並肩(深度學習入門系列之十一)
面向稀疏數據優化的輕量級神經網路庫Vectorflow
雲計算全球首推峰谷價,為企業節約49%成本
如何解決租房煩惱?阿里工程師寫了一套神奇的代碼
深度:主流分散式機器學習平台比較

TAG:雲棲社區 |

您可能感興趣

ButterflyNetwork發布世界上首個AR遠程醫療技術
淺談 Material Design iCourt技術
AMD 3D音頻技術TrueAudio Next中的CU Reservation是什麼
AMD 3D音頻技術TrueAudio Next中的CU Reservation是什麼?
英特爾Dawson Canyon NUC支持vPro技術
《中國科學:技術科學》英文專題:Digital Image Correlation
Content Aware ABR技術(七)
傑發科技購買Arteris IP的FlexNoC互連技術
Content Aware ABR技術(六)
iOS Airplay Screen Mirroring 同屏技術詳解
全球匯款公司Western Union測試Ripple技術
AI技術專利研究系列:Butterfly Network,醫療影像的明星公司
谷歌發布Welcome to Light Fields,帶領大家體驗光場技術
Valve空間音頻系統正式支持AMD TrueAudio Next技術
Inside Xbox 新聞回顧:Xbox系統迎來AMD FreeSync技術
Surgical Theater技術提供大腦VR視圖
Oculus專家將在Display Week上探討下一代VR/AR顯示技術
Valve改進Steam音頻 增加AMD的TrueAudio Next技術
【Inside Xbox】Xbox One春季更新全新功能公布 支持Freesync技術新Avatar系統測試中
「Inside Xbox」Xbox One春季更新全新功能公布 支持Freesync技術新Avatar系統測試中