在基于Tendermint增加一个已有rpc接口参数的时候,是考虑增加接口还是扩张接口,这两者都比较麻烦
增加接口的话扩展性不强
扩展接口的话也比较麻烦,一整套接口都得改,而且后续扩展性不强
想着在数据的前面加一个byte代表类型,接收方去获取第一个byte判断类型后续为数据,但是这样有点赤裸裸的workaround的感觉了,看到已有的go-wire有类似效果,就是这个想法上的包装,所以看看怎么使用的
go-wire是什么
查了下没查到,看起来是Tendermint自己做的?
它的作用是传输数据的时候按照定义的格式encode为二进制传输,接收端接收到数据后按照定义的格式decode二进制位结构化数据。
初始化
使用之前需要先注册,以Tendermint的mempool为例
var _ = wire.RegisterInterface(
struct{ MempoolMessage }{},
wire.ConcreteType{&TxMessage{}, msgTypeTx},
wire.ConcreteType{&TxMessage{}, msgTypeTx},
)
为了方便演示,我将第3行复制了一遍需要调用wire的RegisterInterface
func RegisterInterface(o interface{}, ctypes ...ConcreteType) *TypeInfo {
it := GetTypeFromStructDeclaration(o)
if it.Kind() != reflect.Interface {
cmn.PanicSanity("RegisterInterface expects an interface")
}
toType := make(map[byte]reflect.Type, 0)
toByte := make(map[reflect.Type]byte, 0)
for _, ctype := range ctypes {
crt := reflect.TypeOf(ctype.O)
typeByte := ctype.Byte
if typeByte == 0x00 {
cmn.PanicSanity(cmn.Fmt("Byte of 0x00 is reserved for nil (%v)", ctype))
}
if toType[typeByte] != nil {
cmn.PanicSanity(cmn.Fmt("Duplicate Byte for type %v and %v", ctype, toType[typeByte]))
}
toType[typeByte] = crt
toByte[crt] = typeByte
}
typeInfo := &TypeInfo{
Type: it,
IsRegisteredInterface: true,
ByteToType: toType,
TypeToByte: toByte,
}
typeInfos[it] = typeInfo
return typeInfo
}
1 获取o的struct type,其实就是获取到了类型MempoolMessage
2 for循环ctypes参数,也就是剩下的其余的参数
拿到ctype的类型crt和值typeByte(该值不能为0,0默认为nil),然后分别加入到map toType和toByte,
组织为TypeInfo返回,同时加入到最后加入到map typesInfos中
所以初始化的目的就是把定义的type和value加入到map typesInfos中, type为key。typeInfos中的元素就是解析好的value,有两个map保存value结构type和value
写入数据
写入数据的时候调用接口BinaryBytes。以上述为例子,写入的参数为
struct{ MempoolMessage }{msg},(其中msg为&TxMessage{Tx: tx})就是下面的参数o
func BinaryBytes(o interface{}) []byte {
w, n, err := new(bytes.Buffer), new(int), new(error)
WriteBinary(o, w, n, err)
if *err != nil {
cmn.PanicSanity(*err)
}
return w.Bytes()
}
接着看WriteBinary
func WriteBinary(o interface{}, w io.Writer, n *int, err *error) {
rv := reflect.ValueOf(o)
rt := reflect.TypeOf(o)
writeReflectBinary(rv, rt, Options{}, w, n, err)
}
这里要获取参数的value和type,分别为msg何MempoolMessage
接下来的writeReflectBinary就是把结构化的数据转成byte数组最终传递出去了
unc writeReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, w io.Writer, n *int, err *error) {
// Get typeInfo
typeInfo := GetTypeInfo(rt)
if rt.Kind() == reflect.Interface {
if rv.IsNil() {
WriteByte(0x00, w, n, err)
return
}
crv := rv.Elem() // concrete reflection value
crt := crv.Type() // concrete reflection type
if typeInfo.IsRegisteredInterface {
// See if the crt is registered.
// If so, we're more restrictive.
typeByte, ok := typeInfo.TypeToByte[crt]
if !ok {
switch crt.Kind() {
case reflect.Ptr:
*err = errors.New(cmn.Fmt("Unexpected pointer type %v for registered interface %v. "+
"Was it registered as a value receiver rather than as a pointer receiver?", crt, rt.Name()))
case reflect.Struct:
*err = errors.New(cmn.Fmt("Unexpected struct type %v for registered interface %v. "+
"Was it registered as a pointer receiver rather than as a value receiver?", crt, rt.Name()))
default:
*err = errors.New(cmn.Fmt("Unexpected type %v for registered interface %v. "+
"If this is intentional, please register it.", crt, rt.Name()))
}
return
}
if crt.Kind() == reflect.Ptr {
crv, crt = crv.Elem(), crt.Elem()
if !crv.IsValid() {
*err = errors.New(cmn.Fmt("Unexpected nil-pointer of type %v for registered interface %v. "+
"For compatibility with other languages, nil-pointer interface values are forbidden.", crt, rt.Name()))
return
}
}
WriteByte(typeByte, w, n, err)
writeReflectBinary(crv, crt, opts, w, n, err)
} else {
// We support writing unregistered interfaces for convenience.
writeReflectBinary(crv, crt, opts, w, n, err)
}
return
}
if rt.Kind() == reflect.Ptr {
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
typeInfo = GetTypeInfo(rt)
if !rv.IsValid() {
WriteByte(0x00, w, n, err)
return
} else {
WriteByte(0x01, w, n, err)
// continue...
}
}
// All other types
switch rt.Kind() {
case reflect.Array:
elemRt := rt.Elem()
length := rt.Len()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Bytearrays
if rv.CanAddr() {
byteslice := rv.Slice(0, length).Bytes()
WriteTo(byteslice, w, n, err)
} else {
buf := make([]byte, length)
reflect.Copy(reflect.ValueOf(buf), rv) // XXX: looks expensive!
WriteTo(buf, w, n, err)
}
} else {
// Write elems
for i := 0; i < length; i++ {
elemRv := rv.Index(i)
writeReflectBinary(elemRv, elemRt, opts, w, n, err)
}
}
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Byteslices
byteslice := rv.Bytes()
WriteByteSlice(byteslice, w, n, err)
} else {
// Write length
length := rv.Len()
WriteVarint(length, w, n, err)
// Write elems
for i := 0; i < length; i++ {
elemRv := rv.Index(i)
writeReflectBinary(elemRv, elemRt, opts, w, n, err)
}
}
case reflect.Struct:
if rt == timeType {
// Special case: time.Time
WriteTime(rv.Interface().(time.Time), w, n, err)
} else {
for _, fieldInfo := range typeInfo.Fields {
fieldIdx, fieldType, opts := fieldInfo.unpack()
fieldRv := rv.Field(fieldIdx)
writeReflectBinary(fieldRv, fieldType, opts, w, n, err)
}
}
case reflect.String:
WriteString(rv.String(), w, n, err)
case reflect.Int64:
if opts.Varint {
WriteVarint(int(rv.Int()), w, n, err)
} else {
WriteInt64(rv.Int(), w, n, err)
}
case reflect.Int32:
WriteInt32(int32(rv.Int()), w, n, err)
case reflect.Int16:
WriteInt16(int16(rv.Int()), w, n, err)
case reflect.Int8:
WriteInt8(int8(rv.Int()), w, n, err)
case reflect.Int:
WriteVarint(int(rv.Int()), w, n, err)
case reflect.Uint64:
if opts.Varint {
WriteUvarint(uint(rv.Uint()), w, n, err)
} else {
WriteUint64(rv.Uint(), w, n, err)
}
case reflect.Uint32:
WriteUint32(uint32(rv.Uint()), w, n, err)
case reflect.Uint16:
WriteUint16(uint16(rv.Uint()), w, n, err)
case reflect.Uint8:
WriteUint8(uint8(rv.Uint()), w, n, err)
case reflect.Uint:
WriteUvarint(uint(rv.Uint()), w, n, err)
case reflect.Bool:
if rv.Bool() {
WriteUint8(uint8(1), w, n, err)
} else {
WriteUint8(uint8(0), w, n, err)
}
case reflect.Float64:
if !opts.Unsafe {
*err = errors.New("Wire float* support requires `wire:\"unsafe\"`")
return
}
WriteFloat64(rv.Float(), w, n, err)
case reflect.Float32:
if !opts.Unsafe {
*err = errors.New("Wire float* support requires `wire:\"unsafe\"`")
return
}
WriteFloat32(float32(rv.Float()), w, n, err)
default:
cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind()))
}
}
1 根据type从typeInfos map中拿到该数据的typeInfo,这个就是初始化的时候RegisterInterface产生的
2 然后判断type(rt)的类型,Interfacer、Ptr、Array、Slice、Struct、String、Int64、Int32、Int16、Int8、Int、Uint64、Uint32、Uint16、Uint8、Bool、Float64、Float32等类型,写成byte数组
读取数据
当收到数据后调用wire的ReadBinary来读取数据
msg = wire.ReadBinary(struct{ MempoolMessage }{}, r, maxMempoolMessageSize, n, &err).(struct{ MempoolMessage }).MempoolMessage
func ReadBinary(o interface{}, r io.Reader, lmt int, n *int, err *error) (res interface{}) {
rv, rt := reflect.ValueOf(o), reflect.TypeOf(o)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
// This allows ReadBinary() to return a nil pointer,
// if the value read is nil.
rvPtr := reflect.New(rt)
ReadBinaryPtr(rvPtr.Interface(), r, lmt, n, err)
res = rvPtr.Elem().Interface()
} else {
readReflectBinary(rv, rt, Options{}, r, lmt, n, err)
res = o
}
} else {
ptrRv := reflect.New(rt)
readReflectBinary(ptrRv.Elem(), rt, Options{}, r, lmt, n, err)
res = ptrRv.Elem().Interface()
}
if lmt != 0 && lmt < *n && *err == nil {
*err = ErrBinaryReadOverflow
}
return res
}
具体实现是在ReadReflectBinary
func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Reader, lmt int, n *int, err *error) {
// Get typeInfo
typeInfo := GetTypeInfo(rt)
if rt.Kind() == reflect.Interface {
if !typeInfo.IsRegisteredInterface {
// There's no way we can read such a thing.
*err = errors.New(cmn.Fmt("Cannot read unregistered interface type %v", rt))
return
}
typeByte := ReadByte(r, n, err)
if *err != nil {
return
}
if typeByte == 0x00 {
return // nil
}
crt, ok := typeInfo.ByteToType[typeByte]
if !ok {
*err = errors.New(cmn.Fmt("Unexpected type byte %X for type %v", typeByte, rt))
return
}
if crt.Kind() == reflect.Ptr {
crt = crt.Elem()
crv := reflect.New(crt)
readReflectBinary(crv.Elem(), crt, opts, r, lmt, n, err)
rv.Set(crv) // NOTE: orig rv is ignored.
} else {
crv := reflect.New(crt).Elem()
readReflectBinary(crv, crt, opts, r, lmt, n, err)
rv.Set(crv) // NOTE: orig rv is ignored.
}
return
}
if rt.Kind() == reflect.Ptr {
typeByte := ReadByte(r, n, err)
if *err != nil {
return
}
if typeByte == 0x00 {
return // nil
} else if typeByte != 0x01 {
*err = errors.New(cmn.Fmt("Unexpected type byte %X for ptr of untyped thing", typeByte))
return
}
// Create new if rv is nil.
if rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
typeInfo = GetTypeInfo(rt)
// continue...
}
switch rt.Kind() {
case reflect.Array:
elemRt := rt.Elem()
length := rt.Len()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Bytearrays
buf := make([]byte, length)
ReadFull(buf, r, n, err)
if *err != nil {
return
}
//log.Info("Read bytearray", "bytes", buf, "n", *n)
reflect.Copy(rv, reflect.ValueOf(buf))
} else {
for i := 0; i < length; i++ {
elemRv := rv.Index(i)
readReflectBinary(elemRv, elemRt, opts, r, lmt, n, err)
if *err != nil {
return
}
if lmt != 0 && lmt < *n {
*err = ErrBinaryReadOverflow
return
}
}
//log.Info("Read x-array", "x", elemRt, "length", length, "n", *n)
}
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Byteslices
byteslice := ReadByteSlice(r, lmt, n, err)
//log.Info("Read byteslice", "bytes", byteslice, "n", *n)
rv.Set(reflect.ValueOf(byteslice))
} else {
var sliceRv reflect.Value
// Read length
length := ReadVarint(r, n, err)
//log.Info("Read slice", "length", length, "n", *n)
sliceRv = reflect.MakeSlice(rt, 0, 0)
// read one ReadSliceChunkSize at a time and append
for i := 0; i*ReadSliceChunkSize < length; i++ {
l := cmn.MinInt(ReadSliceChunkSize, length-i*ReadSliceChunkSize)
tmpSliceRv := reflect.MakeSlice(rt, l, l)
for j := 0; j < l; j++ {
elemRv := tmpSliceRv.Index(j)
readReflectBinary(elemRv, elemRt, opts, r, lmt, n, err)
if *err != nil {
return
}
if lmt != 0 && lmt < *n {
*err = ErrBinaryReadOverflow
return
}
}
sliceRv = reflect.AppendSlice(sliceRv, tmpSliceRv)
}
rv.Set(sliceRv)
}
case reflect.Struct:
if rt == timeType {
// Special case: time.Time
t := ReadTime(r, n, err)
//log.Info("Read time", "t", t, "n", *n)
rv.Set(reflect.ValueOf(t))
} else {
for _, fieldInfo := range typeInfo.Fields {
fieldIdx, fieldType, opts := fieldInfo.unpack()
fieldRv := rv.Field(fieldIdx)
readReflectBinary(fieldRv, fieldType, opts, r, lmt, n, err)
}
}
case reflect.String:
str := ReadString(r, lmt, n, err)
//log.Info("Read string", "str", str, "n", *n)
rv.SetString(str)
case reflect.Int64:
if opts.Varint {
num := ReadVarint(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
} else {
num := ReadInt64(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
}
case reflect.Int32:
num := ReadUint32(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
case reflect.Int16:
num := ReadUint16(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
case reflect.Int8:
num := ReadUint8(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
case reflect.Int:
num := ReadVarint(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetInt(int64(num))
case reflect.Uint64:
if opts.Varint {
num := ReadVarint(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
} else {
num := ReadUint64(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
}
case reflect.Uint32:
num := ReadUint32(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
case reflect.Uint16:
num := ReadUint16(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
case reflect.Uint8:
num := ReadUint8(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
case reflect.Uint:
num := ReadVarint(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetUint(uint64(num))
case reflect.Bool:
num := ReadUint8(r, n, err)
//log.Info("Read bool", "bool", num, "n", *n)
rv.SetBool(num > 0)
case reflect.Float64:
if !opts.Unsafe {
*err = errors.New("Wire float* support requires `wire:\"unsafe\"`")
return
}
num := ReadFloat64(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetFloat(float64(num))
case reflect.Float32:
if !opts.Unsafe {
*err = errors.New("Wire float* support requires `wire:\"unsafe\"`")
return
}
num := ReadFloat32(r, n, err)
//log.Info("Read num", "num", num, "n", *n)
rv.SetFloat(float64(num))
default:
cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind()))
}
}
可以看到这个实现完全就是WriteReflectBinary的反向操作,也确实只能这样做。。。编解码嘛,解码要按照编码的规则来
最终把byte数据还原为定义好的结构
总结
所以go-wire的作用也就是编解码,实现把定义的数据结构转为byte数组传输,接收方再按照定义的格式还原
至于高效性怎么样后续有机会再研究吧