色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

C++異步日志實踐

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-09 10:29 ? 次閱讀

一個高效可拓展的異步C++日志庫:RING LOG,本文分享了了其設計方案與技術原理等內容

導論

同步日志與缺點

傳統的日志也叫同步日志,每次調用一次打印日志API就對應一次系統調用write寫日志文件,在日志產生不頻繁的場景下沒什么問題

可是,如果日志打印很頻繁,同步日志有什么問題?

  • 一方面,大量的日志打印陷入等量的write系統調用,有一定系統開銷
  • 另一方面,使得打印日志的進程附帶了大量同步的磁盤IO,影響性能

那么,如何解決如上的問題?就是

異步日志與隊列實現的缺點

異步日志,按我的理解就是主線程的日志打印接口僅負責生產日志數據(作為日志的生產者),而日志的落地操作留給另一個后臺線程去完成(作為日志的消費者),這是一個典型的生產-消費問題,如此一來會使得:

主線程調用日志打印接口成為非阻塞操作,同步的磁盤IO從主線程中剝離出來,有助于提高性能

對于異步日志,我們很容易借助隊列來一個實現方式:主線程寫日志到隊列,隊列本身使用條件變量、或者管道、eventfd等通知機制,當有數據入隊列就通知消費者線程去消費日志

但是,這樣的異步隊列也有一定的問題:

  • 生產者線程產生N個日志,對應后臺線程就會被通知N次,頻繁日志寫入會造成一定性能開銷
  • 不同隊列實現方式也各有缺點:
  • 用數組實現:空間不足時,隊列內存不易拓展
  • 用鏈表實現:每條消息的生產消費都對應內存的創建銷毀,有一定開銷

好了,可以開始正文了

簡介

RING LOG是一個適用于C++的異步日志, 其特點是效率高(實測每秒支持125+萬日志寫入)、易拓展,尤其適用于頻繁寫日志的場景

一句話介紹原理:

使用多個大數組緩沖區作為日志緩沖區,多個大數組緩沖區以雙循環鏈表方式連接,并使用兩個指針p1和p2指向鏈表兩個節點,分別用以生成數據、與消費數據

生產者可以是多線程,共同持有p1來生產數據,消費者是一個后臺線程,持有p2去消費數據

大數組緩沖區 + 雙循環鏈表的設計,使得日志緩沖區相比于隊列有更強大的拓展能力、且避免了大量內存申請釋放,提高了異步日志在海量日志生成下的性能表現

此外,RING LOG還優化了每條日志的UTC格式時間的生成,明顯提高日志性能

具體工作原理

數據結構

Ring Log的緩沖區是若干個cell_buffer以雙向、循環的鏈表組成

cell_buffer是簡單的一段緩沖區,日志追加于此,帶狀態:

  • FREE:表示還有空間可追加日志
  • FULL:表示暫時無法追加日志,正在、或即將被持久化到磁盤;

Ring Log有兩個指針:

  • Producer Ptr:生產者產生的日志向這個指針指向的cell_buffer里追加,寫滿后指針向前移動,指向下一個cell_buffer;Producer Ptr永遠表示當前日志寫入哪個cell_buffer,被多個生產者線程共同持有
  • Consumer Ptr:消費者把這個指針指向的cell_buffer里的日志持久化到磁盤,完成后執行向前移動,指向下一個cell_buffer;Consumer Ptr永遠表示哪個cell_buffer正要被持久化,僅被一個后臺消費者線程持有

圖片

起始時刻,每個cell_buffer狀態均為FREE Producer Ptr與Consumer Ptr指向同一個cell_buffer

整個Ring Log被一個互斥鎖mutex保護

大致原理

消費者

后臺線程(消費者)forever loop:

1.上鎖,檢查當前Consumer Ptr:

  • 如果對應cell_buffer狀態為FULL,釋放鎖,去STEP 4;
  • 否則,以1秒超時時間等待條件變量cond;

