openresty通過lua增加隨機traceid

巨集量發表於2018-07-16

在沒有引入zipkin(或者阿里的鷹眼,百度的華佗)這種trace系統的時候,排查問題的一般思路都是按照請求鏈路來尋找問題源。因此如果能在請求鏈路中有一個唯一的標識就最好了,而在nginx/openresty做接入層的架構中,可以通過lua指令碼生成一個隨機traceid。

隨機數的生成原理,都是先初始化一個隨機數種子,由於偽隨機數的特性,種子的隨機性就顯得格外重要,而一般種子的生成都是通過時間的倒序來選取

lua 隨機數生成方法

首先我們看下通常lua的隨機數生成方法

math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))
math.random(m,n)

通過時間字串的逆序初始化隨機種子,這裡注意到有個sub函式做了截斷,是因為

math.randomseed will call the underlying C function srand which takes an unsigned integer valueLua will cast the value of the seed to this format. In case of an overflow the seed will actually become a bad seed, without warning

所以需要避免出現高型別向低型別轉換的溢位問題

common 方法的問題

但上面的方法有個問題,就是os.time()函式返回的是秒(10位整數), 所以在做web請求的traceid時很容易就出現重複,影響問題追蹤的效率,而lua如果要以毫秒為單位的時間來初始化隨機種子,需要引入socket等外部模組,對於openresty來說一般都是封裝好的,不方便去做這種定製

利用openresty 與lua生成traceid

幸運的是,nginx-lua中有個函式ngx.now會返回一個浮點數(當然在lua中統一為number型別),3位小數即為毫秒位,所以問題就變得簡單了

access_by_lua_block {
    math.randomseed(tonumber(tostring(ngx.now()*1000):reverse():sub(1,9)))
    local randvar = string.format("%.0f",math.random(1000000000000000000,9223372036854775807))
    ngx.req.set_header("traceid", randvar)
}

通過ngx.now()*1000拿到毫秒資料轉換為字串取反,這樣毫秒資料的變化才能顯出效果; unsigned int(64bit機器下為4byte) 最大值為10位數,我們取前9位避免資料溢位帶來的轉換問題; lua在顯示大於64bit的資料時會自動用科學技術法表示,所以我們需要通過string.format函式來將其轉換為19位數字,然後通過ngx.req.set_header新增到請求頭中去


相關文章