动手实现一个简单的 rpc 框架到入门 grpc(上)

rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法。我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机、不同语言中的方法时就需要借助 rpc 来实现,下面我一步步实现一个简单的 rpc 调用。

server 端注册函数,运行并接收客户端请求

func main() {
    srv := NewServer()
    srv.Register("fn", fn)
    srv.Run()
}
//为了简单,这里只需要接收到消息打印出就代表执行成功
func fn(args ...interface{}) {
    fmt.println(args)
}

定义请求格式

type rpcData struct {
    Name string         //函数名
    Args []interface{}  //参数
}

server 运行起来后,接收 socket 请求,解析消息调用已注册的函数

//server结构体
type server struct {
    conn net.Conn                   //socket连接
    maps map[string]reflect.Value   //函数字典
}
//构造函数
func NewServer() *server {
    return &server{
        maps: make(map[string]reflect.Value),
    }
}
//注册函数
func (s *server) Register(fname string, fun interface{}) {
    if _, ok := s.maps[fname]; !ok {
        s.maps[fname] = reflect.ValueOf(fun)
    }
}
//运行一个socket接收请求
func (s *server) Run() {
    listen, err := net.Listen("tcp4", ":3001")
    if err != nil {
        panic(err)
    }
    for {
        s.conn, err = listen.Accept()
        if err != nil {
            continue
        }
        go s.handleConnect()
    }
}

处理请求时,这里为了简单我使用 json 解析,同时需要定义一个简单的协议:客户端发送时,前4个字节放置消息长度,这样服务端接收到时就能知道消息的长度,从而正常解码消息

func (s *server) handleConnect() {
    for {
        header := make([]byte, 4)
        if _, err := s.conn.Read(header); err != nil {
            continue
        }
        bodyLen := binary.BigEndian.Uint32(header)
        body := make([]byte, int(bodyLen))
        if _, err := s.conn.Read(body); err != nil {
            continue
        }
        var req rpcData
        if err := json.Unmarshal(body, &req); err != nil {
            continue
        }
        inArgs := make([]reflect.Value, len(req.Args))
        for i := range req.Args {
            inArgs[i] = reflect.ValueOf(req.Args[i])
        }
        fn := s.maps[req.Name]
        fn.Call(inArgs)
    }
}

client 端只需调用函数,通过网络发送请求

func main() {
    var req = rpcData{"fn", []interface{}{1, "aaa"}}
    rpcCall(req)
}

func rpcCall(data rpcData) {
    conn, err := net.Dial("tcp4", "127.0.0.1:3001")
    if err != nil {
        panic(err)
    }
    req, err := json.Marshal(data)
    if err != nil {
        panic(err)
    }
    buf := make([]byte, 4+len(req))
    binary.BigEndian.PutUint32(buf[:4], uint32(len(req)))
    copy(buf[4:], req)
    _, err = conn.Write(buf)
    if err != nil {
        panic(err)
    }
}

测试时,首先运行 server,然后运行 client,只要看到正确的打印就代表调用成功,这就是一个最简单(简陋)的 rpc 了。

当我们使用 grpc 这些 rpc 框架时,就可以不用自己实现消息编码解码、socket连接这些细节,专注于业务逻辑,而且更为可靠。

参考: github.com/ankur-anand/simple-go-r...

原文:www.cnblogs.com/luke44/p/13267700....

rpc
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!