你所不知道的日誌非同步落庫

2021-08-30 11:20:18 字數 4332 閱讀 2621

在網際網路設計架構過程中,日誌非同步落庫,儼然已經是高併發環節中不可缺少的一環。為什麼說是高併發環節中不可缺少的呢? 原因在於,如果直接用mq進行日誌落庫的時候,低併發下,生產端生產資料,然後由消費端非同步落庫,是沒有什麼問題的,而且效能也都是異常的好,估計tp99應該都在1ms以內。但是一旦併發增長起來,慢慢的你就發現生產端的tp99一直在增長,從1ms,變為2ms,4ms,直至send timeout。尤其在大促的時候,我司的系統就經歷過這個情況,當時mq的傳送耗時超過200ms,甚至一度有不少timeout產生。

考慮到這種情況在高併發的情況下才出現,所以今天我們就來探索更加可靠的方法來進行非同步日誌落庫,保證所使用的方式不會因為過高的併發而出現介面ops持續下降甚至到不可用的情況。

此種方案由於使用了非同步方式,且由於非同步的discard policy策略,當大量資料過來,緩衝區滿了之後,會拋棄部分資料。此種方案適用於能夠容忍資料丟失的業務場景,不適用於對資料完整有嚴格要求的業務場景。

來看看具體的實現方式:

@resource(name = "messageproducer")

private messageproducer messageproducer;

@override

asyncpushmessage(loggingevent.getmessage()); }

/*** 非同步呼叫jmq輸出日誌

* @param message

*/ private void asyncpushmessage(object message) catch (jmqexception e)

}); }

@override

public boolean requireslayout()

@override

public void close()

}然後在log4j.xml中,為此類進行配置:

最後就可以按照如下的方式進行正常使用了:

注意: 此處需要注意log4j的乙個效能問題。在log4j的conversionpattern中,匹配符最好不要出現 c% l%萬用字元,壓測實踐表明,這兩個萬用字元會導致log4j打日誌的效率降低10倍。

方案一很簡便,且剝離了介面直接依賴mq導致的效能問題。但是無法解決資料丟失的問題(但是我們其實可以在本地搞個策略落盤來不及處理的資料,可以大大的減少資料丟失的機率)。但是很多的業務場景,是需要資料不丟失的,所以這就衍生出我們的另一套方案來。

方案二:增量消費log4j日誌

此種方式,是開啟worker在後台增量消費log4j的日誌資訊,和介面完全脫離。此種方式相比方案一,可以保證資料的不丟失,且可以做到完全不影響介面的ops。但是此種方式,由於是後台worker在後台啟動進行掃瞄,會導致落庫的資料慢一些,比如一分鐘之後才落庫完畢。所以適用於對落庫資料實時性不高的場景。

具體的實現步驟如下:

首先,將需要進行增量消費的日誌統一打到乙個資料夾,以天為單位,每天生成乙個帶時間戳日誌檔案。由於log4j不支援直接帶時間戳的日誌檔案生成,所以這裡需要引入log4j.extras元件,然後配置log4j.xml如下:

之後在**中的申明方式如下:

private static logger businesslogger = logge***ctory.getlogger("file_rolling_logger");
最後在需要記錄日誌的地方使用方式如下:

businesslogger.error(jsonutils.tojsonstring(mymessage))
這樣就可以將日誌列印到乙個單獨的檔案中,且按照日期,每天生成乙個。

然後,當日誌檔案生成完畢後,我們就可以開啟我們的worker進行增量消費了,這裡的增量消費方式,我們選擇randomaccessfile這個類來進行,由於其獨特的位點讀取方式,可以使得我們非常方便的根據位點的位置來消費增量檔案,從而避免了逐行讀取這種低效率的實現方式。

注意,為每個日誌檔案都單獨建立了乙個位點檔案,裡面儲存了對應的檔案的位點讀取資訊。當worker掃瞄開始的時候,會首先讀取位點檔案裡面的位點資訊,然後找到相應的日誌檔案,從位點資訊位置開始進行消費。這就是整個增量消費worker的核心。具體**實現如下(**太長,做了摺疊):

