本文不是關於新版 OpenResty 如何支持 ARM64 的,而是關於如何應對這一過程引入的 break change。服務器
另外,若是你沒用 OpenResty 本身的 LuaJIT 分支,那麼能夠直接關掉這個頁面了,由於這些 break change 只有在使用了 OpenResty 本身的 LuaJIT 分支纔會出現。架構
一切的根源在於,新版本的 OpenResty 把當前請求的 ngx_http_request_t
放到了 luaState
的 exdata
屬性裏面,再也不使用 getfenv(0).__ngx_req
這種方式了。exdata
是 OpenResty 本身的 LuaJIT 分支加的屬性,因此若是不用 OpenResty 本身的 LuaJIT 分支,依舊仍是得走 getfenv(0).__ngx_req
這種方式。一樣被移除的還有 getfenv(0).__ngx_cycle
和給每一個 main thread 準備的全局環境表。接下來咱們談談如何應對這幾個變化。函數
新版 OpenResty 使用 resty.core.base
裏面的 get_request()
代替了 getfenv(0).__ngx_req
。在你的代碼裏,能夠這麼寫:性能
local base = require "resty.core.base" local get_request = base.get_request if not get_request then get_request = function() return getfenv(0).__ngx_req end end ... local r = get_request()
可是! get_request()
並不是 100% 兼容 getfenv(0).__ngx_req
。前者返回的是一個 cdata,然後者返回的是一個 lightuserdata。cdata 和 lightuserdata 在語義上有些微妙的不一樣。當你用 lightuserdata 做爲 table 的 hash key 時,若是 lightuserdata 指向的地址相同,那麼 hash 值會相同。但若是是 cdata,即便是指向同一地址的指針類型的 cdata,因爲計算 hash 時用的是 cdata 的地址,而非其內部的值,因此不一樣的 cdata 的 hash 值會不同。舉個例子:優化
local r = get_request() local h = {} h[r] = 1 ngx.say(h[get_request()])
在以前的版本里,兩次 get_request()
會返回同一個地址(都是同一個請求嘛),因此會輸出 1。而新的 OpenResty 裏,你會發現輸出結果是 nil。這是由於兩次 get_request()
會創造兩個 cdata 對象,這兩個對象雖然值同樣,可是內存地址不同,因此 hash 值不同。ui
那怎麼解決呢?咱們能夠實現一個轉換函數,把 cdata 的值變成某種可用做 hash key 的類型。一個簡單的解決方法是加上 tostring。tostring(cdata)
的輸出中會包含 cdata 指向的地址,這樣同一個請求對應的 key 就會相同。lua
考慮到 LuaJIT 建立字符串的開銷比較大,做爲一種優化手段,在某些架構下咱們能夠用 tonumber(ffi_cast("intptr_t", cdata))
代替。之因此限定在某些架構,是由於 LuaJIT 的 Number 實際上是 double,而不是 int64,因此對於某些 64 位的架構,不必定能獲得正確的輸出。好在 x64 的用戶態空間地址不會超過 48 位,因此咱們能夠在主流的 x64 服務器上採用該優化。固然前提是你沒有啓用 5 級頁表。考慮到只有數百 TB 內存的機器纔會有開啓 5 級頁表的須要,大致上你能夠放心地認爲你的 x64 環境不會遇到這樣的問題。即便開啓了 5 級頁表,現階段 48 位以上的內存地址也不是默承認用的。關於 5 級頁表的更多上下文,能夠看下這兩個連接:.net
https://lwn.net/Articles/717293/
https://www.kernel.org/doc/Do...指針
有些 Lua 代碼會經過 getfenv(0).__ngx_cycle
獲取 ngx_cycle
, 而後經過 FFI 調用傳給 C 函數。其實直接在 C 函數裏面訪問 ngx_cycle
就能夠了,不須要通過 Lua 這一層。rest
你可能會問,reload 的時候,init
階段下 ngx_cylce
應該會指向舊的 ngx_cycle
吧?這裏 OpenResty 作了點手腳。它會把舊的 ngx_cycle
放到 saved_ngx_cycle
裏面來,讓 ngx_cycle
指向新構建的 ngx_cycle_t *cycle
。因此並不須要特殊的對待。
爲了放下 getfenv(0).__ngx_req
,過去的 OpenResty 須要給每一個 main thread 準備獨立的全局環境表,這樣每一個請求的 getfenv(0)
纔會返回不一樣的 table。既然新的 OpenResty 已經不須要 getfenv(0).__ngx_req
,這些全局環境表就能幹掉了。
不過讓它們下崗,還有點反作用。過去在 rewrite
/access
/content
等階段裏定義了全局變量(一般是手誤引入的),不會污染到其餘的請求。那是由於 OpenResty 設置了全局環境表,這些全局變量只會影響到它們所在的全局環境表。可是移除了全局環境表的保護後,這些全局變量就能肆無忌憚地跑來跑去。爲此新版 OpenResty 加了個 guard,若是在這些階段裏遇到全局變量的定義,會打印這樣的錯誤信息:
2019/04/30 11:01:18 [warn] 26843#26843: *240 [lua] _G write guard:12: __newindex(): writing a global lua variable ('xxx') which may lead to race conditions between concurrent requests, so prefer the use of 'local' variables stack traceback: ...
這種錯誤信息對程序的流程沒有影響,但對性能有影響。解決辦法?把全局變量一個個都揪出來解決掉。
固然若是你是在 init
或 init_worker
階段定義全局變量,並不會觸發這個 guard。畢竟這麼作的人通常是故意的,在過去的 OpenResty 裏這也是標準的「使用」全局變量的方式。雖然我我的不推薦這麼作。用全局變量,早晚都要還的。