Nginx Unique Tracing ID

背景

我們想要從Nginx接受請求開始,生成一個Unique Tracing ID舍咖,不僅記錄在Nginx的日志中矩父,也要貫穿到整個后臺的服務,從而利用這個ID方便問題的排查排霉。

方案一

利用Nginx豐富的內置變量窍株,拼接出一個“unique enough id”。這里使用了五個變量:

  • $pid: Nginx worker process id
  • $msec: timestamp in millisecond
  • $remote_addr: client address
  • $connection: TCP connection serial number
  • $connection_requests: current number of requests made through a connection

實現(xiàn)步驟

1.在nginx.conf的location模塊里:

location / {
    proxy_pass http://upstream;
    set $req_id $pid.$msec.$remote_addr.$connection.$connection_requests;
    proxy_set_header X-Request-Id $req_id;
}

2.在http模塊的 log_format 里加上 $req_id攻柠,至此Nginx的日志中將包含這個ID

log_format trace '... $req_id';

3.在后臺服務中可以通過下面的方式獲取$req_id

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write(self.request.headers["X-Request-Id"])

4.重啟Nginx

nginx -s reload

問題

格式混亂球订,信息冗余,生成的效果如下:

97372.1493211301.686.127.0.0.1.471.32

方案二

使用Nginx內置的變量 $request_id
這是最直接的辦法瑰钮,使用Nginx自帶的一個$request_id冒滩,一個16位比特的隨機數(shù),用32位的16進制數(shù)表示浪谴。

proxy_set_header X-Request-Id $request_id;

問題

這Nginx 1.11.0 版本新增加的feature开睡,使用Nginx舊版本,或者依賴某些二次開發(fā)的Nginx版本苟耻,例如 Tengine 繼承的是Nginx 1.8.1 版本篇恒,都面臨著升級Nginx的問題。

方案三

使用 Lua 生成一個uuid.
利用Lua輕量小巧的特性梁呈,嵌入到Nginx的配置文件當中婚度,然后生成一個uuid.

實現(xiàn)步驟

1.在 http 模塊里加入:

    map $host $uuid {
        default '';
    }
    lua_package_path '/path/to/uuid4.lua';
    init_by_lua '
        uuid4 = require "uuid4"
        math = require "math"
    ';

2.在server模塊里加入:

    set_by_lua $uuid '
        return uuid4.getUUID()
    ';

3.在location模塊里加入:

    proxy_set_header X-Request-Id $uuid;

4.uuid4.lua
引用自 第三方庫

--[[
The MIT License (MIT)
Copyright (c) 2012 Toby Jennings
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--]]

local M = {}
-----
math.randomseed( os.time() )
math.random()
-----
local function num2bs(num)
    local _mod = math.fmod or math.mod
    local _floor = math.floor
    --
    local result = ""
    if(num == 0) then return "0" end
    while(num  > 0) do
         result = _mod(num,2) .. result
         num = _floor(num*0.5)
    end
    return result
end
--
local function bs2num(num)
    local _sub = string.sub
    local index, result = 0, 0
    if(num == "0") then return 0; end
    for p=#num,1,-1 do
        local this_val = _sub( num, p,p )
        if this_val == "1" then
            result = result + ( 2^index )
        end
        index=index+1
    end
    return result
end
--
local function padbits(num,bits)
    if #num == bits then return num end
    if #num > bits then print("too many bits") end
    local pad = bits - #num
    for i=1,pad do
        num = "0" .. num
    end
    return num
