zoukankan      html  css  js  c++  java
  • 动手实现一个简单的 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连接这些细节,专注于业务逻辑,而且更为可靠。

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

  • 相关阅读:
    linux系统rewrite重定向及搭建discuz
    nginx客户端请求分类
    linux系统nginx动静分离
    RDB与AOF
    AOF持久化
    redis持久化(RDB)
    redis操作
    redis搭建
    Redis简介
    小技巧从此拒绝$?
  • 原文地址:https://www.cnblogs.com/luke44/p/13267700.html
Copyright © 2011-2022 走看看