開源項目brpc從入門到精通(二)

開源項目brpc從入門到精通(二)

本文是第一個開源項目研究系列,第一個開源項目選擇的是brpc,brpc是百度內最常使用的工業級RPC框架, 有1,000,000+個實例(不包含client)和上千種多種服務, 在百度內叫做"baidu-rpc". 目前只開源C++版本。

ps:為啥選擇brpc,因為算法想要真正在實際中運用,我們還是得要一個高性能實現,而brpc能幫助我們快速解決網絡交互問題,讓我們去實現算法邏輯。

在在線系統中,日誌是非常重要的功能,能幫助我們分析、定位問題,下面會介紹如果讓我們自己設計一個高性能的日誌系統,我們應該怎麼做。

日誌

日誌我們平時都用,如果自己來設計一個日誌框架,需要從哪些角度考慮呢?

我們先來考慮我們使用日誌的話,他應該有哪些最基本的功能?

場景:程序在運行過程中,首先發出要記錄的信息,然後通過約定格式化這些信息,最後再將其輸出到目的地,所以一個日誌框架需要有的基本功能有:

  • 日誌記錄
  • 格式化
  • 輸出地

日誌記錄我們可以看做是一個前端api,而輸出地則是後端,兩者之間經過一個格式化組件進行數據的規範、傳輸。另外在分佈式系統中,日誌的輸出地只有一個,那就是本地磁盤。

在日誌框架的整體設計上,可以抽象為一個多生產者、單消費者的模型。前端通過api不斷寫入日誌,後端有一個消費者對日誌進行輸出,而目的就是本次磁盤。

如果輸出地是磁盤,那就必須要有日誌文件滾動功能,滾動的條件一般有兩個:

1. 日誌大小(每1G)

2. 時間(每隔1小時)

另外,如果我們要寫磁盤,那io就是關鍵,下面介紹下深入介紹下Linux 文件 io。

Linux 標準io

要想了解fwrite,最好的方式就是進行調試,下面是動手環節,大家可以按照下面的步驟,自己動手實驗的,便於理解,有任何問題都可以留言,儘量回答。

下面記錄下如何在開發機器上調試glibc代碼

因為在mac上開發,所以首先得有個Ubuntu鏡像。

1. docker run -it --name="gpp" ubuntu /bin/bash

2. apt-get unpdate && apt-get install -y ubuntu-dev-tools vim

3. docker commit gpp zhuanxuhit/ubuntu:v1

此處安裝 ubuntu-dev-tools 開發者工具。一勞永逸

參考文檔 跟我一起學Docker——搭建編譯環境篇

下一步是啟動了,啟動過程中,因為需要運行gdb,需要在啟動時加上--privileged=true參數,具體可以看:

dockercontainer下gdb無法正常工作的解決辦法

docker run --privileged=true -it -v ~/dev/share:/home/binss --name="gpp" ubuntu /bin/bash

下面是一段簡單的代碼:

開源項目brpc從入門到精通(二)

gcc -g3 -O0 -gdwarf-4 -ggdb test.c -o test

運行出core,我們通過ulimit -c unlimited在當前文件夾下產生core文件,具體可以看 Linux 下如何產生core文件(core dump設置)

發現是沒有temp文件導致的,創建touch temp後繼續運行。

gdb a.out

> l

> b 5

> r

> s

出現錯誤

_IO_new_fopen (filename=0x5555555547e6 "./temp", mode=0x5555555547e4 "r") at iofopen.c:88

88 iofopen.c: No such file or directory.

這是需要我們去下載glibc源文件

root@28dcf784e1be:/home/binss# ldd a.out

linux-vdso.so.1 (0x00007ffd7b3c1000)

libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f63d95b1000)

/lib64/ld-linux-x86-64.so.2 (0x00007f63d9ba4000)

依賴的是 libc.so.6

直接查看 /lib/x86_64-linux-gnu/libc.so.6

root@28dcf784e1be:/home/binss# ll /lib/x86_64-linux-gnu/libc.so.6

lrwxrwxrwx 1 root root 12 Apr 16 20:14 /lib/x86_64-linux-gnu/libc.so.6 -> libc-2.27.so*

執行 libc-2.27.so

接著我們下載libc的源代碼,需要修改下source.list

具體的源可以查看:https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/,注意打開deb-source,下載源代碼。

開源項目brpc從入門到精通(二)

安裝完代碼後,我們就可以開始調試了。通過

(gdb) directory /home/binss/glibc-2.27/libio

設置好源代碼搜索目錄,下面開始調試。

通過gdb可以直接定位到fwrite的源碼,源碼如下,位於./libio/iofwrite.c。

設置gdb選項 set print pretty on

先到了iofwrite.c:31

開源項目brpc從入門到精通(二)

下面我們看下gdb如任調試宏定義,怎麼在gdb中調試宏,可以參照https://sourceware.org/gdb/onlinedocs/gdb/Macros.html

gcc -g3 -O0 -gdwarf-2 test.c

上面我們怎麼知道gdb調試的時候會去加載glibc的debug版本呢?

開源項目brpc從入門到精通(二)

通過設置 verbose on可以看到gdb會去自動加載符號進來。

最終 _IO_sputn 被化簡為:IO_validate_vtable(const struct _IO_jump_t)-> __xsputn(fp, buf, request)

__xsputn 是 _IO_jump_t 中的指針

開源項目brpc從入門到精通(二)

(gdb) s

_IO_new_file_xsputn (f=0x555555756260, data=0x7fffffffe67b, n=12) at fileops.c:1220

1220 {

開源項目brpc從入門到精通(二)

整個調用棧

開源項目brpc從入門到精通(二)

將新申請到的內存設置到f->_IO_buf_base 中, 最終執行完後,fp中內存數據,我們將數據從用戶空間拷貝到了glibc分配的空間中。

開源項目brpc從入門到精通(二)

上面我們可以看到,我們將數據是寫入到了glibc的緩衝區中,下面通過fflush,將其寫入到內核緩衝區中。調用函數fflush,gdb調試:

開源項目brpc從入門到精通(二)

現在調用鏈到了系統調用了

開源項目brpc從入門到精通(二)

系統調用部分,先上一張圖:

開源項目brpc從入門到精通(二)

記得大學那塊,自己做嵌入式開發,就是看好多Linux驅動程序編寫。這塊有機會以後專門開個專題來介紹的。

現在總結下目前的進展,我們此次調試的目的是想看下fwrite是怎麼一步一步將數據寫入磁盤的,我們發現要想寫入磁盤,首先我們的數據會從用戶緩衝區中被拷貝到glibc的緩衝區,然後glibc再進行系統調用,將數據寫入到內核緩衝區,然後設備驅動程序再將數據從內核緩衝區寫到設備緩衝區,整個過程可以看下圖:

開源項目brpc從入門到精通(二)

圖片來自文章:漫談linux文件IO,文中很好的闡述了整個io過程,推薦閱讀。

總結

本文介紹了程序中重要的日誌功能,為了能實現高性能日誌,我們去分析了底層寫入磁盤到底發生了什麼,發現其中一層層的數據拷貝,這些完全都是可以優化的,所以下一篇會去介紹目前高性能庫的做法,歡迎持續關注。


分享到:


相關文章: