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()//做爲一個庫,也間接擔任了一個儲存變量的部分
function _M.initted() return logger_initted end
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
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正在退出,第二種,超過了丟棄的限制不加入緩存,並丟棄。緩存
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
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
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