2.再次檢查當前Consumer Ptr:

  • 若cell_buffer狀態為FULL,釋放鎖,去STEP 4;
  • 否則,如果cell_buffer無內容,則釋放鎖,回到STEP 1;
  • 如果cell_buffer有內容,將其標記為FULL,同時Producer Ptr前進一位;

3.釋放鎖

4.持久化cell_buffer

5.重新上鎖,將cell_buffer狀態標記為FREE,并清空其內容;Consumer Ptr前進一位;

6.釋放鎖

生產者

1.上鎖,檢查當前Producer Ptr對應cell_buffer狀態:

如果cell_buffer狀態為FREE,且生剩余空間足以寫入本次日志,則追加日志到cell_buffer,去STEP X;

2.如果cell_buffer狀態為FREE但是剩余空間不足了,標記其狀態為FULL,然后進一步探測下一位的next_cell_buffer:

  • 如果next_cell_buffer狀態為FREE,Producer Ptr前進一位,去STEP X;
  • 如果next_cell_buffer狀態為FULL,說明Consumer Ptr = next_cell_buffer,Ring Log緩沖區使用完了;則我們繼續申請一個new_cell_buffer,將其插入到cell_buffer與next_cell_buffer之間,并使得Producer Ptr指向此new_cell_buffer,去STEP X;

3.如果cell_buffer狀態為FULL,說明此時Consumer Ptr = cell_buffer,丟棄日志;

4.釋放鎖,如果本線程將cell_buffer狀態改為FULL則通知條件變量cond

在大量日志產生的場景下,Ring Log有一定的內存拓展能力;實際使用中,為防止Ring Log緩沖區無限拓展,會限制內存總大小,當超過此內存限制時不再申請新cell_buffer而是丟棄日志

圖解各場景

初始時候,Consumer Ptr與Producer Ptr均指向同一個空閑cell_buffer1

圖片

然后生產者在1s內寫滿了cell_buffer1,Producer Ptr前進,通知后臺消費者線程持久化

圖片

消費者持久化完成,重置cell_buffer1,Consumer Ptr前進一位,發現指向的cell_buffer2未滿,等待

圖片

超過一秒后cell_buffer2雖有日志,但依然未滿:消費者將此cell_buffer2標記為FULL強行持久化,并將Producer Ptr前進一位到cell_buffer3

圖片

消費者在cell_buffer2的持久化上延遲過大,結果生產者都寫滿cell_buffer3456,已經正在寫cell_buffer1了

圖片

生產者寫滿寫cell_buffer1,發現下一位cell_buffer2是FULL,則拓展換沖區,新增new_cell_buffer

圖片

UTC時間優化

每條日志往往都需要UTC時間:yyyy-mm-dd hh:mm:ss(PS:Ring Log提供了毫秒級別的精度)

Linux系統下本地UTC時間的獲取需要調用localtime函數獲取年月日時分秒

在localtime調用次數較少時不會出現什么性能問題,但是寫日志是一個大批量的工作,如果每條日志都調用localtime獲取UTC時間,性能無法接受

在實際測試中,對于1億條100字節日志的寫入,未優化locatime函數時 RingLog寫內存耗時245.41s,僅比傳統日志寫磁盤耗時292.58s快將近一分鐘;而在優化locatime函數后,RingLog寫內存耗時79.39s,速度好幾倍提升

策略

為了減少對localtime的調用,使用以下策略

RingLog使用變量_sys_acc_sec記錄寫上一條日志時,系統經過的秒數(從1970年起算)、使用變量_sys_acc_min記錄寫上一條日志時,系統經過的分鐘數,并緩存寫上一條日志時的年月日時分秒year、mon、day、hour、min、sec,并緩存UTC日志格式字符串

