實戰:一種在http請求中使用protobuffer+nginx+lua收集打點日誌的方案

Arli發表於2020-09-27

背景

app打點日誌的上報和收集,是網際網路公司的基本需求。

一.方案選擇

1.1 protobuffer vs json

探究一種以最高效的方式上報和解析打點資料是一個系統性的問題,需要解決的子問題有很多,例如降低網路傳輸成本,減少序列化反序列化的效能開銷,可靠性和高峰期的水平擴充套件,以及非耦合的編碼等等。

很多公司的打點日誌會採用比較簡單通用的json格式來上報,比如"第四正規化"的先薦系統就是使用json格式作為資料上報格式的,這樣做便於開發和理解,但是從處理效能方面來考慮並不是最好的選擇。

附上protobuffer和json的序列化反序列化效能評測對比: http://www.52im.net/thread-772-1-1.html

在2019年的資料庫峰會上,騰訊廣告聯盟的負責人曾介紹了廣告資料平臺的原始日誌格式,用的就是protobuffer,並且為了方便直接查原始資料格式,自研了一個名為dragon的資料儲存格式。

1.2  OpenResty (nginx+lua)

Nginx作為一款開源高效能且穩定的web伺服器,經歷了10年的發展,已經打敗了Apache,IIS等巨頭,成為了網際網路界的新寵。

Nginx的非同步非阻塞,以及模組化的特性,再加上lua指令碼的輕量級的特性,讓我們很方便的就能開發出一套可擴充套件且高可靠性的日誌收集系統,開發人員只需要關注功能實現本身即可。

 

1.3 處理流程圖

 

這裡只畫出了收集部分的步驟,通過Flume收集和處理日誌的步驟請見我的另一篇部落格:《將nginx收集的日誌通過flume轉到hive》

 

二.實現步驟

 2.1 定義日誌格式

由於每個客戶端5秒傳送一批日誌,可能會包含1條或者多條,為了防止重複傳送uuid、客戶端版本號等在一次傳送週期中不會改變的資料,可以抽取這部分客戶端公共的屬性作為獨立欄位;而如點選、播放、翻頁等非公共的屬性才通過protobuf陣列的形式傳送。  

post日誌的上傳格式如下:

1) body就是事件體陣列部分,每個事件單獨一條資料;

2)其他的欄位是可共用的公共屬性部分,一批事件中這些屬性相同。

3)token欄位是信令欄位,如果token錯誤,則可能是身份不明者偽造的上報資料。token的格式是(時間戳+金鑰)的md5編碼。金鑰部分可以隨意指定,客戶端和服務端保持一致即可。出於安全考慮本處打碼。

 eventobj的格式定義:

event:{

'eventtype': 'sv', #事件型別

'pg': 'home' #事件發生的一級頁面

'spg': 'recommend' #事件發生的二級頁面

 'ts': 1527238632,  #timestamp 為事件發生的unix時間戳(+當前時區),精確到秒 

 'arg': ''  # 字串型別,每個事件對應的其他引數,可能0個或者多個,0個的為空字串,多個的話用符號&連結。

 }

 

2.2 編寫event.proto檔案

本文中不會詳細的介紹protobuffer的知識,只會針對該案例講解操作步驟。如需要了解更多protobuffer的知識可以自行學習。

(有個比較坑的地方是工信部禁了developers.google.com,苦了找文件的各位童鞋。)

如下示例中指定了若干事件型別,若干一級頁面和二級頁面。檔名為event.proto。

syntax = "proto3";  //protobuff 

option java_outer_classname = "EventsProtos";

message Event {

  enum T { // event type
    SCANV = 0; // sv,  scan video
    PLAYV = 1; // pv, play video
    LIKEV = 6;//lv,  like video
    CLIKEV = 7; // clv, canceld like video
    SHAREV = 8; //shv, share video
  }
  
  enum Pg{ // first level page type
    HOME = 0; // 
    SEARCH = 1; //
    UPLOAD = 2; //
  }
  
  enum Spg{ // second level page type
    RECOMMEND = 0; //home 
    FRESH = 1; // home 
    HOT = 2; //home
  }

  T eventtype = 1;
  Pg pg = 2;  
  Spg spg = 3;
  int32 ts = 4; 
  string arg = 5;  

}

message Events {
  repeated Event events = 1;
}

 

2.3 生成protobuffer客戶端檔案。

EventsProtos.java 為Android 端用, Events.pbobjc.h Events.pbobjc.m 為ios端用,

 

2.4 讓OpenResty的lua模組支援protobuffer

1 mkdir /root/project/ 
2 mkdir /root/project/lua-protobuf 
3 git clone https://github.com/starwing/lua-protobuf lua-protobuf/ 
4 cd lua-protobuf/
5 gcc -O2 -I/usr/local/openresty/luajit/include/luajit-2.1/ -fPIC -shared -Wl,-rpath=./   pb.c -o pb.so
6 cp pb.so /usr/local/openresty/lualib/
7 cp serpent.lua /usr/local/openresty/lualib/
8 cp protoc.lua /usr/local/openresty/lualib/

 

相關文章