Openresty 優秀lua源碼庫分析(一)

                                            Openresty 優秀lua源碼庫分析(一)nginx

源碼:https://github.com/cloudflare/lua-resty-logger-socket/git

用法:github

local logger = require "resty.logger.socket"
if not logger.initted() then
    local ok, err = logger.init{
        host = 'xxx',
        port = 1234,
        flush_limit = 1234,
        drop_limit = 5678,
       }
    if not ok then
        ngx.log(ngx.ERR, "failed to initialize the logger: ",err)
            return 
     end
end


local bytes, err = logger.log(msg)
if err then
    ngx.log(ngx.ERR, "failed to log message: ", err)
       return
 end

咱們能夠看到這裏有兩個比較類似的函數的用法數組

initted(),init()//做爲一個庫,也間接擔任了一個儲存變量的部分

initted方法主要返回一個是否執行過初始化的變量

function _M.initted()
    return logger_initted
end

init方法參數是一個table類型

function _M.init(user_config)
    if (type(user_config) ~= "table") then
        return nil, "user_config must be a table"
    end
    遍歷table的key/value值,並進行賦值,注意賦值以前進行類型的判斷,嚴謹(初始化一次面臨的問題)
    for k, v in pairs(user_config) do
        if k == "host" then
            if type(v) ~= "string" then
                return nil, '"host" must be a string'
            end
            host = v
          ..........
        end
    end
    //對必要參數進行校驗
    if not (host and port) and not path then
        return nil, "no logging server configured. \"host\"/\"port\" or "
                .. "\"path\" is required."
    end


    flushing = false
    exiting = false
    //將鏈接分爲兩個階段,鏈接中(ing,爲了是提供重試次數的一個狀態),鏈接完畢(ed)根據true/false肯定是否鏈接成功
    connecting = false
    connected = false
    retry_connect = 0
    retry_send = 0

    logger_initted = true
    
    //檢查是不是按時間發送緩存,若是是就將need_periodic_flush設置爲true,而後使用timer_at函數解決定時回調函數
    if periodic_flush then
        if debug then
            ngx_log(DEBUG, "periodic flush enabled for every "
                    .. periodic_flush .. " seconds")
        end
        need_periodic_flush = true
        timer_at(periodic_flush, _periodic_flush)//定時刷盤,但應該是一次的
    end

    return logger_initted//返回初始化後的狀態
end

log函數,這個函數是整個庫的核心部分

