内容简介:在实践 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写入效率
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First HTML与CSS(第2版)
Elisabeth Robson、Eric Freeman / 徐阳、丁小峰 / 中国电力出版社 / 2013-9 / 98.00元
是不是已经厌倦了那些深奥的HTML书?你可能在抱怨,只有成为专家之后才能读懂那些书。那么,找一本新修订的《Head First HTML与CSS(第2版)》吧,来真正学习HTML。你可能希望学会HTML和CSS来创建你想要的Web页面,从而能与朋友、家人、粉丝和狂热的顾客更有效地交流。你还希望使用最新的HTML5标准,能够保证随时间维护和扩展你的Web页面,使它们在所有浏览器和移动设备中都能正常工......一起来看看 《Head First HTML与CSS(第2版)》 这本书的介绍吧!