Обзор
Многие операции на игровом сервере (включая игроков и не игроков) необходимо передавать в мидл-офис компании для сбора, обобщения и анализа данных в соответствии с потребностями операции. Сторона Zhongtai требует, чтобы данные передавались в формате JSON. В начале мы использовали стандартную библиотеку golang.encoding/json
, обнаружили, что производительность неоптимальна (поскольку сериализация использует отражение, которое включает несколько выделений памяти). Поскольку исходный формат данныхmap[string]interface{}
, и мне нужно создать одно поле за другим, поэтому я думаю, что длина конечной строки JSON может быть рассчитана в процессе построения, поэтому требуется только одно выделение памяти.
использовать
скачать:
$ go get github.com/darjun/json-gen
импорт:
import (
jsongen "github.com/darjun/json-gen"
)
Удобнее использовать:
m := jsongen.NewMap()
m.PutUint("key1", 123)
m.PutInt("key2", -456)
m.PutUintArray("key3", []uint64{78, 90})
data := m.Serialize(nil)
data
То есть окончательная сериализованная json String. Конечно, типы могут быть произвольно вложены. См. Кодgithub.
github
Сравните с ним производительность стандартной библиотеки JSON в 10 раз!
Library | Time/op(ns) | B/op | allocs/op |
---|---|---|---|
encoding/json | 22209 | 6673 | 127 |
darjun/json-gen | 3300 | 1152 | 1 |
выполнить
Сначала определите интерфейсValue
, все значения, которые можно сериализовать в JSON, реализуют этот интерфейс:
type Value interface {
Serialize(buf []byte) []byte
Size() int
}
-
Serialize
Вы можете передать выделенную память, и этот метод добавит сериализованную строку JSON значения кbuf
Позади. -
Size
Возвращает количество байтов, которое это значение в конечном итоге займет в строке JSON.
Классификация
Я разделил значения, которые можно сериализовать как строки JSON, на 4 категории:
-
QuotedValue
: необходимо использовать в последней строке"
Обернутое значение, такое как строка в golang. -
UnquotedValue
: не требуется в конечной строке"
обернутое значение, например.uint/int/bool/float32
Ждать. -
Array
: соответствует массиву в формате JSON. -
Map
: соответствует отображению в JSON.
В настоящее время эти 4 вида уже могут удовлетворить мои потребности, да и последующее расширение тоже очень удобно, нужно только реализоватьValue
интерфейс. Согласно следующемуValue
Обсуждаются два интерфейса 4-х типов реализации.
QuotedValue
Нижняя часть основана наstring
определение типаQuotedValue
:
type QuotedValue string
из-заQuotedValue
В конце концов в строке JSON будет 2"
, поэтому его размер: длина + 2. Давайте посмотримSerialize
а такжеSize
Реализация метода:
func (q QuotedValue) Serialize(buf []byte) []byte {
buf = append(buf, '"')
buf = append(buf, []byte(q)...)
return append(buf, '"')
}
func (q QuotedValue) Size() int {
return len(q) + 2
}
UnquotedValue
То же самое основано наstring
определение типаUnquotedValue
:
type UnquotedValue string
а такжеQuotedValue
разница в том,UnquotedValue
ненужный"
пакет,Serialize
а такжеSize
Array
Array
[]Value
Array
type Array []Value
Array
,
[]
Size
func (a Array) Size() int {
size := 0
for _, e := range a {
// 递归求元素的大小
size += e.Size()
}
// for []
size += 2
if len(a) > 1 {
// for ,
size += len(a) - 1
}
return size
}
Serialize
Serialize
,
[]
func (a Array) Serialize(buf []byte) []byte {
if len(buf) == 0 {
// 如果未传入分配好的空间,根据 Size 分配空间
buf = make([]byte, 0, a.Size())
}
buf = append(buf, '[')
count := len(a)
for i, e := range a {
buf = e.Serialize(buf)
if i != count-1 {
// 除了最后一个元素,每个元素后添加,
buf = append(buf, ',')
}
}
return append(buf, ']')
}
Array/Map
AppendType
AppendTypeArray
Type
uint/int/bool/float/Array/Map
string/Array/Map
strconv
UnquotedValue
"
func (a *Array) AppendUint(u uint64) {
value := strconv.FormatUint(u, 10)
*a = append(*a, UnquotedValue(value))
}
func (a *Array) AppendString(value string) {
*a = append(*a, QuotedValue(escapeString(value)))
}
func (a *Array) AppendUintArray(u []uint64) {
value := make([]Value, 0, len(u))
for _, v := range u {
value = append(value, UnquotedValue(strconv.FormatUint(v, 10)))
}
*a = append(*a, Array(value))
}
func (a *Array) AppendStringArray(s []string) {
value := make([]Value, 0, len(s))
for _, v := range s {
value = append(value, QuotedValue(escapeString(v)))
}
*a = append(*a, Array(value))
}
Append*
Array
Map
Map
map[string]Value
map
Map
type Map struct {
keys []string
values []Value
}
Map
{}
"
:
,
Size
func (m Map) Size() int {
size := 0
for i, key := range m.keys {
// +2 for ", +1 for :
size += len(key) + 2 + 1
size += m.values[i].Size()
}
// +2 for {}
size += 2
if len(m.keys) > 1 {
// for ,
size += len(m.keys) - 1
}
return size
}
Serialize
func (m Map) Serialize(buf []byte) []byte {
if len(buf) == 0 {
buf = make([]byte, 0, m.Size())
}
buf = append(buf, '{')
count := len(m.keys)
for i, key := range m.keys {
buf = append(buf, '"')
buf = append(buf, []byte(key)...)
buf = append(buf, '"')
buf = append(buf, ':')
buf = m.values[i].Serialize(buf)
if i != count-1 {
buf = append(buf, ',')
}
}
return append(buf, '}')
}
Array
Map
Map
Array/Map
PutType
PutTypeArray
Type
uint/int/bool/float/Array/Map
func (m *Map) put(key string, value Value) {
m.keys = append(m.keys, key)
m.values = append(m.values, value)
}
func (m *Map) PutUint(key string, u uint64) {
value := strconv.FormatUint(u, 10)
m.put(key, UnquotedValue(value))
}
func (m *Map) PutUintArray(key string, u []uint64) {
value := make([]Value, 0, len(u))
for _, v := range u {
value = append(value, UnquotedValue(strconv.FormatUint(v, 10)))
}
m.put(key, Array(value))
}