摘要:于是才有了對,或者說的一次過程。另外,這類基礎的操作,不適合存放在業務態,由調用者自己控制,因為這兩個函數必須成對調用,否則就會造成內存泄漏。使用之后,強烈建議進行壓測,確認沒有內存泄漏的隱患。
緣起
ngx.ctx 是 lua-nginx-module 提供的一個充滿魔力的 Lua table,它可以存放任何我們想要存放的內容,生命周期貫穿整個 location,也正因為生命周期局限在單個 location 里,所以當發生內部跳轉(例如通過 ngx.exec)之后,之前的 ngx.ctx
將被銷毀。所以很多時候,我們不得不轉而使用 ngx.var.VARIABLE 來替代 ngx.ctx,例如我們需要在 log 階段的時候收集之前準備好的字段,然后發送到日志服務器或者 nsq 等組件。
然而,事物總是具有兩面性,`ngx.var.VARIABLE` 生命周期雖然貫穿于一個請求,但是其代價卻更加昂貴,它具有計算 `hash` 值,查找 `hash` 表,分配內存等等操作,這相比于 `ngx.ctx` 實在是繁重得多了。通過觀察火焰圖,大量的使用 `ngx.var.VARIABLE` 已經成為了一個瓶頸。于是才有了對 `ngx.ctx`,或者說 `ngx.exec` 的一次 hack 過程。ngx.ctx
既然要對 ngx.ctx 進行 hack,首先需要了解 ngx.ctx 的機制,事實上,ngx.ctx 就是一個普通的 Lua table,lua-nginx-module 創建一個 table 之后,將其存放在 Lua 的注冊表里,利用 luaL_ref 來索引每個 ngx.ctx,利用 luaL_unref 來解除索引。這個索引,是被存放在 lua-nginx-module 的模塊上下文里的,也就是 ngx_http_lua_ctx_s::ctx_ref 這個成員變量。
為什么經過內部跳轉,ngx.ctx 會被銷毀
Nginx 核心在進行內部跳轉的時候,會把對應請求所有的模塊上下文全部清除,可以參考函數 ngx_http_internal_redirect,所以 lua-nginx-module 的 ctx_ref 也會被銷毀。在 lua-nginx-module 關于 ngx.exec 的源碼里也可以看到對 ngx.ctx 的解索引過程。
Hack it了解了它的機制之后,我們可以試著來繞過這種限制,既然 lua-nginx-module 利用一個數字來索引 ngx.ctx,我們也可以主動創建一個索引,將它存在一個介質里,只要這個介質不隨著內部跳轉而消失即可(例如 Nginx 變量就是一個非常好的選擇),等到內部跳轉完成之后,第一時間將 ngx.ctx 恢復出來即可,下面來介紹下這個過程。
首先我們需要一個變量
set ctx_ref "";
設計一個函數,創建一個新的索引
function _M.stash_ngx_ctx() local ctxs = registry.ngx_lua_ctx_tables local ctx_ref = base.ref_in_table(ctxs, ngx.ctx) ngx.var.ctx_ref = tostring(ctx_ref) end
registry 就是 Lua 的注冊表,通過下面的方法獲得。
local debug = require "debug" local registry = debug.getregistry()
所有請求的 ngx.ctx 放置在一張表里,這張表存放在注冊表里,key 就是 "ngx_http_lua_ctx_tables",所以上述代碼里的 ctxs 就是存放所有請求的 ngx.ctx 的那張表了。
local ctx_ref = base.ref_in_table(ctxs, ngx.ctx)
這行代碼給 ngx.ctx 創建了一個新的索引,關于具體的細節,大家有興趣可以查看 lua-resty-core 的 base.ref_in_table,這個函數的原理和 luaL_ref 一致。
拿到索引之后,將它存放到我們的變量即可。至此,當前請求的 ngx.ctx 就存在 2 個索引了(一個索引由 lua-nginx-module 管理,另外一個則由我們自己管理)。
執行完內部跳轉后,恢復跳轉前的 ngx.ctx
function _M.apply_ngx_ctx() local ctx_ref = tonumber(ngx.var.ctx_ref) if not ctx_ref then return end local ctxs = registry.ngx_lua_ctx_tables local origin_ngx_ctx = ctxs[ctx_ref] ngx.ctx = origin_ngx_ctx local FREE_LIST_REF = 0 ctxs[ctx_ref] = ctxs[FREE_LIST_REF] ctxs[FREE_LIST_REF] = ctx_ref ngx.var.ctx_ref = "" end
我們通過存放在變量的 ctx_ref 來得到執行內部跳轉前的 ngx.ctx 表,接著需要把我們自己管理的這個索引解除,否則會造成嚴重的內存泄漏!
local FREE_LIST_REF = 0 ctxs[ctx_ref] = ctxs[FREE_LIST_REF] ctxs[FREE_LIST_REF] = ctx_ref
這三行代碼即完成了解索引(和 LuaL_unref 一直),這里簡單解釋下, LuaL_unref 管理索引的時候,用 0 這個 index 記錄上一次解索引的 index(為 nil 則表示目前還沒有過解索引的操作),所以上述兩行代碼,實際上就是在當前需要解索引的 index 處記錄了上一次解索引的 index,然后在 0 下標處記錄當前最新的 index,有點像鏈表。這樣操作有什么好處呢?當下次需要產生索引的時候,可以首先檢查 0 下標,看看是否有解過索引的位置,如果有,復用即可,否則需要返回 #table + 1,所以利用這個 “鏈表”,可以避免很多 Lua table 擴大,導致內存拷貝,影響到性能。
后續這兩個函數的代碼已經經過充分測試,目前已經運行在我們的一個項目當中。
另外,這類基礎的 Hack 操作,不適合存放在業務態,由調用者自己控制,因為這兩個函數必須成對調用,否則就會造成內存泄漏。
使用之后,強烈建議進行壓測,確認沒有內存泄漏的隱患。
如果你有更多的 idea,可以給我發送郵件(zchao1995@gmail.com)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/40543.html
摘要:內核代表進程來執行信號處理器函數,當處理器返回時,主程序會在處理器被中斷的位置恢復執行。進程信號掩碼內核會為每個進程維護一個信號掩碼。這個競態條件發生在主程序和信號處理器對同一個被解除信號的競爭關系。 運營研發團隊 季偉濱 一、前言 眾所周如,Nginx是多進程架構。有1個master進程和N個worker進程,一般N等于cpu的核數。另外, 和文件緩存相關,還有cache mana...
摘要:結構體數組,用來表示該模塊可以在配置文件中配置的項目,及其操作指令。 源文件路徑 srccore gx_conf_file.h srccore gx_conf_file.c 主要內容 本篇的主要目的在于分析Nginx的配置功能。由于Nginx的配置基本就是對模塊的配置,因此,在討論配置功能之前,需要先分析Nginx的模塊功能。 對于模塊功能,這里的重點不在于某個模塊的細節,而...
摘要:限流算法最簡單粗暴的限流算法就是計數器法了,而比較常用的有漏桶算法和令牌桶算法計數器計數器法是限流算法里最簡單也是最容易實現的一種算法。 運營研發團隊 李樂 高并發系統有三把利器:緩存、降級和限流; 限流的目的是通過對并發訪問/請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務(定向到錯誤頁)、排隊等待(秒殺)、降級(返回兜底數據或默認數據); 高并發系統常見的限流有:限制總并發...
摘要:如果當前需要延遲處理,又會把請求放到定時器中,等到定時器過期以后,執行寫事件回調,這個函數里會執行,重新進行的個階段。 ngx_http_limit_req_module 是 Nginx 官方提供的一個 http 模塊,它工作在 NGX_HTTP_PREACCESS_PHASE 階段,通過在 nginx.conf 中進行簡單地配置,我們可以輕易地對請求速率進行限制。 配置指令 官方文檔...
摘要:找到這個模塊的指令后,則會調用這個指令的解析回調函數即結構體的第三個參數來進行處理。調用他們上面提到的中的回調函數來申請和初始化對應模塊的配置結構體。需要注意的是,即時當前是直接在塊級別,這三個回調函數都會被調用。拒絕暴力枚舉式編寫配置文件 原博:https://blog.coordinate35.cn/... 熱身 首先來看下這幾個小例子: 第一個例子: server { l...
閱讀 2180·2021-11-24 09:39
閱讀 2797·2021-07-29 13:49
閱讀 2327·2019-08-29 14:15
閱讀 2241·2019-08-29 12:40
閱讀 3320·2019-08-26 13:42
閱讀 640·2019-08-26 12:13
閱讀 2075·2019-08-26 11:41
閱讀 3354·2019-08-23 18:32