实战:一种在http请求中使用protobuffer+nginx+lua收集打点日志的方案
背景
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/