每當準備寫一條日志:

  1. 調用gettimeofday獲取系統經過的秒tv.tv_sec,與_sys_acc_sec比較;
  2. 如果tv.tv_sec 與 _sys_acc_sec相等,說明此日志與上一條日志在同一秒內產生,故年月日時分秒是一樣的,直接使用緩存即可;
  3. 否則,說明此日志與上一條日志不在同一秒內產生,繼續檢查:tv.tv_sec/60即系統經過的分鐘數與_sys_acc_min比較;
  4. 如果tv.tv_sec/60與_sys_acc_min相等,說明此日志與上一條日志在同一分鐘內產生,故年月日時分是一樣的,年月日時分 使用緩存即可,而秒sec = tv.tv_sec%60,更新緩存的秒sec,重組UTC日志格式字符串的秒部分;
  5. 否則,說明此日志與上一條日志不在同一分鐘內產生,調用localtime重新獲取UTC時間,并更新緩存的年月日時分秒,重組UTC日志格式字符串

小結:如此一來,localtime一分鐘才會調用一次,頻繁寫日志幾乎不會有性能損耗

性能測試

對比傳統同步日志、與RingLog日志的效率(為了方便,傳統同步日志以sync log表示)

1. 單線程連續寫1億條日志的效率

分別使用Sync log與Ring log寫1億條日志(每條日志長度為100字節)測試調用總耗時,測5次,結果如下:

圖片

單線程運行下,Ring Log寫日志效率是傳統同步日志的近3.7倍,可以達到每秒127萬條長為100字節的日志的寫入

2、多線程各寫1千萬條日志的效率

分別使用Sync log與Ring log開5個線程各寫1千萬條日志(每條日志長度為100字節)測試調用總耗時,測5次,結果如下:

圖片

多線程(5線程)運行下,Ring Log寫日志效率是傳統同步日志的近3.8倍,可以達到每秒135.5萬條長為100字節的日志的寫入

2. 對server QPS的影響

現有一個Reactor模式實現的echo Server,其純凈的QPS大致為19.32萬/s

現在分別使用Sync Log、Ring Log來測試:echo Server在每收到一個數據就調用一次日志打印下的QPS表現

對于兩種方式,分別采集12次實時QPS,統計后大致結果如下:

圖片

傳統同步日志sync log使得echo Server QPS從19.32w萬/s降低至11.42萬/s,損失了40.89%RingLog使得echo Server QPS從19.32w萬/s降低至16.72萬/s,損失了13.46%

TODO

  • 日志本身緩存大小的配置
  • 程序正常退出、異常退出,此時在buffer中緩存的日志會丟失
  • 第N天23:59:59秒產生的日志有時會被刷寫到第N+1天的日志文件中
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • API
    API
    +關注

    關注

    2

    文章

    1509

    瀏覽量

    62269
  • 文件
    +關注

    關注

    1

    文章

    570

    瀏覽量

    24798
  • C++
    C++
    +關注

    關注

    22

    文章

    2114

    瀏覽量

    73785
  • 日志
    +關注

    關注

    0

    文章

    138

    瀏覽量

    10666
