0x0 前言
項目的消息包發的proto的二進制流,遇到的問題是有個別消息包特別大。這里分享一個分析工具
0x1 golang代碼
package util import ( "fmt" "reflect" "github.com/golang/protobuf/proto" ) //------------------------------------------------------ // 分析proto結構,計算每個字段序列化后大小 //------------------------------------------------------ // fieldInfo 字段信息 type fieldInfo struct { Name string // 字段名字 Type string // 字段類型 Count int // 個數(map和slice可能是多個) Size int // pb序列化成后的大小 } // ProtoStructInfo // pb結構體信息 type ProtoStructInfo struct { Tag string // 自定義標記 Total int // 序列化后總大小 Accumulate int // 每個字段序列化后累加大小 Fields fields // 每個字段信息 } type fields []fieldInfo // sortFunc quick sort func sortFunc(f fields, i, j int) { if i >= j { return } b, e := i, j key := f[i] for i < j { for i < j && key.Size >= f[j].Size { j-- } if i < j { f[i] = f[j] } for i < j && key.Size <= f[i].Size { i++ } if i < j { f[j] = f[i] } } f[i] = key sortFunc(f, b, i-1) sortFunc(f, i+1, e) } func (f fields) sort() { if 0 == len(f) { return } sortFunc(f, 0, len(f)-1) } // String 打印字符串 func (f ProtoStructInfo) String() string { str := "" if f.Tag != "" { str = fmt.Sprintf("%s\n", f.Tag) } str = str + fmt.Sprintf("total\t%8d\nsum\t\t%8d\n", f.Total, f.Accumulate) n := 0 for k, v := range f.Fields { //str += fmt.Sprintf("%-16s\t%-16s\tcount:%10d\tsize:%10d\n", v.Name, v.Type, v.Count, v.Size) n += v.Size str += fmt.Sprintf("%2d %-16s\t %-28s\tcount\t%4d\tsize\t%6d\tflat%%\t%.2f%%\tsum\t%6d\tsum%%\t%.2f%%\n", k, v.Name, v.Type, v.Count, v.Size, 100*float32(v.Size)/float32(f.Accumulate), n, 100*float32(n)/float32(f.Accumulate)) } return str } // AnalyseProto 分析proto結構 func AnalyseProto(ptr proto.Message) ProtoStructInfo { ret := ProtoStructInfo{ Fields: make([]fieldInfo, 0), } // 計算總大小 if b, err := proto.Marshal(ptr); nil != err { panic(err) } else { ret.Total = len(b) } v := reflect.Indirect(reflect.ValueOf(ptr)) t := v.Type() for i := 0; i < v.NumField(); i++ { f := v.Field(i) tt := t.Field(i) // 只累計這幾個類型,其他類型沒法單獨計算大小 if f.Kind() != reflect.Ptr && f.Kind() != reflect.Map && f.Kind() != reflect.Slice { continue } if f.IsNil() { continue } n := 0 // 字段大小 count := 0 // 字段個數 if f.Kind() == reflect.Ptr { if m, ok := f.Interface().(proto.Message); !ok { continue } else { b, err := proto.Marshal(m) if nil != err { panic(err) } n = len(b) count++ } } else if f.Kind() == reflect.Map { for it := f.MapRange(); it.Next(); { count++ if m, ok := it.Value().Interface().(proto.Message); !ok { continue } else { b, err := proto.Marshal(m) if nil != err { panic(err) } n += len(b) } } } else if f.Kind() == reflect.Slice { for i := 0; i < f.Len(); i++ { ff := f.Index(i) count++ if m, ok := ff.Interface().(proto.Message); !ok { break } else { b, err := proto.Marshal(m) if nil != err { panic(err) } n += len(b) } } } if 0 == n && 0 == count { continue } fi := fieldInfo{ Name: tt.Name, Type: f.Type().String(), Count: count, Size: n, } ret.Fields = append(ret.Fields, fi) ret.Accumulate += n } ret.Fields.sort() return ret }
0x2 使用方法
fmt.Println(util.AnalyseProto(&pb.User{Name:1,ID:2}))
0x3 問題
1. 沒有統計基本類型
2. 可以把string的統計也加進去