【Go】Golang使用反射实现RPC函数调用

1、前言

在使用gRPC时发现每次新起一个业务都需要改到proto文件新增rpc方法
为了减少这一步操作,于是想到用反射实现一个简单的服务器函数调用
后续开发简单的调用时便不用改到proto文件了

golang版本:1.21.11

2、允许函数的参数类型

bool
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
float32
float64
string
byte
[]byte

3、想要调用的目标函数

// 本次要调用的函数
func TestReflectFunc1(a1 bool, a2 int, a3 int8, a4 int16, a5 int32, a6 int64, a7 uint, a8 uint8, a9 uint16, a10 uint32, a11 uint64, a12 float32, a13 float64, a14 string, a15 byte,
	b []byte) {
	fmt.Println("a1:", a1)
	fmt.Println("a2:", a2)
	fmt.Println("a3:", a3)
	fmt.Println("a4:", a4)
	fmt.Println("a5:", a5)
	fmt.Println("a6:", a6)
	fmt.Println("a7:", a7)
	fmt.Println("a8:", a8)
	fmt.Println("a9:", a9)
	fmt.Println("a10:", a10)
	fmt.Println("a11:", a11)
	fmt.Println("a12:", a12)
	fmt.Println("a13:", a13)
	fmt.Println("a14:", a14)
	fmt.Println("a15:", a15)
	var t *TestBody
	err := json.Unmarshal(b, &t)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("t:", t)
}

4、定义用于反射的map集

var (
	fnMap = map[string]interface{}{}
)

// 需要在调用之前加载一遍所有需要反射的函数
func load() {
	register(TestReflectFunc1)
}

// 检测注册的函数是否正确
func register(f any) {
	fn := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
	ft := reflect.TypeOf(f)
	if ft.Kind() != reflect.Func {
		return
	}
	fnMap[fn] = f
}

5、函数调用

这里只展示了数据编码到byte[],传输过程中的服务通信不在案例内容中,需要自行实现

// Call 函数调用
func Call[T any](f T, args ...any) {
	fn := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
	ft := reflect.TypeOf(f)
	if ft.Kind() != reflect.Func {
		return
	}
	numArgs := ft.NumIn()
	if numArgs != len(args) {
		return
	}
	pack := TelecomPack{
		FuncName: fn,
		Args:     args,
	}
	datas, err := encode(pack)
	if err != nil {
		fmt.Println(err)
		return
	}

	// TODO 将编码后的byte数组发送到对应服务器
	// TODO 对应服务器收到后调用ReqServerTelecom函数
	err = ReqServerTelecom(datas)
	if err != nil {
		fmt.Println(err)
	}
}

6、函数接收

代码中一长串的类型转化,是由于解码后会将类型转化为int64和float64,若需要转化成其他类型,则需要增加更多的判断,若是有更好的转换方法,请多指教