收藏 人收藏

    評論

    相關推薦

    寫好C++代碼需要遵循的10個最佳實踐

    C++代碼提供了足夠的靈活性,因此對于大部分工程師來說都很難把握。本文介紹了寫好C++代碼需要遵循的10個最佳實踐,并在最后提供了一個工具可以幫助我們分析C++代碼的健壯度。
    發表于 09-19 09:30 ?753次閱讀

    現代C++項目的最佳實踐

    本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風格、安全性、可維護性、可移植性、多線程、性能、正確性等角度全面介紹了現代C++項目的最佳實踐。本文是該系列的第三篇。
    發表于 09-29 11:32 ?1203次閱讀

    C++程序設計原理與實踐》(C++之父最新力作)

    `《C++程序設計原理與實踐》(C++之父最新力作)`
    發表于 08-19 16:30

    Visual C++小波變換技術與工程實踐

    本帖最后由 lee_st 于 2018-2-15 23:48 編輯 Visual C++小波變換技術與工程實踐
    發表于 02-15 18:27

    Visual C++ 串口通信技術與工程實踐

    Visual C++ 串口通信技術與工程實踐
    發表于 08-19 22:31

    Visual C++ 串口通信技術與工程實踐

    Visual C++ 串口通信技術與工程實踐
    發表于 12-30 15:19

    Visual C++ 串口通信技術與工程實踐

    Visual C++ 串口通信技術與工程實踐
    發表于 04-03 11:39

    Visual C++ 串口通信技術與工程實踐

    Visual C++ 串口通信技術與工程實踐
    發表于 04-13 12:07

    在NDK開發中C++的代碼中怎么實現日志輸出

    在NDK開發中C++的代碼中怎么實現日志輸出?實現方法是什么?
    發表于 09-30 07:04

    C++課件、習題及答案

    *1.1  從CC++*1.2  最簡單的C++程序 1.3  C++程序的構成和書寫形式 1.4 
    發表于 09-08 09:35 ?108次下載
    <b class='flag-5'>C++</b>課件、習題及答案

    Visual C++數字圖像模式識別技術及工程實踐

    Visual C++數字圖像模式識別技術及工程實踐
    發表于 11-06 10:08 ?3次下載

    C++程序設計原理與實踐

    C++程序設計原理與實踐
    發表于 02-28 23:01 ?0次下載

    《Visual C++編程基礎與實踐》中文電子教材詳細資料免費下載

    本文檔的主要內容介紹的是《Visual C++編程基礎與實踐》中文電子教材詳細資料免費下載
    發表于 07-09 08:00 ?37次下載

    C++程序設計教程之C++的初步知識的詳細資料說明

    C++程序設計教程之C++的初步知識的詳細資料說明包括了:1. 從CC++,2 . 最簡單的C++程序,3 .
    發表于 03-14 14:48 ?31次下載
    <b class='flag-5'>C++</b>程序設計教程之<b class='flag-5'>C++</b>的初步知識的詳細資料說明

    C++代碼需要遵循的10個最佳實踐

    C++代碼提供了足夠的靈活性,因此對于大部分工程師來說都很難把握。本文介紹了寫好C++代碼需要遵循的10個最佳實踐,并在最后提供了一個工具可以幫助我們分析C++代碼的健壯度。
    的頭像 發表于 10-18 15:20 ?870次閱讀
    主站蜘蛛池模板: 美女扒开腿让男人桶个爽| 好大好硬好爽好深好硬视频| 亚洲一日韩欧美中文字幕在线| 人人模人人干| 美女露100%全身无遮挡| 久久精品视频15人人爱在线直播| 国产揄拍国产精品| 国产欧美一区二区三区视频| 国产精品99久久久久久AV下载 | 国产精品久免费的黄网站| 大迪克黑人异族| 二级片免费看| 搞基福利社| 国产成人精品久久一区二区三区| 厨房玩朋友娇妻中文字幕| 成人毛片免费在线观看| 成人国产亚洲欧美成人综合网 | 国产精品第八页| 国产欧美一区二区精品仙草咪| 国产女合集小岁9三部| 国产免费人视频在线观看免费| 国产精品一区二区激情| 国产偷国产偷亚洲高清SWAG| 国产在线AV一区二区香蕉| 果冻传媒视频在线观看完整版免费 | 国产露脸150部国语对白| 国产三级影院| 精品欧美一区二区三区久久久| 久久久久久九九| 奶头从情趣内衣下露了出来AV| 欧美精品一区二区三区视频| 热の中文 AV天堂| 偷拍久久国产视频免费| 亚洲成av人影院| 在线免费视频a| a免费在线观看视频| 成年免费三级视频| 国产精品久人妻精品| 九九99热久久999精品| 男男肉肉互插腐文| 色爱区综合激情五月综合激情|