Skip to content

Commit 0fda5aa

Browse files
committed
增加缓存,获得30-40%性能提升,默认开启缓存
1 parent 0afef7c commit 0fda5aa

File tree

10 files changed

+1142
-9
lines changed

10 files changed

+1142
-9
lines changed

filter/bench_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,26 @@ var str string
5050
func BenchmarkUserPointer(b *testing.B) {
5151
user := newUsers()
5252
for i := 0; i < b.N; i++ {
53-
str = SelectMarshal("article", &user).MustJSON()
53+
_ = SelectMarshal("article", &user)
54+
}
55+
}
56+
57+
func BenchmarkUserPointerWithCache(b *testing.B) {
58+
user := newUsers()
59+
for i := 0; i < b.N; i++ {
60+
_ = SelectWithCache("article", &user)
5461
}
5562
}
5663

5764
func BenchmarkUserVal(b *testing.B) {
5865
user := newUsers()
5966
for i := 0; i < b.N; i++ {
60-
str = SelectMarshal("article", user).MustJSON()
67+
_ = SelectMarshal("article", user)
68+
}
69+
}
70+
func BenchmarkUserValWithCache(b *testing.B) {
71+
user := newUsers()
72+
for i := 0; i < b.N; i++ {
73+
_ = SelectWithCache("article", user)
6174
}
6275
}

filter/filter.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,36 @@ func SelectMarshal(selectScene string, el interface{}) Filter {
5252
}
5353
}
5454

55+
// Select 直接返回过滤后的数据结构,相当于直接SelectMarshal后再调用Interface方法
56+
//func Select(selectScene string, el interface{}) interface{} {
57+
// return SelectMarshal(selectScene, el).Interface()
58+
//}
59+
5560
// Select 直接返回过滤后的数据结构,相当于直接SelectMarshal后再调用Interface方法
5661
func Select(selectScene string, el interface{}) interface{} {
62+
if enableCache {
63+
return selectWithCache(selectScene, el).Interface()
64+
}
5765
return SelectMarshal(selectScene, el).Interface()
5866
}
5967

68+
// selectWithCache 直接返回过滤后的数据结构,相当于直接SelectMarshal后再调用Interface方法
69+
func selectWithCache(selectScene string, el interface{}) Filter {
70+
tree := &fieldNodeTree{
71+
Key: "",
72+
ParentNode: nil,
73+
}
74+
tree.ParseSelectValueWithCache("", selectScene, el)
75+
return Filter{
76+
node: tree,
77+
}
78+
}
79+
6080
// Omit 直接返回过滤后的数据结构,相当于直接OmitMarshal后再调用Interface方法
6181
func Omit(omitScene string, el interface{}) interface{} {
82+
if enableCache {
83+
return omitWithCache(omitScene, el).Interface()
84+
}
6285
return OmitMarshal(omitScene, el).Interface()
6386
}
6487

@@ -73,3 +96,20 @@ func OmitMarshal(omitScene string, el interface{}) Filter {
7396
node: tree,
7497
}
7598
}
99+
100+
// omitWithCache 第一个参数填你结构体omit标签里的场景,第二个参数是你需要过滤的结构体对象,如果字段的omit标签里标注的有该场景那么该字段会被过滤掉
101+
func omitWithCache(omitScene string, el interface{}) Filter {
102+
tree := &fieldNodeTree{
103+
Key: "",
104+
ParentNode: nil,
105+
}
106+
tree.ParseOmitValueWithCache("", omitScene, el)
107+
return Filter{
108+
node: tree,
109+
}
110+
}
111+
112+
// EnableCache 决定是否启用缓存,默认开启(强烈建议,除非万一缓存模式下出现bug,可以关闭缓存退回曾经的无缓存过滤模式),开启缓存后会有30%-40%的性能提升,开启缓存并没有副作用,只是会让结构体的字段tag常驻内存减少tag字符串处理操作
113+
func EnableCache(enable bool) {
114+
enableCache = enable
115+
}

