Golang中的RPC(转载)

  1. RPC 简介

    1. 远程过程调用(Remote Procedure Call,缩写为 RPC)
    2. 可以将一些比较通用的场景抽象成微服务,然后供其他系统远程调用
    3. RPC 可以基于HTTP协议 也可以基于TCP协议,基于HTTP协议的RPC像是我们访问网页一样(GET/POST/PUT/DELETE/UPDATE),大部分的RPC都是基于TPC协议的(因为基于传输层,效率稍高一些)
    4. 基于TCP 的 RPC 工作过程
      1. 客户端对请求的对象序列化
      2. 客户端连接服务端,并将序列化的对象通过socket 传输给服务端,并等待接收服务端的响应
      3. 服务端收到请求对象后将其反序列化还原客户端的对象
      4. 服务端从请求对象中获取到请求的参数,然后执行对应的方法,得到返回结果
      5. 服务端将其结果序列化并传给客户端,客户端得到响应结果对象后将其反序列化,得到响应结果
  2. Golang中的RPC 注:例子参考 golang实现RPC的几种方式

    1. net/rpc库

      注:没办法在其他语言中调用上面例子实现的RPC方法

      1. 服务端 rpc_server.go

        package main
        import (
          "errors"
          "fmt"
          "log"
          "net"
          "net/http"
          "net/rpc"
          "os"
        )
        // 算数运算结构体
        type Arith struct {
        }
        
        type AirthRequest struct {
          A int
          B int
        }
        
        // 算数运算请求结构体
        type AirthResponse struct {
          Pro int // 成绩
          Quo int // 商
          Rem int // 余数
        }
        
        // 乘法运算方法
        func (this *Arith) Multiply(req AirthRequest, res *AirthResponse) error {
          res.Pro = req.A * req.B
          return nil
        }
        
        // 除法运算
        func (this *Arith) Divide(req AirthRequest, res *AirthResponse) error {
          if req.B == 0 {
            return errors.New("divide by zero")
          }
          res.Quo = req.A / req.B
          res.Rem = req.A % req.B
          return nil
        }
        
        func main() {
          rpc.Register(new(Arith))	// 注册rpc服务
          rpc.HandleHTTP()				//采用http协议作为rpc载体
          lis, err := net.Listen("tcp", "127.0.0.1:8905")
          if err != nil {
            log.Fatalln("fatal error:", err)
          }
          fmt.Println(os.Stdout, "%s", "start connect\n")
          http.Serve(lis, nil)
        }
        
        
      2. 客户端 rpc_client.go

        package main
        
        import (
        	"fmt"
        	"log"
        	"net/rpc"
        )
        
        // 运算请求结构体
        type AirthRequest struct {
        	A int
        	B int
        }
        
        // 运算响应结构体
        type AirthResponse struct {
        	Pro int // 乘积
        	Quo int // 商
        	Rem int // 余数
        }
        
        func main() {
        	conn, err := rpc.DialHTTP("tcp", "127.0.0.1:8905")
        	if err != nil {
        		log.Fatalln("dialing error:", err)
        	}
        	req := AirthRequest{10, 2}
        	var res AirthResponse
        	err = conn.Call("Arith.Multiply", req, &res)
        
        	if err != nil {
        		log.Fatalln("arith error:", err)
        	}
        	fmt.Println("%d * %d = %d\n", req.A, req.B, res.Pro)
        	err = conn.Call("Arith.Divide", req, &res)
        	if err != nil {
        		log.Fatal("arith error:", err)
        	}
        	fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
        }
        
        
    2. net/rpc/jsonrpc库

      注:支持跨语言调用

      1. jsonrpc_server.go

        package main
        
        import (
            "errors"
            "fmt"
            "log"
            "net"
            "net/rpc"
            "net/rpc/jsonrpc"
            "os"
        )
        
        // 算数运算结构体
        type Arith struct {
        }
        
        // 算数运算请求结构体
        type ArithRequest struct {
            A int
            B int
        }
        
        // 算数运算响应结构体
        type ArithResponse struct {
            Pro int // 乘积
            Quo int // 商
            Rem int // 余数
        }
        
        // 乘法运算方法
        func (this *Arith) Multiply(req ArithRequest, res *ArithResponse) error {
            res.Pro = req.A * req.B
            return nil
        }
        
        // 除法运算方法
        func (this *Arith) Divide(req ArithRequest, res *ArithResponse) error {
            if req.B == 0 {
                return errors.New("divide by zero")
            }
            res.Quo = req.A / req.B
            res.Rem = req.A % req.B
            return nil
        }
        
        func main() {
            rpc.Register(new(Arith)) // 注册rpc服务
        
            lis, err := net.Listen("tcp", "127.0.0.1:8096")
            if err != nil {
                log.Fatalln("fatal error: ", err)
            }
        
            fmt.Fprintf(os.Stdout, "%s", "start connection")
        
            for {
                conn, err := lis.Accept() // 接收客户端连接请求
                if err != nil {
                    continue
                }
        
                go func(conn net.Conn) { // 并发处理客户端请求
                    fmt.Fprintf(os.Stdout, "%s", "new client in coming\n")
                    jsonrpc.ServeConn(conn)
                }(conn)
            }
        }
        
      2. jsonrpc_client.go

        package main
        
        import (
            "fmt"
            "log"
            "net/rpc/jsonrpc"
        )
        
        // 算数运算请求结构体
        type ArithRequest struct {
            A int
            B int
        }
        
        // 算数运算响应结构体
        type ArithResponse struct {
            Pro int // 乘积
            Quo int // 商
            Rem int // 余数
        }
        
        func main() {
            conn, err := jsonrpc.Dial("tcp", "127.0.0.1:8096")
            if err != nil {
                log.Fatalln("dailing error: ", err)
            }
        
            req := ArithRequest{9, 2}
            var res ArithResponse
        
            err = conn.Call("Arith.Multiply", req, &res) // 乘法运算
            if err != nil {
                log.Fatalln("arith error: ", err)
            }
            fmt.Printf("%d * %d = %d\n", req.A, req.B, res.Pro)
        
            err = conn.Call("Arith.Divide", req, &res)
            if err != nil {
                log.Fatalln("arith error: ", err)
            }
            fmt.Printf("%d / %d, quo is %d, rem is %d\n", req.A, req.B, res.Quo, res.Rem)
        }
        

GitHub: https://github.com/zhuchenglin/gorpc

posted @ 2020-06-21 16:44  lin_zone  阅读(691)  评论(0编辑  收藏  举报