比较早的时候写过一篇 Python Golang 解析web日志正则一例 当时发现 Golang的正则模块性能不是很好。最近在逛 reddit的时候发现了这样一篇文章 Fast log parsing with Go ,文中提到了一个基于行做半结构化解析的库ldetool, 于是又做了一些尝试和学习。
ldetool 这个工具更像一个DSL,通过一种语法来写解析的语法,然后通过 ldetool generate 命令来生成 golang 代码,这个代码就是针对某中格式单行的解析方法,详细的教程大家可以参考 https://github.com/sirkon/ldetool/blob/master/README.md ,下面是一个简单的测试过程的记录。
测试的环境 Mac Pro 8G4Core,粗略的看下性能,并不严谨
ldetool
安装
通过 go get就可以安装, 安装之后就是个命令
▶ go version
go version go1.11 darwin/amd64
go get -u github.com/sirkon/ldetool
▶ ldetool -v
ldetool version 0.0.0
场景
测试的样本是类似nginx的access log文件,格式如下
27.154.70.117 - - [20/Nov/2018:20:23:56 +0800] 2 "GET http://7img1.tianlaikge.com/tv/homeimage/201709/shengridangao.zip HTTP/1.1" 200 842186 841382 "-" "-" "Dalvik/2.1.0 (Linux; U; Android 7.1.2; vivo Y79A Build/N2G47H)" "-" 219082621 "HIT" 27.159.73.35
测试需求,解析30M左右的日志文件,大概12w行,计算总流量(流量字段为 842186)
分析
这种日志文件有几个特点
- 每个记录就是一行文件
- 虽然是文本,但是不是完全的无结构,算半结构化
- 上下行基本无关,可以并行处理
通常的解析方案
- 使用正则表达式(后面有个python版本的使用正则来分析)
- 使用split方法,根据特征分隔符来分割,例如空格,双引号
- 使用DSL,本质还是类似split,不过是通过代码生成器自动生成解析代码(上面提到的 ldetool 命令啦)
测试
源代码在 https://github.com/orangle/snippets/tree/master/golang/log_parse ,主要是对比split方式和ldetool生成的代码的性能(因为之前测试regex效果并不好,这里没有加入测试)
直接执行函数,使用 time 来测量的结果(多次测试下来,下面两个算是平均值)
# split
▶ time go run main.go nginx_lde.go
total: 96130191409
go run main.go nginx_lde.go 1.80s user 0.25s system 106% cpu 1.914 total
# ldetool
▶ time go run main.go nginx_lde.go
total: 96130191409
go run main.go nginx_lde.go 1.36s user 0.23s system 108% cpu 1.464 total
使用 golang test benckmark 测量的结果
▶ go test -bench=. -benchmem
total: 96130191409
goos: darwin
goarch: amd64
pkg: log_parse
BenchmarkSplit-4 1 1549334472 ns/op 512189744 B/op 1629989 allocs/op
total: 96130191409
BenchmarkLdetool-4 1 1052007947 ns/op 2996064 B/op 24805 allocs/op
PASS
ok log_parse 2.608s
总的来看,ldetool还是能快40~50%的,内存使用和内存分配更是少了很多。
Python
之后我还做了个同样样本的Python测试,主要是正则表达式和split方式的对比,也可以参照golang的结果对比。
代码在 https://github.com/orangle/snippets/tree/master/python/regex_vs_split
python re, re没有 compile
▶ python regex_vs_split.py
total bytes: 96130099518 cost: 5.805 qps: 92174.132
python re, re compile之后
▶ python regex_vs_split.py
total bytes: 96130099518 cost: 5.324 qps: 100500.519
python split 之后分析
▶ python regex_vs_split.py
total bytes: 96130191409 cost: 3.957 qps: 135215.152
这样看起来 split要比 regex 快一些,但是比golang又慢蛮多的。
End
下次在遇到这种需求,可以尝试下 ldetool