亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

對 ngx.ctx 的一次 hack

qc1iu / 2994人閱讀

摘要:于是才有了對,或者說的一次過程。另外,這類基礎(chǔ)的操作,不適合存放在業(yè)務(wù)態(tài),由調(diào)用者自己控制,因?yàn)檫@兩個(gè)函數(shù)必須成對調(diào)用,否則就會造成內(nèi)存泄漏。使用之后,強(qiáng)烈建議進(jìn)行壓測,確認(rèn)沒有內(nèi)存泄漏的隱患。

緣起

ngx.ctxlua-nginx-module 提供的一個(gè)充滿魔力的 Lua table,它可以存放任何我們想要存放的內(nèi)容,生命周期貫穿整個(gè) location,也正因?yàn)樯芷诰窒拊趩蝹€(gè) location 里,所以當(dāng)發(fā)生內(nèi)部跳轉(zhuǎn)(例如通過 ngx.exec)之后,之前的 ngx.ctx
將被銷毀。所以很多時(shí)候,我們不得不轉(zhuǎn)而使用 ngx.var.VARIABLE 來替代 ngx.ctx,例如我們需要在 log 階段的時(shí)候收集之前準(zhǔn)備好的字段,然后發(fā)送到日志服務(wù)器或者 nsq 等組件。

然而,事物總是具有兩面性,`ngx.var.VARIABLE` 生命周期雖然貫穿于一個(gè)請求,但是其代價(jià)卻更加昂貴,它具有計(jì)算 `hash` 值,查找 `hash` 表,分配內(nèi)存等等操作,這相比于 `ngx.ctx` 實(shí)在是繁重得多了。通過觀察火焰圖,大量的使用 `ngx.var.VARIABLE` 已經(jīng)成為了一個(gè)瓶頸。于是才有了對 `ngx.ctx`,或者說 `ngx.exec` 的一次 hack 過程。

ngx.ctx

既然要對 ngx.ctx 進(jìn)行 hack,首先需要了解 ngx.ctx 的機(jī)制,事實(shí)上,ngx.ctx 就是一個(gè)普通的 Lua table,lua-nginx-module 創(chuàng)建一個(gè) table 之后,將其存放在 Lua 的注冊表里,利用 luaL_ref 來索引每個(gè) ngx.ctx,利用 luaL_unref 來解除索引。這個(gè)索引,是被存放在 lua-nginx-module 的模塊上下文里的,也就是 ngx_http_lua_ctx_s::ctx_ref 這個(gè)成員變量。

為什么經(jīng)過內(nèi)部跳轉(zhuǎn),ngx.ctx 會被銷毀

Nginx 核心在進(jìn)行內(nèi)部跳轉(zhuǎn)的時(shí)候,會把對應(yīng)請求所有的模塊上下文全部清除,可以參考函數(shù) ngx_http_internal_redirect,所以 lua-nginx-modulectx_ref 也會被銷毀。在 lua-nginx-module 關(guān)于 ngx.exec 的源碼里也可以看到對 ngx.ctx 的解索引過程。

Hack it

了解了它的機(jī)制之后,我們可以試著來繞過這種限制,既然 lua-nginx-module 利用一個(gè)數(shù)字來索引 ngx.ctx,我們也可以主動創(chuàng)建一個(gè)索引,將它存在一個(gè)介質(zhì)里,只要這個(gè)介質(zhì)不隨著內(nèi)部跳轉(zhuǎn)而消失即可(例如 Nginx 變量就是一個(gè)非常好的選擇),等到內(nèi)部跳轉(zhuǎn)完成之后,第一時(shí)間將 ngx.ctx 恢復(fù)出來即可,下面來介紹下這個(gè)過程。

首先我們需要一個(gè)變量

set ctx_ref "";

設(shè)計(jì)一個(gè)函數(shù),創(chuàng)建一個(gè)新的索引

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 創(chuàng)建了一個(gè)新的索引,關(guān)于具體的細(xì)節(jié),大家有興趣可以查看 lua-resty-corebase.ref_in_table,這個(gè)函數(shù)的原理和 luaL_ref 一致。

拿到索引之后,將它存放到我們的變量即可。至此,當(dāng)前請求的 ngx.ctx 就存在 2 個(gè)索引了(一個(gè)索引由 lua-nginx-module 管理,另外一個(gè)則由我們自己管理)。

執(zhí)行完內(nèi)部跳轉(zhuǎn)后,恢復(fù)跳轉(zhuǎn)前的 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 來得到執(zhí)行內(nèi)部跳轉(zhuǎn)前的 ngx.ctx 表,接著需要把我們自己管理的這個(gè)索引解除,否則會造成嚴(yán)重的內(nèi)存泄漏!

    local FREE_LIST_REF = 0
     ctxs[ctx_ref] = ctxs[FREE_LIST_REF]
     ctxs[FREE_LIST_REF] = ctx_ref

