-
Notifications
You must be signed in to change notification settings - Fork 346
流量存储
hueng edited this page Apr 16, 2020
·
8 revisions
为了方便检索,线上的流量会经过json.Marshal存入ES,传统方式json.Marshal会存在以下问题:
- 示例
type Session struct {
Request []byte
}
s := &Session{Request: []byte{104, 101, 108, 108, 111, 26}} // string(s.Request) == "hello"
res, _ := json.Marshal(s)
fmt.Println(string(res)) // {"Request":"aGVsbG8="}
- 问题
json.Marshal会对[]byte字段进行base64编码,「如上示例:base64("hello") == "aGVsbG8="
」,这样在ES里存储的时候就无法全文检索了,至少原生不支持base64解码再分词。
- 示例
type Session struct {
Request string
}
s := &Session{Request: string([]byte{104, 101, 108, 108, 111, 239, 240})}
res, _ := json.Marshal(s)
fmt.Println(string(res)) // {"Request":"hello\ufffd\ufffd"}
- 问题
json.Marshal会对string字段做utf8合法性校验,对于非法字符会转换成\ufffd
,如上面示例,我们没法区分不合法的utf8字符239
和240
。
重写 string 的 json 序列化代码,对于不合法的 utf8 字符转码成了 \\x00
格式「注意有两个 \,因为这个是json转义了的,\x本身并不是被 json 支持」。如果原有的输入里就有 \ 转义的,把 \ 也做了 \x 的转义。
- 1.1、重写MarshalJSON方法
新增函数EncodeAnyByteArray转义不合法的 utf8 字符。
type Session struct {
Request []byte
}
func (s *Session) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Session
Request json.RawMessage
}{
Session: *s,
Request: EncodeAnyByteArray(s.Request),
})
}
- 1.2、Marshal示例
合法的utf8字符不会经过base64编码方便检索,合法的utf8字符也不会出现信息丢失。
s := &Session{Request: []byte{104, 101, 108, 108, 111, 239, 240}}
res, _ := json.Marshal(s)
fmt.Println(string(res)) // {"Request":"hello\\xef\\xf0"}
- 2.1、重写UnmarshalJSON方法
type Session struct {
Request Raw
}
type Raw struct {
Data []byte
}
func (r *Raw) UnmarshalJSON(data []byte) error {
// step1: unquote string
tmp, err := strconv.Unquote(string(data))
if err != nil {
return err
}
// step2: stripcslashes:(把 \x 解开)才能取到真正原始的 []byte,参考php stripcslashes方法
r.Data = StripcSlashes([]byte(tmp))
return nil
}
- 2.2、Unmarshal示例
合法的utf8字符不会经过base64编码方便检索,合法的utf8字符也不会出现信息丢失。
str := `{"Request":"hello\\xef\\xf0"}` // 新方案json Marshal之后的字符串
var s Session
json.Unmarshal([]byte(str), &s)
fmt.Println(s.Request.Data) // 还原原始byte数组,[104 101 108 108 111 239 240]