function _M.log(msg)
    //判斷狀態
    if not logger_initted then
        return nil, "not initialized"
    end

    local bytes
    //將傳入字符串進行判斷和轉換
    if type(msg) ~= "string" then
        msg = tostring(msg)
    end

    if (debug) then
        ngx.update_time()
        ngx_log(DEBUG, ngx.now(), ":log message length: " .. #msg)
    end
    --判斷長度
    local msg_len = #msg
    //沒有搞清楚這個註釋的真正意義
    -- response of "_flush_buffer" is not checked, because it writes
    -- error buffer
    --對work是否正在退出事件進行捕捉,防止丟失部分日誌,這個部分是這個庫的健全性,考慮了work自己的生命週期
    if (is_exiting()) then
        exiting = true
        _write_buffer(msg)
        _flush_buffer()
        if (debug) then
            ngx_log(DEBUG, "Nginx worker is exiting")
        end
        bytes = 0
    --判斷是否達到了緩存的最大值
    elseif (msg_len + buffer_size < flush_limit) then
        _write_buffer(msg)
        bytes = msg_len
    --判斷是否到了丟棄的最大值
    elseif (msg_len + buffer_size <= drop_limit) then
        _write_buffer(msg)
        _flush_buffer()
        bytes = msg_len
    else
        --增加速度過大,必須丟棄,否則會壓垮緩存
        _flush_buffer()
        if (debug) then
            ngx_log(DEBUG, "logger buffer is full, this log message will be "
                    .. "dropped")
        end
        bytes = 0
        --- this log message doesn't fit in buffer, drop it
    end

    if last_error then
        local err = last_error
        last_error = nil
        return bytes, err
    end

    return bytes
end

總結:對bytes爲0會有兩種可能,一種是work正在退出,第二種,超過了丟棄的限制不加入緩存,並丟棄。緩存

方法 _write_buffer(msg)

local function _write_buffer(msg)
    log_buffer_index = log_buffer_index + 1
    log_buffer_data[log_buffer_index] = msg

    buffer_size = buffer_size + #msg


    return buffer_size
end

使用的數組的感受用法以下:

-- internal variables
local buffer_size           = 0
-- 2nd level buffer, it stores logs ready to be sent out
local send_buffer           = ""
-- 1st level buffer, it stores incoming logs
local log_buffer_data       = new_tab(20000, 0)

-- table.new(narr, nrec)
local succ, new_tab = pcall(require, "table.new")
if not succ then
    new_tab = function () return {} end
end

方法_flush_buffer()

local function _flush_buffer()
    local ok, err = timer_at(0, _flush)
    //使用這個的緣由還不清楚,將定時刷盤設置爲false
    need_periodic_flush = false

    if not ok then
        _write_error(err)
        return nil, err
    end
end

使用timer_at()回調_flush()函數,時間爲0就不等待,之因此使用這個是爲了突破nginx.socket的做用範圍,簡直就是一個神器。socket

方法_flush()對整個緩存的設計核心部分,須要仔細分析(續)

local function _flush()
    local err
    --對沒有上鎖的狀況進行上鎖,並往下走
    -- pre check
    if not _flush_lock() then
        if debug then
            ngx_log(DEBUG, "previous flush not finished")
        end
        -- do this later
        return true
    end
    --進入事後再判斷緩存數據是否爲空,畢竟哪一個線程拿到鎖也說不清楚
    if not _need_flush() then
        if debug then
            ngx_log(DEBUG, "no need to flush:", log_buffer_index)
        end
        _flush_unlock()
        return true
    end
    -- start flushing
    retry_send = 0--記錄重試次數
    if debug then
        ngx_log(DEBUG, "start flushing")
    end

    local bytes
    while retry_send <= max_retry_times do
        if log_buffer_index > 0 then
            _prepare_stream_buffer()
        end

        bytes, err = _do_flush()--發送

        if bytes then--使用返回字節作判斷
            break
        end

        if debug then
            ngx_log(DEBUG, "resend log messages to the log server: ", err)
        end

        -- ngx.sleep time is in seconds
        if not exiting then
            ngx_sleep(retry_interval / 1000)
        end

        retry_send = retry_send + 1
    end

    _flush_unlock()--除去鎖住的狀況

    if not bytes then
        local err_msg = "try to send log messages to the log server "
                        .. "failed after " .. max_retry_times .. " retries: "
                        .. err
        _write_error(err_msg)
        return nil, err_msg
    else
        if debug then
            ngx_log(DEBUG, "send " .. bytes .. " bytes")
        end
    end

    buffer_size = buffer_size - #send_buffer--減去剩下的buffer大小
    send_buffer = ""--對發送值制空

    return bytes
end

_prepare_stream_buffer處理髮送數據函數(參數max_buffer_reuse爲0能夠確保沒有髒數據)函數

--從新使用日誌buffer的次數限制,不清楚爲何會有一個複用限制,counter就是純粹的計數,到達次數就開闢一個新的空間,用回收的方式應該會有一部分的髒數據我以爲,發送間歇請求差距過大的狀況下,但日誌髒數據影響不啊
local function _prepare_stream_buffer()
    local packet = concat(log_buffer_data, "", 1, log_buffer_index)
    send_buffer = send_buffer .. packet--最新的buffer和之前加起來,怎麼處理log_buffer_data比較好奇

    log_buffer_index = 0
    counter = counter + 1
    if counter > max_buffer_reuse then
        log_buffer_data = new_tab(20000, 0)
        counter = 0
        if debug then
            ngx_log(DEBUG, "log buffer reuse limit (" .. max_buffer_reuse
                    .. ") reached, create a new \"log_buffer_data\"")
        end
    end
end
相關文章
相關標籤/搜索