這三行代碼即完成了解索引(和 LuaL_unref 一直),這里簡單解釋下, LuaL_unref 管理索引的時(shí)候,用 0 這個(gè) index 記錄上一次解索引的 index(為 nil 則表示目前還沒有過解索引的操作),所以上述兩行代碼,實(shí)際上就是在當(dāng)前需要解索引的 index 處記錄了上一次解索引的 index,然后在 0 下標(biāo)處記錄當(dāng)前最新的 index,有點(diǎn)像鏈表。這樣操作有什么好處呢?當(dāng)下次需要產(chǎn)生索引的時(shí)候,可以首先檢查 0 下標(biāo),看看是否有解過索引的位置,如果有,復(fù)用即可,否則需要返回 #table + 1,所以利用這個(gè) “鏈表”,可以避免很多 Lua table 擴(kuò)大,導(dǎo)致內(nèi)存拷貝,影響到性能。

后續(xù)

這兩個(gè)函數(shù)的代碼已經(jīng)經(jīng)過充分測試,目前已經(jīng)運(yùn)行在我們的一個(gè)項(xiàng)目當(dāng)中。

另外,這類基礎(chǔ)的 Hack 操作,不適合存放在業(yè)務(wù)態(tài),由調(diào)用者自己控制,因?yàn)檫@兩個(gè)函數(shù)必須成對調(diào)用,否則就會造成內(nèi)存泄漏。

使用之后,強(qiáng)烈建議進(jìn)行壓測,確認(rèn)沒有內(nèi)存泄漏的隱患。

如果你有更多的 idea,可以給我發(fā)送郵件(zchao1995@gmail.com)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/40543.html

相關(guān)文章

  • 【Nginx源碼研究】Master進(jìn)程淺析

    摘要:內(nèi)核代表進(jìn)程來執(zhí)行信號處理器函數(shù),當(dāng)處理器返回時(shí),主程序會在處理器被中斷的位置恢復(fù)執(zhí)行。進(jìn)程信號掩碼內(nèi)核會為每個(gè)進(jìn)程維護(hù)一個(gè)信號掩碼。這個(gè)競態(tài)條件發(fā)生在主程序和信號處理器對同一個(gè)被解除信號的競爭關(guān)系。 運(yùn)營研發(fā)團(tuán)隊(duì) 季偉濱 一、前言 眾所周如,Nginx是多進(jìn)程架構(gòu)。有1個(gè)master進(jìn)程和N個(gè)worker進(jìn)程,一般N等于cpu的核數(shù)。另外, 和文件緩存相關(guān),還有cache mana...

    wupengyu 評論0 收藏0
  • Nginx 源碼分析:從模塊到配置(上)

    摘要:結(jié)構(gòu)體數(shù)組,用來表示該模塊可以在配置文件中配置的項(xiàng)目,及其操作指令。 源文件路徑 srccore gx_conf_file.h srccore gx_conf_file.c 主要內(nèi)容 本篇的主要目的在于分析Nginx的配置功能。由于Nginx的配置基本就是對模塊的配置,因此,在討論配置功能之前,需要先分析Nginx的模塊功能。 對于模塊功能,這里的重點(diǎn)不在于某個(gè)模塊的細(xì)節(jié),而...

    gotham 評論0 收藏0
  • 【Nginx源碼研究】nginx限流模塊詳解

    摘要:限流算法最簡單粗暴的限流算法就是計(jì)數(shù)器法了,而比較常用的有漏桶算法和令牌桶算法計(jì)數(shù)器計(jì)數(shù)器法是限流算法里最簡單也是最容易實(shí)現(xiàn)的一種算法。 運(yùn)營研發(fā)團(tuán)隊(duì) 李樂 高并發(fā)系統(tǒng)有三把利器:緩存、降級和限流; 限流的目的是通過對并發(fā)訪問/請求進(jìn)行限速來保護(hù)系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)(定向到錯(cuò)誤頁)、排隊(duì)等待(秒殺)、降級(返回兜底數(shù)據(jù)或默認(rèn)數(shù)據(jù)); 高并發(fā)系統(tǒng)常見的限流有:限制總并發(fā)...

    voyagelab 評論0 收藏0
  • ngx_http_limit_req_module 源碼分析

    摘要:如果當(dāng)前需要延遲處理,又會把請求放到定時(shí)器中,等到定時(shí)器過期以后,執(zhí)行寫事件回調(diào),這個(gè)函數(shù)里會執(zhí)行,重新進(jìn)行的個(gè)階段。 ngx_http_limit_req_module 是 Nginx 官方提供的一個(gè) http 模塊,它工作在 NGX_HTTP_PREACCESS_PHASE 階段,通過在 nginx.conf 中進(jìn)行簡單地配置,我們可以輕易地對請求速率進(jìn)行限制。 配置指令 官方文檔...

    lentrue 評論0 收藏0
  • 不再依靠巧合編寫 Nginx 配置

    摘要:找到這個(gè)模塊的指令后,則會調(diào)用這個(gè)指令的解析回調(diào)函數(shù)即結(jié)構(gòu)體的第三個(gè)參數(shù)來進(jìn)行處理。調(diào)用他們上面提到的中的回調(diào)函數(shù)來申請和初始化對應(yīng)模塊的配置結(jié)構(gòu)體。需要注意的是,即時(shí)當(dāng)前是直接在塊級別,這三個(gè)回調(diào)函數(shù)都會被調(diào)用。拒絕暴力枚舉式編寫配置文件 原博:https://blog.coordinate35.cn/... 熱身 首先來看下這幾個(gè)小例子: 第一個(gè)例子: server { l...

    tulayang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<