【原創】modb 中日誌的設計


【日誌格式】

      在 以前 肯定好通訊所用的 json 數據格式後,到 肯定 最終生成的 日誌內容的時候了。以前提到日誌內容至少要包括下面幾點:
  • 日誌記錄的時間戳(在本地生成
  • 日誌的「流向」(從哪裏來,到哪裏去)->(從本地各類 app 來,到外部雲去
  • sql 語句自己(JSON 數據中攜帶
  • sql 語句的執行狀況(分紅:直接在 MySQL 上執行成功後在 modb 上記錄;經過 modb 向 MySQL 發送執行命令後記錄)->(OK/ERR)
      其實須要思考的問題只有「用哪些信息代表日誌流向」而已。modb 做爲基於 rabbitmq 的、消息轉發處理服務,「流向」只能經過消息內容自己得到,即經過 JSON 消息體中相關字段指明。經過上面定義的 JSON 數據結構,能夠獲得消息來源自哪一個 app ,對應 ip 地址,以及路由消息時的 routing_key(rk) 。

      關於時間戳如何獲取,在各種開源項目中具備相似的程序可參考,無外乎使用 C 庫中定義的標準 API進行組合。具體的獲取方式能夠參考:《C語言時間函數簡介》。
      關於 json 數據的解析使用的是 jansson 開源項目。關於各類開源 json 項目的簡單比較能夠參考:《各類 JSON 解析庫的功能簡介》。

      而寫日誌的相關代碼,參考了 Redis 中實現的 log 機制並與 Atlas 中採用的 log 機制進行了比較,在此基礎上改寫出了 modb 使用的 logger 。 細節:Redis 中的日誌記錄行爲是,針對每一條日誌打開文件、寫日誌、關閉文件。支持 syslog 的使用。

modb 最終產生的 log 內容示例以下:
[10643] 11 Dec 11:11:12 * src:172.16.80.111 rk:pc_1 app:Ejabberd sql:'set names utf8' OK 'Current Query affect [0] rows!'

【業務執行流程】

這裏描述下可能出現的日誌執行流程。
1. 公有云本地業務 A 更新數據庫;
2. 公有云本地業務 A 發送 rabbitmq 消息通知本地其餘業務數據庫更新成功,同時要求 modb 進行跨機房同步。消息中 "state" 必須爲 "transfer"。
{
    "src" : "172.16.80.111", 
    "rk" : "pc_1",
    "app" : "Ejabberd",
    "state" : "transfer",
    "sql" : "set names utf8"
    "desc" : "update database success"
}
3. 公有云 modb 收到上述 rabbitmq 消息後檢測其 "state" 字段的值,若爲 "transfer",則先記錄日誌(此時本地數據庫已經更新成功)再進行轉發;若爲 "notify" ,則不進行處理。
4. 私有云 modb 收到轉發來的 rabbitmq 消息後,先提取其中的 sql 語句更新本地數據庫(不管成功仍是失敗均要記錄日誌),若執行成功,則將 rabbitmq 消息中的 "state" 字段值變爲 "notify" 後通知本地的其餘業務更新成功。
{
    "src" : "172.16.80.111", 
    "rk" : "pc_1",
    "app" : "Ejabberd",
    "state" : "notify",
    "sql" : "set names utf8"
    "desc" : "update database success"
}
5. 私有云 modb 此時也會收到上述消息,能夠經過提取 "state" 的值來斷定是否須要進行轉發,若爲 "notify" 則代表不須要進行轉發處理。


一個小知識點:
===========

【使用 fprintf 和 fwrite 寫文件有何區別?】

======
博客一

fprintf(fp, "%d", buffer); 是將格式化的數據寫入文件

fprintf(文件指針,格式字符串,輸出表列); 

fwrite(&buffer, sizeof(int), 1, fp);是以二進制位方式寫入文件
fwrite(數據,數據類型大小(字節數),寫入數據的最大數量,文件指針); 

因爲fprintf寫入時,對於整數來講,一位佔一個字節,好比1,佔1個字節;10,佔2個字節;100,佔3個字節,10000,佔5個字節
因此文件的大小會隨數據的大小而改變,對大數據空間佔用很大。
而fwrite是按二進制寫入,因此寫入數據所佔空間是根據數據類型來肯定,好比int的大小爲4個字節(通常32位下),那麼整數10所佔空間爲4個字節,100、10000所佔空間也是4個字節。因此二進制寫入比格式化寫入更省空間。

所以,
對於1 2 3 4 5 6 7 8 9 0 十個整數,用fprintf寫入時,佔10個字節;而用fwrite寫入時,佔40個字節。
對於100 101 102 103 104 105 106 107 108 109 110 這十個整數,用fprintf寫入時,佔30個字節;而用fwrite寫入時,佔40個字節。
對於10000 10100 10200 10300 10400 10500 10600 10700 10800 10900 11000 這十個整數,用fprintf寫入時,佔50個字節;而用fwrite寫入時,仍是佔40個字節。
======

======
博客二

      C語言把文件看做一個字符(字節)的序列,即由一個一個字符(字節)的數據順序組成。根據數據的組織形式,可分爲ASCII文件和二進制文件。ASCII文件又稱爲文本(text)文件,它的每一個字節放一個ASCII代碼,表明一個字符。二進制文件是把內存中的數據按其在內在中的存儲形式原樣輸出到磁盤上存放。
fprintf(fp, "%d", buffer); 是將格式化的數據寫入文件
fprintf(文件指針,格式字符串,輸出表列);
fwrite(&buffer, sizeof(int), 1, fp);是以二進位位方式寫入文件
fwrite(數據,數據類型大小(字節數),寫入數據的最大數量,文件指針);
======

======
博客三

一句話表述:fwrite 是將數據不經轉換直接以二進制的形式寫入文件,而 fprintf 是將數據轉換爲字符(ASCII碼)後再寫入文件。

這樣就致使:
當使用 fwrite 將一個 int 型數字 65 寫入文本文件時,因爲 65 對應的二進制數是 1000001,十六進制數是 0x41 ,存儲的內容以二進制的形式表示爲 1000001 。在 notepad++ 中使用十六進制方式打開顯示的是:0x0041 ,轉換爲十進制則爲 65 ,使用記事本打開這個文本文件後顯示的是 A ,由於記事本程序默認爲存儲在文本文件中的數據都是 ASCII 碼形式存儲,它把 65 當作 ASCII 碼翻譯爲字符 A 。

當使用 fpintf 將一個 int 型數字 65 寫入文本文件時,將 65 每一位轉換爲 ASCII 碼存儲,六、5 分別對應 ASCII 碼 5四、53 ,存儲的是 ASCII 碼 5四、53 。在 notepad++ 中使用十六進制方式打開顯示的是:3635 ,轉換爲十進制則爲 5四、53,這正是數字 六、5 的 ASCII 碼。使用記事本打開這個文本文件時,記事本將存儲在其中的 5四、53 當作 ASCII 碼翻譯爲字符 六、5 顯示,咱們看到的是即是字符 65 。
======

===== 未完待續 =====
相關文章
相關標籤/搜索