-
Notifications
You must be signed in to change notification settings - Fork 338
流量存储
hueng edited this page Apr 29, 2020
·
8 revisions
为了方便检索,线上的流量会经过json.Marshal存入ES,常见存储流量方式有下列两种:
问题:json.Marshal会对[]byte字段进行base64编码,这样在ES里存储的时候就无法全文检索了,至少原生不支持base64解码再分词。
type Session struct {
Request []byte
}
s := &Session{Request: []byte{104, 101, 108, 108, 111, 26}}
res, _ := json.Marshal(s)
fmt.Println(string(res)) // {"Request":"aGVsbG8="}
// string(s.Request) == "hello"
// base64("hello") == "aGVsbG8="
问题:json.Marshal会对string字段做utf8合法性校验,对于非法字符都会转换成\ufffd
,没法区分。
参考:stringBytes
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"}
// 问题:没法区分不合法的utf8字符:239、240
重写 string 的 json 序列化代码,对于不合法的 utf8 字符转码成了 \\x00
格式。
注意有两个 \,因为这个是json转义了的,\x本身并不是被 json 支持。
如果原有的输入里就有 \ 转义的,把 \ 也要做 \x 的转义。
新增函数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),
})
}
合法的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"}
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
}
合法的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]