zoukankan      html  css  js  c++  java
  • [Go] 分析proto序列化每个字段大小

    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
    ", f.Tag)
        }
        str = str + fmt.Sprintf("total	%8d
    sum		%8d
    ", f.Total, f.Accumulate)
    
        n := 0
        for k, v := range f.Fields {
            //str += fmt.Sprintf("%-16s	%-16s	count:%10d	size:%10d
    ", v.Name, v.Type, v.Count, v.Size)
            n += v.Size
            str += fmt.Sprintf("%2d %-16s	 %-28s	count	%4d	size	%6d	flat%%	%.2f%%	sum	%6d	sum%%	%.2f%%
    ", 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的统计也加进去

  • 相关阅读:
    mysql 数据库【目录】
    Django 模板层
    Django文件下载(通过反向解析)
    Django 的路由系统
    Linux 搭建Django环境 + nginx + virtualenv虚拟环境
    layui 框架之秒传文件 (前端分段 MD5 型成秒传)
    Bootstrap 使用小点总结
    Django 之数据表操作
    前端之旅【目录】
    学习中遇到的小坑坑
  • 原文地址:https://www.cnblogs.com/mrblue/p/13558193.html
Copyright © 2011-2022 走看看