muduo学习笔记 - 第五章 高效的多线程日志
第五章 高效的多線程日志
-
日志有兩種意思:
-
診斷日志
-
交易日志
-
-
本章講的是前一種日志,文本的供人閱讀的日志,通常用于故障診斷和追蹤,也可用于性能分析。
-
日志通常要記錄:
-
收到的每條消息的id(關鍵字段,長度,hash等)
-
收到的每條外部消息的全文
-
發出每條消息的全文,每條消息都有全局唯一的id
-
關鍵部分狀態的變更,等等
-
5.1 功能需求
-
日志庫大體分為前端和后端兩個部分
- 前端負責提供應用程序使用的接口API,并生成日志消息
- 后端負責把日志消息寫到目的地
-
C++日志庫的前端大體有兩種API風格
- printf的格式化輸出風格
- stream<<風格
stream風格的好處是當輸出日志級高于語句的日志級別時,打印日志操作時個空操作,運行時開銷接近零
-
分布式系統中的服務進程而言,日志的目的地只有一個:本地文件。往網絡寫日志消息時不靠譜的,因為診斷日志功能之一正是診斷網絡故障,如果日志消息也是通過網絡發到另一臺機器就一損俱損…
-
本地文件作為destination,日志文件的滾動時必須的,可以簡化日志的歸檔實現
- 文件大小(例如寫滿10GB就換下一個文件)
- 時間(例如每天零點新建一個日志文件,不論上一個文件是否寫滿)
日志文件壓縮和歸檔,不應該是日志庫應有的功能,應該交給專門的腳本去做
-
日志重復利用空間的功能,只會幫倒忙
-
往文件寫日志的常見問題是,如果程序崩潰,最后幾條日志信息就會丟失,因為日志庫不能每條消息都flush硬盤,更不能每條消息都open/close文件,這樣開銷太大。
- 定時(默認3秒)將緩沖區的日志消息flush到硬盤
- 每條內存中的日志消息都帶有cookie,其值為函數地址,通過core dump文件找到cookie就能找到尚未寫到硬盤的消息
5.2 性能需求
- 日志庫的高性能體現在:
- 每秒寫幾千上萬條日志的時候沒有明顯的性能損失
- 能應對一個進程產生大量日志數據的場景,1GB/min
- 不阻塞正常的執行流程
- 在多線程中不造成爭用
- muduo日志庫的實現的優化措施:
- 時間戳字符串中的日期和時間兩部分是緩沖的,一秒之內的多條日志只需重新格式化微妙部分
- 日志消息前4個字節時定長的,避免在運行期間求strlen,編譯器認識memcpy()函數,對于定長的內存復制,會在編譯期把它inline展開為高效的目標代碼
- 線程id時預先格式化為字符串,輸出只需要拷貝幾個字節
- 每條日志消息的源文件名部分采用了編譯期計算來獲得basename,避免運行期strrchr()開銷
5.3 多線程異步日志
多線程程序的每個進程最好寫一個日志文件,這樣分析日志最容易,不必再多個文件中跳來跳去
-
muduo日志庫采用的時雙緩沖技術
準備兩塊buffer:A和B,前端負責往buffer A寫入數據,后端負責將buffer A的數據寫到文件中,之后前端往buffer B中填入新的數據,如此反復。
用兩個buffer的好處是在新建日志消息的時候不必等待磁盤文件操作,也避免每條新日志消息都觸發后端日志線程,換句話就是前端不是將每一條日志消息分別送給后端,而是將多條日志消息平成一個大的buffer傳給后端,相當于批處理,減少線程喚醒的開銷,降低開銷。
為了及時將日志消息寫道文件,即使buffer A沒有滿,日志庫也要沒3秒執行一次swap(buffer A, buffer B)
-
muduo采用前端后端,各兩個buffer(curr buffer,next buffer)和一個buffer vector保存full buffer四種可能存在的情況
-
前端寫日志頻率不高,后端3秒超時寫入文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ny3JEoOc-1583147157747)(./img/5.3-buf1.png)]
在第2.9s,curr使用了80%,第3秒后端線程醒過來,將curr送到buffers,把new1移動到curr,隨后交換buffers和buffersToWrite,當文件寫完之后,把new1重新填上,等待下次cond.waitForSeconds()返回
-
3秒超時之前已經寫滿burr buffer,喚醒后端開始寫入文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pY35lWXl-1583147157765)(./img/5.3-buf2.png)]
在第1.5s,curr使用了80%,在1.8scurr寫滿送到buffers,將next替換到curr buff,喚醒后端線程。后端將curr加到buffers中,在把new1移動到curr,交換buffers和buffersToWrite,new2替換next。完成之后重新填充new1和new2
-
前端短時間密集寫入日志消息,用完了兩個緩沖需要新分配緩沖
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1WTHFN2J-1583147157770)(./img/5.3-buf3.png)]
在第1.8s,A已經寫滿,B接近滿,已經notify()后端線程,但是后端線程由于一些原因沒有立即響應,到1.9s線程B寫滿,前端線程新創建緩沖E。在1.8s+后端線程獲得控制權,將C,D移動給前端,把當前的curr放到buffers,將A、B、E寫到文件。使用A、B填充new1/2釋放緩沖E
-
文件寫入速度較慢,導致前端耗盡了兩個緩沖,并分配了新緩沖
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Vz3gpLwR-1583147157772)(./img/5.3-buf4.png)]
1.8s前和場景2相同,后端線程寫入過慢,導致前端已經寫滿兩個緩沖并且分配了一個新的緩沖,這期間notify已經丟失。當后端完成之后,發現buffers不為空,立刻進入下個循環
-
總結
以上是生活随笔為你收集整理的muduo学习笔记 - 第五章 高效的多线程日志的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows桌面任务栏透明化
- 下一篇: kali 设置中文字体