// ReqServerTelecom 请求到具体服务器
func ReqServerTelecom(data []byte) (err error) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("处理 ReqServerTelecom 服务器间协议发生错误 err ", r)
		}
	}()
	pack, err := decode(data)
	if err != nil {
		return
	}
	ff := fnMap[pack.FuncName]
	val := reflect.ValueOf(ff)
	if val.Kind() != reflect.Func {
		return
	}

	ft := reflect.TypeOf(ff)
	if ft.Kind() != reflect.Func {
		return
	}
	numArgs := ft.NumIn()
	callArgs := make([]reflect.Value, 0)
	for i := 0; i < numArgs; i++ {
		argType := ft.In(i)
		// 由于解码后数据类型会丢失原本定义的类型,int8会被转成int64,所以需要手动转回来
		if argType.Kind() >= reflect.Int && argType.Kind() <= reflect.Uint64 {
			if argType.Kind() == reflect.Int {
				callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(int)))
			} else if argType.Kind() == reflect.Int8 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(int8(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(int8)))
				}
			} else if argType.Kind() == reflect.Int16 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(int16(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(int16)))
				}
			} else if argType.Kind() == reflect.Int32 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(int32(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(int32)))
				}
			} else if argType.Kind() == reflect.Int64 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(int64(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(int64)))
				}
			} else if argType.Kind() == reflect.Uint {
				callArgs = append(callArgs, reflect.ValueOf(uint(pack.Args[i].(int))))
			} else if argType.Kind() == reflect.Uint8 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(uint8(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(uint8)))
				}
			} else if argType.Kind() == reflect.Uint16 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(uint16(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(uint16)))
				}
			} else if argType.Kind() == reflect.Uint32 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(uint32(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(uint32)))
				}
			} else if argType.Kind() == reflect.Uint64 {
				if v, ok := pack.Args[i].(int); ok {
					callArgs = append(callArgs, reflect.ValueOf(uint64(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(uint64)))
				}
			} else {
				callArgs = append(callArgs, reflect.ValueOf(pack.Args[i]))
			}
		} else if argType.Kind() == reflect.Float32 || argType.Kind() == reflect.Float64 {
			if argType.Kind() == reflect.Float32 {
				if v, ok := pack.Args[i].(float64); ok {
					callArgs = append(callArgs, reflect.ValueOf(float32(v)))
				} else {
					callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(float32)))
				}
			} else if argType.Kind() == reflect.Float64 {
				callArgs = append(callArgs, reflect.ValueOf(pack.Args[i].(float64)))
			} else {
				callArgs = append(callArgs, reflect.ValueOf(pack.Args[i]))
			}
		} else {
			callArgs = append(callArgs, reflect.ValueOf(pack.Args[i]))
		}
	}
	// 类型转化结束,最终进行方法反射进入
	// 若是需要双向,则需要接收Call的返回进行处理
	val.Call(callArgs)
	return
}

7、编码解码

type TelecomPack struct {
	FuncName string
	Args     []any
}

func encode(data TelecomPack) ([]byte, error) {
	var buf bytes.Buffer
	bufEnc := gob.NewEncoder(&buf)
	if err := bufEnc.Encode(data); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func decode(b []byte) (TelecomPack, error) {
	buf := bytes.NewBuffer(b)
	bufDec := gob.NewDecoder(buf)
	var data TelecomPack
	if err := bufDec.Decode(&data); err != nil {
		return data, err
	}
	return data, nil
}

8、测试

func main() {
	// 在调用前需要先注册好对应的函数名以及函数
	load()

	t := &TestBody{
		A1:  []bool{true},
		A2:  []int{2},
		A3:  []int8{3},
		A4:  []int16{4},
		A5:  []int32{5},
		A6:  []int64{6},
		A7:  []uint{7},
		A8:  []uint8{8},
		A9:  []uint16{9},
		A10: []uint32{10},
		A11: []uint64{11},
		A12: []float32{12},
		A13: []float64{13},
		A14: []string{"14"},
		A15: []byte{15},
		A17: map[string]int{"a1": 1, "a2": 2, "a3": 3},
		A18: map[int]string{1: "a1", 2: "a2", 3: "a3"},
		A19: map[string]float64{"a1": 1.1, "a2": 2.2, "a3": 3.3},
		T: &TestBody{
			A17: map[string]int{"a4": 4, "a5": 5, "a6": 6},
			A18: map[int]string{4: "a4", 5: "a5", 6: "a6"},
			A19: map[string]float64{"a4": 4.4, "a5": 5.5, "a6": 6.6},
		},
	}
	b, _ := json.Marshal(t)
	// 服务端调用方法TestReflectFunc1
	Call(TestReflectFunc1,
		true, math.MaxInt, math.MaxInt8, math.MaxInt16, math.MaxInt32,
		math.MaxInt64, math.MaxInt, math.MaxUint8, math.MaxUint16, math.MaxUint32,
		math.MaxInt64, math.MaxFloat32, math.MaxFloat64, "1234567890", byte(255), b)
}

最终输出结果

a1: true
a2: 9223372036854775807
a3: 127
a4: 32767
a5: 2147483647
a6: 9223372036854775807
a7: 9223372036854775807
a8: 255
a9: 65535
a10: 4294967295
a11: 9223372036854775807
a12: 3.4028235e+38
a13: 1.7976931348623157e+308
a14: 1234567890
a15: 255
t: &{[true] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] map[a1:1 a2:2 a3:3] map[1:a1 2:a2 3:a3] map[a1:1.1 a2:2.2 a3:3.3] 0xc00013c9c0}

学艺不精,请多多指教

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值