filter/omit_encode_cache.go

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
package filter
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
func (t *fieldNodeTree) ParseOmitValueWithCache(key, omitScene string, el interface{}) {
8+
9+
typeOf := reflect.TypeOf(el)
10+
valueOf := reflect.ValueOf(el)
11+
pkgInfo := typeOf.PkgPath()
12+
pkgInfo = pkgInfo + "." + typeOf.Name()
13+
TakePointerValue: //取指针的值
14+
switch typeOf.Kind() {
15+
case reflect.Ptr: //如果是指针类型则取地址重新判断类型
16+
typeOf = typeOf.Elem()
17+
goto TakePointerValue
18+
case reflect.Struct: //如果是字段结构体需要继续递归解析结构体字段所有值
19+
20+
TakeValueOfPointerValue: //这里主要是考虑到有可能用的不是一级指针,如果是***int 等多级指针就需要不断的取值
21+
if valueOf.Kind() == reflect.Ptr {
22+
if valueOf.IsNil() {
23+
t.IsNil = true
24+
//tree.IsNil=true
25+
//t.AddChild(tree)
26+
return
27+
} else {
28+
valueOf = valueOf.Elem()
29+
goto TakeValueOfPointerValue
30+
}
31+
}
32+
33+
if valueOf.CanConvert(timeTypes) { //是time.Time类型或者底层是time.Time类型
34+
t.Key = key
35+
t.Val = valueOf.Interface()
36+
return
37+
}
38+
if typeOf.NumField() == 0 { //如果是一个struct{}{}类型的字段或者是一个空的自定义结构体编码为{}
39+
t.Key = key
40+
t.Val = struct{}{}
41+
return
42+
}
43+
var isAnonymous bool
44+
for i := 0; i < typeOf.NumField(); i++ {
45+
46+
tag, find := tagCache.getTag("", omitScene, pkgInfo, false, typeOf.Field(i).Name, nil)
47+
if !find {
48+
jsonTag, ok := typeOf.Field(i).Tag.Lookup("json")
49+
50+
omitNotTag := true
51+
if !ok {
52+
//tag = newOmitNotTag(omitScene, typeOf.Field(i).Name)
53+
//isAnonymous = typeOf.Field(i).Anonymous
54+
55+
tag, find = tagCache.getTag("", omitScene, pkgInfo, false, typeOf.Field(i).Name, &omitNotTag)
56+
isAnonymous = typeOf.Field(i).Anonymous
57+
} else {
58+
if jsonTag == "-" {
59+
continue
60+
}
61+
//tag = newOmitTag(jsonTag, omitScene, typeOf.Field(i).Name)
62+
omitNotTag = false
63+
tag, find = tagCache.getTag(jsonTag, omitScene, pkgInfo, false, typeOf.Field(i).Name, &omitNotTag)
64+
//isAnonymous = typeOf.Field(i).Anonymous
65+
}
66+
}
67+
68+
if tag.IsOmitField || !tag.IsSelect {
69+
continue
70+
}
71+
isAnonymous = typeOf.Field(i).Anonymous && tag.IsAnonymous ////什么时候才算真正的匿名字段? Book中Article才算匿名结构体
72+
73+
//jsonTag, ok := typeOf.Field(i).Tag.Lookup("json")
74+
////var tag tag
75+
//if !ok {
76+
// tag = newOmitNotTag(omitScene, typeOf.Field(i).Name)
77+
// isAnonymous = typeOf.Field(i).Anonymous
78+
//} else {
79+
// if jsonTag == "-" {
80+
// continue
81+
// }
82+
// tag = newOmitTag(jsonTag, omitScene, typeOf.Field(i).Name)
83+
// if tag.IsOmitField || !tag.IsSelect {
84+
// continue
85+
// }
86+
// isAnonymous = typeOf.Field(i).Anonymous && tag.IsAnonymous ////什么时候才算真正的匿名字段? Book中Article才算匿名结构体
87+
//}
88+
89+
//type Book struct {
90+
// BookName string `json:"bookName,select(resp)"`
91+
// *Page `json:"page,select(resp)"` // 这个不算匿名字段,为什么?因为tag里打了字段名表示要当作一个字段来对待,
92+
// Article `json:",select(resp)"` //这种情况才是真正的匿名字段,因为tag里字段名为空字符串
93+
//}
94+
//
95+
96+
tree := &fieldNodeTree{
97+
Key: tag.UseFieldName,
98+
ParentNode: t,
99+
IsAnonymous: isAnonymous,
100+
}
101+
102+
value := valueOf.Field(i)
103+
TakeFieldValue:
104+
if value.Kind() == reflect.Ptr {
105+
if value.IsNil() {
106+
if tag.Omitempty {
107+
continue
108+
}
109+
tree.IsNil = true
110+
t.AddChild(tree)
111+
continue
112+
} else {
113+
value = value.Elem()
114+
goto TakeFieldValue
115+
}
116+
}
117+
if tag.Omitempty {
118+
if value.IsZero() { //为零值忽略
119+
continue
120+
}
121+
}
122+
123+
tree.ParseOmitValueWithCache(tag.UseFieldName, omitScene, value.Interface())
124+
125+
if t.IsAnonymous {
126+
t.AnonymousAddChild(tree)
127+
} else {
128+
t.AddChild(tree)
129+
}
130+
}
131+
if t.Children == nil && !t.IsAnonymous {
132+
//t.Val = struct{}{} //这样表示返回{}
133+
134+
t.IsAnonymous = true //给他搞成匿名字段的处理方式,直接忽略字段
135+
//说明该结构体上没有选择任何字段 应该返回"字段名:{}"?还是直接连字段名都不显示? 我也不清楚怎么好,后面再说
136+
//反正你啥也不选这字段留着也没任何意义,要就不显示了,至少还能节省一点空间
137+
}
138+
case reflect.Bool,
139+
reflect.String,
140+
reflect.Float64, reflect.Float32,
141+
reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int,
142+
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
143+
144+
if t.IsAnonymous {
145+
tree := &fieldNodeTree{
146+
Key: t.Key,
147+
ParentNode: t,
148+
Val: t.Val,
149+
}
150+
t.AnonymousAddChild(tree)
151+
} else {
152+
t.Val = valueOf.Interface()
153+
t.Key = key
154+
}
155+
156+
case reflect.Map:
157+
takeVMap:
158+
if valueOf.Kind() == reflect.Ptr {
159+
valueOf = valueOf.Elem()
160+
goto takeVMap
161+
}
162+
keys := valueOf.MapKeys()
163+
if len(keys) == 0 { //空map情况下解析为{}
164+
t.Val = struct{}{}
165+
return
166+
}
167+
for i := 0; i < len(keys); i++ {
168+
mapIsNil := false
169+
val := valueOf.MapIndex(keys[i])
170+
takeValMap:
171+
if val.Kind() == reflect.Ptr {
172+
if val.IsNil() {
173+
mapIsNil = true
174+
continue
175+
} else {
176+
val = val.Elem()
177+
goto takeValMap
178+
}
179+
}
180+
k := keys[i].String()
181+
nodeTree := &fieldNodeTree{
182+
Key: k,
183+
ParentNode: t,
184+
}
185+
if mapIsNil {
186+
nodeTree.IsNil = true
187+
t.AddChild(nodeTree)
188+
} else {
189+
nodeTree.ParseOmitValueWithCache(k, omitScene, val.Interface())
190+
t.AddChild(nodeTree)
191+
}
192+
}
193+
194+
case reflect.Slice, reflect.Array:
195+
l := valueOf.Len()
196+
if l == 0 {
197+
t.Val = nilSlice //空数组空切片直接解析为[],原生的json解析空的切片和数组会被解析为null,真的很烦,遇到脾气暴躁的前端直接跟你开撕。
198+
return
199+
}
200+
t.IsSlice = true
201+
for i := 0; i < l; i++ {
202+
sliceIsNil := false
203+
204+
//node := newFieldNodeTree("", t)
205+
node := &fieldNodeTree{
206+
Key: "",
207+
ParentNode: t,
208+
}
209+
val := valueOf.Index(i)
210+
takeValSlice:
211+
if val.Kind() == reflect.Ptr {
212+
if val.IsNil() {
213+
sliceIsNil = true
214+
continue
215+
} else {
216+
val = val.Elem()
217+
goto takeValSlice
218+
}
219+
}
220+
221+
if sliceIsNil {
222+
node.IsNil = true
223+
t.AddChild(node)
224+
} else {
225+
node.ParseOmitValueWithCache("", omitScene, valueOf.Index(i).Interface())
226+
t.AddChild(node)
227+
}
228+
}
229+
}
230+
}

0 commit comments

Comments
 (0)