内容简介:在实践 Kubernetes 中应用日志的收集方案时,发现有一些日志无法收集到承接《入门——
写在前面
在实践 Kubernetes 中应用日志的收集方案时,发现有一些日志无法收集到 E lastic S earch(ELK 技术栈中的 E ,其他两个是 L ogstash 和 K ibana)。查了原因后发现原来是因为业务打印的日志“不合规”,不仅无法保存到 ES 中,还会影响 ES 保存日志的效率;业务方不乐意,负责 ES 的同事也不乐意 :pensive:
承接《 Go 反序列化 JSON 字符串的两种常见用法 》,谈谈 Go 标准库对 JSON 的处理效率。
适用人群
入门—— 初级√ ——中级——高级;本文适应初级及以上。
跑一跑 Benchmark 的数据
先看一个 JSON 字符串
// string-of-json JSON 字符串
{"employees":{"first":{"firstName":"Bill","lastName":"Gates"},"second":{"firstName":"George","lastName":"Bush"},"third":{"firstName":"Thomas","lastName":"Carter"}}}
// echo ```string-of-json``` | python -m json.tool
// 可以得到下面 Pretty 后的 JSON 格式
// Pretty 样式给人看起来比较舒服
{
"employees": {
"first": {
"firstName": "Bill",
"lastName": "Gates"
},
"second": {
"firstName": "George",
"lastName": "Bush"
},
"third": {
"firstName": "Thomas",
"lastName": "Carter"
}
}
}
再看看需求
根据 ES 的限制,期望能够把上面的 JSON 字符串转换成为下面的模样:
// ① 把大于第一层的所有内容转换为字符串
{
"employees": "{\"first\":{\"firstName\":\"Bill\",\"lastName\":\"Gates\"},\"second\":{\"firstName\":\"George\",\"lastName\":\"Bush\"},\"third\":{\"firstName\":\"Thomas\",\"lastName\":\"Carter\"}}"
}
// ② 把大于第二层的所有内容转换为字符串
{
"employees": {
"first": "{\"firstName\":\"Bill\",\"lastName\":\"Gates\"}",
"second": "{\"firstName\":\"George\",\"lastName\":\"Bush\"}",
"third": "{\"firstName\":\"Thomas\",\"lastName\":\"Carter\"}"
}
}
// ③ 把大于第三层的所有内容转换为字符串
// 从效果来看,最后一层存在字符串转义,不太期望如此,可是也还好把
{
"employees": {
"first": {
"firstName": "\"Bill\"",
"lastName": "\"Gates\""
},
"second": {
"firstName": "\"George\"",
"lastName": "\"Bush\""
},
"third": {
"firstName": "\"Thomas\"",
"lastName": "\"Carter\""
}
}
}
用代码跑跑数据
package hello
import (
"encoding/json"
"fmt"
"testing"
)
var data = `{"employees":{"first":{"firstName":"Bill","lastName":"Gates"},"second":{"firstName":"George","lastName":"Bush"},"third":{"firstName":"Thomas","lastName":"Carter"}}}`
func Parser(data []byte) interface{} {
var i interface{}
json.Unmarshal(data, &i)
return i
}
func Test_Unmarshal(t *testing.T) {
da := Parser([]byte(data))
fmt.Printf("%v\n", da)
}
// go test -v -timeout 30s . -run ^Test_Unmarshal$
// map[employees:map[first:map[firstName:Bill lastName:Gates] second:map[firstName:George lastName:Bush] third:map[firstName:Thomas lastName:Carter]]]
func Benchmark_Unmarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
Parser([]byte(data))
}
}
// go test -benchmem -run=^$ . -bench ^Benchmark_Unmarshal$
// Benchmark_Unmarshal-4 300000 4652 ns/op 2320 B/op 38 allocs/op
func Test_Marshal(t *testing.T) {
da := Parser([]byte(data))
d, _ := json.Marshal(da)
fmt.Println(string(d))
}
// go test -v -timeout 30s . -run ^Test_Marshal$
// {"employees":{"first":{"firstName":"Bill","lastName":"Gates"},"second":{"firstName":"George","lastName":"Bush"},"third":{"firstName":"Thomas","lastName":"Carter"}}}
func Benchmark_Marshal(b *testing.B) {
da := Parser([]byte(data))
for i := 0; i < b.N; i++ { //use b.N for looping
json.Marshal(da)
}
}
// go test -benchmem -run=^$ . -bench ^Benchmark_Marshal$
// Benchmark_Marshal-4 200000 6455 ns/op 2192 B/op 49 allocs/op
func Test_MarshalLevel1(t *testing.T) {
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for k, v := range daJSON {
d, _ := json.Marshal(v)
daJSON[k] = string(d)
}
s, _ := json.Marshal(daJSON)
fmt.Printf("Test_MarshalLevel1 %v\n", string(s))
}
// go test -v -timeout 30s . -run ^Test_MarshalLevel1$
// Test_MarshalLevel1 {"employees":"{\"first\":{\"firstName\":\"Bill\",\"lastName\":\"Gates\"},\"second\":{\"firstName\":\"George\",\"lastName\":\"Bush\"},\"third\":{\"firstName\":\"Thomas\",\"lastName\":\"Carter\"}}"}
func Benchmark_MarshalLevel1(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for k, v := range daJSON {
d, _ := json.Marshal(v)
daJSON[k] = string(d)
}
json.Marshal(daJSON)
}
}
// go test -benchmem -run=^$ . -bench ^Benchmark_MarshalLevel1$
// Benchmark_MarshalLevel1-4 100000 12399 ns/op 4880 B/op 90 allocs/op
func Test_MarshalLevel2(t *testing.T) {
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for k1, v1 := range daJSON {
v1Map := v1.(map[string]interface{})
for k2, v2 := range v1Map {
d, _ := json.Marshal(v2)
v1Map[k2] = string(d)
}
daJSON[k1] = v1Map
}
s, _ := json.Marshal(daJSON)
fmt.Printf("Test_MarshalLevel2 %v\n", string(s))
}
// go test -v -timeout 30s . -run ^Test_MarshalLevel2$
// Test_MarshalLevel2 {"employees":{"first":"{\"firstName\":\"Bill\",\"lastName\":\"Gates\"}","second":"{\"firstName\":\"George\",\"lastName\":\"Bush\"}","third":"{\"firstName\":\"Thomas\",\"lastName\":\"Carter\"}"}}
func Benchmark_MarshalLevel2(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for _, v1 := range daJSON {
v1Map := v1.(map[string]interface{})
for k2, v2 := range v1Map {
d2, _ := json.Marshal(v2)
v1Map[k2] = string(d2)
}
}
json.Marshal(daJSON)
}
}
// go test -benchmem -run=^$ . -bench ^Benchmark_MarshalLevel2$
// Benchmark_MarshalLevel2-4 100000 13297 ns/op 4881 B/op 96 allocs/op
func Test_MarshalLevel3(t *testing.T) {
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for k1, v1 := range daJSON {
v1Map := v1.(map[string]interface{})
for k2, v2 := range v1Map {
v2Map := v2.(map[string]interface{})
for k3, v3 := range v2Map {
d, _ := json.Marshal(v3)
v2Map[k3] = string(d)
}
v1Map[k2] = v2Map
}
daJSON[k1] = v1Map
}
s, _ := json.Marshal(daJSON)
fmt.Printf("Test_MarshalLevel2 %v\n", string(s))
}
// go test -v -timeout 30s . -run ^Test_MarshalLevel3$
// Test_MarshalLevel2 {"employees":{"first":{"firstName":"\"Bill\"","lastName":"\"Gates\""},"second":{"firstName":"\"George\"","lastName":"\"Bush\""},"third":{"firstName":"\"Thomas\"","lastName":"\"Carter\""}}}
func Benchmark_MarshalLevel3(b *testing.B) {
for i := 0; i < b.N; i++ { //use b.N for looping
da := Parser([]byte(data))
daJSON := da.(map[string]interface{})
for k1, v1 := range daJSON {
v1Map := v1.(map[string]interface{})
for k2, v2 := range v1Map {
v2Map := v2.(map[string]interface{})
for k3, v3 := range v2Map {
d, _ := json.Marshal(v3)
v2Map[k3] = string(d)
}
v1Map[k2] = v2Map
}
daJSON[k1] = v1Map
}
json.Marshal(daJSON)
}
}
// go test -benchmem -run=^$ . -bench ^Benchmark_MarshalLevel3$
// Benchmark_MarshalLevel3-4 100000 14319 ns/op 4720 B/op 105 allocs/op
分析一下数据
# 整合上面的 benchmark 的数据 Benchmark_Unmarshal-4 300000 4652 ns/op 2320 B/op 38 allocs/op Benchmark_Marshal-4 200000 6455 ns/op 2192 B/op 49 allocs/op Benchmark_MarshalLevel1-4 100000 12399 ns/op 4880 B/op 90 allocs/op Benchmark_MarshalLevel2-4 100000 13297 ns/op 4881 B/op 96 allocs/op Benchmark_MarshalLevel3-4 100000 14319 ns/op 4720 B/op 105 allocs/op
go test -count=2 -benchtime 3.1s -benchmem . -bench .
小结
以前在写业务代码的时候,记得有一次对接 C#
技术栈的一个服务,其返回的数据格式是本文中提到的 “大于第 x 层的内容转换为字符串” 类似的存在,当时一直想不明白为什么要有这样的设计。现在来看,很可能是开发者把本该用在日志上的规范用到了 api 接口规范。
- 理论上来说,对于 api 接口,规规矩矩地返回 JSON 就好了嘛,就不要玩嵌套了,避免给调用接口的开发者制造不痛快。
- 对于日志来说,那些已知并不会用作精确检索条件的字段(进一步,指那些内容格式不固定的字段)确实转换为字符串更好。
以上,希望对大家有所启发。
参考
- 如何使 Kubernetes 中的应用日志被收集得又快又稳 前面写的博文,介绍了收集 k8s 里应用日志的方案
- GitHub - bitly/go-simplejson: a Go package to interact with arbitrary JSON 封装了 Go 的 json 标准库的一个第三方库
- GitHub - a8m/djson: Fast Go decoder for dynamic JSON 可以与 Go 的 JSON 标准库做对比
- GitHub - buger/jsonparser 以效率著称,可以与 Go 的 JSON 标准库对比
- Benchmark: Retrieving Values from Deep Nested JSON at Golang · tanaike
- Go 测试,go test 工具的具体指令 flag - Deepzz’s Blog 可以参考 go test 的使用方法
- Golang测试用例的困境与最佳实践 - 敬维 早前写的关于 Go 测试用例的思考
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Linux 下三种提高工作效率的文件处理技巧
- 剑指offer题解笔记:时间效率和空间效率的平衡
- Kubernetes云上效率方法论,10倍研发效率提升
- 领导怼程序员:效率高不是不加班的理由!程序员:那降低效率
- 浅谈 “效率” 提升
- 优化ElasticSearch写入效率
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Art of Computer Programming, Volumes 1-3 Boxed Set
Donald E. Knuth / Addison-Wesley Professional / 1998-10-15 / USD 199.99
This multivolume work is widely recognized as the definitive description of classical computer science. The first three volumes have for decades been an invaluable resource in programming theory and p......一起来看看 《The Art of Computer Programming, Volumes 1-3 Boxed Set》 这本书的介绍吧!