rpcx传输struct中一个字段为interface{},客户传入为struct,服务端接收变为map[string]interface{} ---- rpcx编解码问题

博客探讨了在rpcx中,当struct中包含一个`interface{}`字段,客户端传入struct,服务端接收到的是`map[string]interface{}`的问题。分析了可能的原因,发现官方默认编解码器会将`interface{}`转成`map[string]interface{}`,导致处理困难。目前无解,建议限制传入数据类型为已知struct或`map[string]interface{}`。同时提到了如何自定义编码器以应对这个问题。

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

问题描述:

有一个结构体作为rpc传输用的结构体:

type RpcDataArgs struct {
	Typ       string
	Time      string
	DJV       interface{}
	Action    string //tuple_info 是否阻断
}

其中DJVinterface{},实例化一个结构体:

rpcargs := RpcDataArgs{Typ: typ, DJV: osinfo}   //osinfo是一个结构体
rpcCall(rpcargs)

传输之后服务端处理:

func process(args *RpcDataArgs) error {
	d := args.DJV
	c:=reflect.TypeOf(d).String()
	osinfo, ok := d.(OsInfo)        //本来计划进行正常的类型转化
	fmt.Println(c,ok)
}

打印结果为:map[string]interface {},false

***问题:***传入的是一个结构体,怎么拿到的成了map

寻因

查看rpc官方文档的编解码部分:https://doc.rpcx.io/part3/codec.html

在没有自己选择的情况下,官方默认的编解码是MsgPack

编码器问题:会把interface{}转换成map[string]interface{},原因暂未知,可能是由这个编码器为了跨语言而做的处理(interface{}是golang独有的,而map是所有语言通用的)

致命问题:把struct 转换成map[string]interface {}后,要使用mao中元素,还需要再转。直接用会报错。

package main

import "fmt"
import "reflect"

import "github.com/vmihailenco/msgpack"

func ExampleMarshal() {
	type FFFFF struct {
		Name string
		Age  int
		QQ   []int
		AAA  map[int]int
	}
	pp := FFFFF{
		Name: "fsdf",
		Age:  14,
		QQ:   []int{2, 1},
		//		AAA: map[int]int{
		//			1: 3,
		//		},
	}

	type Item struct {
		Foo string
		TTT int
		HHH float64
		KKK map[string]int
		NNN []int
		MMM interface{}
	}

	b, err := msgpack.Marshal(&Item{MMM: pp, Foo: "bar", TTT: 76, HHH: 78.1, KKK: map[string]int{"Fds": 23}, NNN: []int{2, 3, 2}})
	if err != nil {
		panic(err)
	}

	var item Item
	err = msgpack.Unmarshal(b, &item)
	if err != nil {
		panic(err)
	}

	fmt.Println(reflect.TypeOf(item.MMM).String())
	//	fmt.Println(reflect.TypeOf(item.MMM.Name).String())
	//	fmt.Println(reflect.TypeOf(item.MMM.QQ).String())
	//	fmt.Println(reflect.TypeOf(item.MMM.Age).String())
	fmt.Println(item.Foo)
	fmt.Println(reflect.TypeOf(item.Foo).String())
	fmt.Println(item.TTT)
	fmt.Println(reflect.TypeOf(item.TTT).String())
	fmt.Println(item.HHH)
	fmt.Println(reflect.TypeOf(item.HHH).String())
	fmt.Println(item.KKK)
	fmt.Println(reflect.TypeOf(item.KKK).String())
	fmt.Println(item.NNN)
	fmt.Println(reflect.TypeOf(item.NNN).String())
}

func main() {
	ExampleMarshal()
}

结论:

该问题暂未解决,自己选择别的编码器也是一样的结果。
无法做到任意兼容性

暂时的解决方法:
传入的数据类型是确定的结构体类型或者是map[string]interface {}等

type RpcDataArgs struct {
	Typ       string
	Time      string
	DJV       osinfo   //osinfo是结构体类型
	Action    string //tuple_info 是否阻断
}

或者:
type RpcDataArgs struct {
Typ string
Time string
DJV map[string]interface {} //比较麻烦,服务端还需要转换类型
Action string //tuple_info 是否阻断
}

拓展

(如何自己选择编码器)

要在客户端进行设置(注释部分):

import (

	//"github.com/smallnest/rpcx/protocol"

)
func datainit(){
	d := client.NewPeer2PeerDiscovery("tcp@" + *addr, "")
	opt := client.DefaultOption
	opt.ConnectTimeout = RPCConnTimeout
	//opt.SerializeType = protocol.JSON      //选择编码器,默认msgpack
	xclient = client.NewXClient("Arith", client.Failfast, client.RandomSelect, d, opt)
}

rpcx提供的可选择编码器(编码器具体内容可参考文档)

const (
	// SerializeNone uses raw []byte and don't serialize/deserialize
	SerializeNone SerializeType = iota
	// JSON for payload.
	JSON
	// ProtoBuffer for payload.
	ProtoBuffer
	// MsgPack for payload
	MsgPack
	// Thrift
	// Thrift for payload
	Thrift
)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值