比較早的時候寫過一篇 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