end
--
local function getUUID()
    local _rnd = math.random
    local _fmt = string.format
    --
    _rnd()
    --
    local time_low_a = _rnd(0, 65535)
    local time_low_b = _rnd(0, 65535)
    --
    local time_mid = _rnd(0, 65535)
    --
    local time_hi = _rnd(0, 4095 )
    time_hi = padbits( num2bs(time_hi), 12 )
    local time_hi_and_version = bs2num( "0100" .. time_hi )
    --
    local clock_seq_hi_res = _rnd(0,63)
    clock_seq_hi_res = padbits( num2bs(clock_seq_hi_res), 6 )
    clock_seq_hi_res = "10" .. clock_seq_hi_res
    --
    local clock_seq_low = _rnd(0,255)
    clock_seq_low = padbits( num2bs(clock_seq_low), 8 )
    --
    local clock_seq = bs2num(clock_seq_hi_res .. clock_seq_low)
    --
    local node = {}
    for i=1,6 do
        node[i] = _rnd(0,255)
    end
    --
    local guid = ""
    guid = guid .. padbits(_fmt("%X",time_low_a), 4)
    guid = guid .. padbits(_fmt("%X",time_low_b), 4)
    guid = guid .. padbits(_fmt("%X",time_mid), 4)
    guid = guid .. padbits(_fmt("%X",time_hi_and_version), 4)
    guid = guid .. padbits(_fmt("%X",clock_seq), 4)
    --
    for i=1,6 do
        guid = guid .. padbits(_fmt("%X",node[i]), 2)
    end
    --
    return guid
end
--
M.getUUID = getUUID
return M

問題

Lua的這個模塊太長蘸秘,擔心性能問題官卡,需要進行性能評估蝗茁。

方案四

還是利用Lua腳本,使用時間戳加隨機數(shù)的方式
關鍵步驟:

    set_by_lua $rdm_number '
        return os.time() .. os.clock()*100 .. math.random(1000000000, os.time())
    ';

問題

os.time()的精確度在1秒寻咒,os.clock()的精確度在0.01秒哮翘,這樣處理之后,總的精度在10毫秒毛秘,沒有達到要求饭寺。
Lua有一個 Luasocket 模塊,可以達到毫秒級別的精度叫挟,但是需要安裝艰匙。

方案五

結合Nginx的 $msec 變量和 Lua 的隨機數(shù)
關鍵配置

server {
    ...
    set_by_lua $rdm_number '
        return math.random(1000000000, os.time())
    ';
    location / {
        ...
        set $req_id $msec$rdm_number;
        proxy_set_header X-Request-Id $req_id;
    }
}

終記

最終確定方案五,簡單抹恳,方便员凝,影響最小。
在方案選擇奋献、測試過程中健霹,還遇到了環(huán)境搭建相關的問題,將記錄在下篇文章中瓶蚂,敬請期待糖埋!


參考

1.http://stackoverflow.com/questions/17748735/setting-a-trace-id-in-nginx-load-balancer
2.https://blog.ryandlane.com/2014/12/11/using-lua-in-nginx-for-unique-request-ids-and-millisecond-times-in-logs/
3.http://www.jb51.net/article/82167.htm
4.http://nginx.org/en/docs/http/ngx_http_core_module.html#.24args
5.http://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_id

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市窃这,隨后出現(xiàn)的幾起案子瞳别,更是在濱河造成了極大的恐慌,老刑警劉巖钦听,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洒试,死亡現(xiàn)場離奇詭異,居然都是意外死亡朴上,警方通過查閱死者的電腦和手機垒棋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來痪宰,“玉大人叼架,你說我怎么就攤上這事∫虑耍” “怎么了乖订?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長具练。 經常有香客問我乍构,道長,這世上最難降的妖魔是什么扛点? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任哥遮,我火速辦了婚禮岂丘,結果婚禮上,老公的妹妹穿的比我還像新娘眠饮。我一直安慰自己奥帘,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布仪召。 她就那樣靜靜地躺著寨蹋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扔茅。 梳的紋絲不亂的頭發(fā)上已旧,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音召娜,去河邊找鬼评姨。 笑死,一個胖子當著我的面吹牛萤晴,可吹牛的內容都是我干的吐句。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼店读,長吁一口氣:“原來是場噩夢啊……” “哼嗦枢!你這毒婦竟也來了?” 一聲冷哼從身側響起屯断,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤文虏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后殖演,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氧秘,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年趴久,在試婚紗的時候發(fā)現(xiàn)自己被綠了丸相。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡彼棍,死狀恐怖灭忠,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情座硕,我是刑警寧澤弛作,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站华匾,受9級特大地震影響映琳,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一萨西、第九天 我趴在偏房一處隱蔽的房頂上張望黍瞧。 院中可真熱鬧,春花似錦原杂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至际看,卻和暖如春咸产,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背仲闽。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工脑溢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赖欣。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓屑彻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親顶吮。 傳聞我的和親對象是個殘疾皇子社牲,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容