Golang反射学习:100行代码手写一个RPC

2023年 7月 11日 34.5k 0

golang反射学习后做一个小练习,使用100行代码实现一个通用的RPC服务。

简要说明

golang 的RPC框架还是非常丰富的,比如 gRPC,go-zero, go-dubbo 等都是使用非常普遍的rpc框架。在go语言实现的RPC客户端中,大部分RPC框架采用的是使用生成代码的方式来构建RPC服务。即:定义好相应的接口后,需要通过命令生成相应的代码。采用这种方式的优点在于可以减少不必要的类型转换;而麻烦之处也显而易见,需要在每次结构发生改变时,重新生成对应的代码。那么,如果不采用命令行生成的方式来调用RPC该怎么做呢?经过对golang反射的学习后,让我们用100行代码来小试牛刀,实现一个极简版的RPC。

协议的定义

由于极简,我们采用HTTP协议,数据传输采用最常见的json结构。 服务请求,通过http 请求路径判断调用哪个方法。

  • • 输入参数定义如下: ["参数1", "参数2"] 其中参数1,参数2 采用Json编码,最终的请求参数在做一次编码。 例如:Do("abc", 123) ,其post请求body为 ["\"abc\"", 123]
  • • 输出参数定义与输入参数定义格式相同

如何使用

服务端:

服务端需要实现每一个接口,并把接口绑定到对应的路由上。

package main

import "github.com/lpflpf/rpc"
import "strconv"

func main() {
  serv := rpc.NewRpcServ("127.0.0.1:18080")
  serv.Impl("/conv/int2str", strconv.Itoa) // 路由绑定到方法
  serv.Impl("/conv/str2int", strconv.Atoi)
  serv.Impl("/math/add", func(a, b int) int { return a + b })
  serv.Start()
}

客户端调用

客户端仅需要定义对应的rpc服务的方法,并通过struct tag的方式指定路由即可

package main

import "fmt"
import "github.com/lpflpf/rpc"

type Conv struct {
  Int2Str func(int) string                `rpc:"conv/int2str"`
  Str2Int func(input string) (int, error) `rpc:"conv/str2int"`
}

type Math struct {
  Add func(int, int) int `rpc:"math/add"`
}

func main() {
  conv := &Conv{}
  rpc.Connect("http://127.0.0.1:18080", conv) // 连接RPC 服务
  fmt.Println(conv.Int2Str(123), conv.Int2Str(456)) // 123 456
  fmt.Println(conv.Str2Int("1234")) // 1234 

  math := &Math{}
  rpc.Connect("http://127.0.0.1:18080", math) // 连接 RPC 服务
  fmt.Println(math.Add(1, 2)) // 3
}

Server 端的实现

服务端主要是将注册路由。在处理请求时,需要将请求的数据转化为注册句柄的参数,并将句柄的处理结果编码,并返回给客户端。代码如下:

package rpc

import "net/http"
import "reflect"
import "encoding/json"
import "io/ioutil"

type RpcServ struct {
  serv *http.Server
  mux  *http.ServeMux
}

func (rs *RpcServ) Impl(router string, f interface{}) {
  rs.mux.HandleFunc(router, func(rw http.ResponseWriter, request *http.Request) {
    rt := reflect.TypeOf(f)
    requestBody, _ := ioutil.ReadAll(request.Body)

    requestData := []string{}
    _ = json.Unmarshal(requestBody, &requestData)

    params := []reflect.Value{}

    num := rt.NumIn()
    if rt.IsVariadic() {
      num = num - 1
    }
    for i := 0; i 

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论