/**

* @description: 增量日誌掃瞄worker

* @detail: 此worker主要用來掃瞄增量日誌,日誌本身會在不停的插入中,此worker會不停的掃瞄此日誌來將資料上傳到kafka集群

* @date 2018-04-08 10:30

*/public class limitbuyscanworker

//昨日的檔案未掃瞄完畢

else }

/*** 檢測日誌是否被掃瞄消費完畢,true:消費完畢;false:未消費完畢

* @description 此舉主要防止log4j在零點大促開始的時候,突然的滾動檔案造成的部分增量日誌不會被消費的問題

* @param logfilepath

* @param offsetfilepath

*/ private boolean checkifconsumeok(string logfilepath, string offsetfilepath)

return false;

} catch (filenotfoundexception e) catch (ioexception e) }

/*** 掃瞄並消費增量日誌

* @param logfilepath

* @param offsetfilepath

*/ private void scanandconsumelog(string logfilepath, string offsetfilepath)

//讀取@scan_step行

for (long i = currentoffset; i < currentoffset + scan_step; i++) }

//讀取@scan_step行之後的位點

logger.error("讀取" + scan_step + "行之後位點==>" + randomaccessfile.getfilepointer());

//如果update不成功,可以不處理,後面掃瞄進來重新過一遍即可

updateoffset(randomaccessfile.getfilepointer(), offsetfilepath);

logger.error("檔案總長==>" + randomaccessfile.length());

} catch (filenotfoundexception e) catch (ioexception e) }

/*** 校驗位點

* 不存在則建立並賦值為0

* 已存在則更新位點

* @param offsetfilepath

* @return

* @throws ioexception

*/ private long checkoffset(string offsetfilepath) throws ioexception

//如果位點檔案存在,則返回位點檔案內容

else

string filteredoffset = stringbuilder.tostring().trim();

if (stringutil.isnotblank(filteredoffset)) else

} }/**

* 更新位點資訊

* @param offset

* @param offsetfilepath

*/ private void updateoffset(long offset, string offsetfilepath) throws ioexception

}

此種方式由於worker掃瞄是每隔一段時間啟動一次進行消費,所以導致資料從產生到入庫,可能經歷時間超過一分鐘以上,但是在一些對資料延遲要求比較高的業務場景,比如庫存扣減,是不能容忍的,所以這裡我們就引申出第三種做法,基於記憶體檔案佇列的非同步日誌消費。

方案三:基於記憶體檔案佇列的非同步日誌消費

由於方案一和方案二都嚴重依賴log4j,且方案本身都存在著要麼丟資料,要麼入庫時間長的缺點,所以都並不是那麼盡如人意。但是本方案的做法,既解決了資料丟失的問題,又解決了資料入庫時間被拉長的尷尬,所以是終極解決之道。而且在大**過程中,此種方式經歷了實戰檢驗,可以大面積的推廣使用。

本群提供免費的學習指導 架構資料 以及免費的解答

不懂得問題都可以在本群提出來 之後還會有直播平台和講師直接交流噢

哦對了,喜歡就別忘了關注一下哦~

你所不知道的 const

const 常量是不可修改的,也就是說only read,例如 const int nbuffsize 512 nbuffsize 0 error就是因為const 常量不能修改,所以定義時必須初始化預設在全域性作用域中定義的非const變數可以在整個程式中訪問,例如 int ncounter ex...

你所不知道的background

今天要說說css中background這個屬性裡面的大學問。在乙個宣告中設定所有的背景屬性 body 看到這串 你怕了嗎?知道他們都代表啥意思嘛?不要捉急,來看展開式。展開式 background color設定元素的背景顏色,不能設定到外邊距,可以繼承父級的背景顏色,預設為透明。backgroun...

overflow hidden 你所不知道的事

overflow hidden 你所不知道的事 overflow hidden這個css樣式是大家常用到的css樣式,但是大多數人對這個樣式的理解僅僅侷限於隱藏溢位,而對於清除浮動這個含義不是很了解。這是乙個常用的div寫法,下面我們來書寫樣式。大家可以在dmx中自己做試驗 wai nei 可以看到...