Tendermint之go-wire

本文介绍了Tendermint中的go-wire库,它用于将数据按照预定义的格式编码为二进制进行传输,并在接收端解码回结构化数据。内容包括go-wire的初始化、数据写入与读取过程,以及其在接口扩展中的应用。通过RegisterInterface注册类型,然后利用WriteBinary和ReadBinary进行编码解码,实现了高效的数据传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在基于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数组传输,接收方再按照定义的格式还原

至于高效性怎么样后续有机会再研究吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值