RPC communication in GO language

GO language provides the rpc package, which makes the RPC communication becomes pretty easy.

To set up an rpc server, you need to register an object. The methods of the object meet the criteria below can be called remotely. Otherwise, they will be ignored.

  • the method is exported. (The first character of the identifier’s name is a Unicode upper case letter)
  • the method has two arguments, both exported (or builtin) types.
  • the method’s second argument is a pointer.
  • the method has return type error.

In summary, the method signature is like:

func (t *T) MethodName(argType T1, replyType *T2) error

For the arguments, only the exported fields are accessible.

Once the object is registered, the rpc package can know all the exported methods via reflection and make them accessible.

At the client, you can use the string “<TypeName>.<MethodName>” to indicate the method running on server.

Below is an example to demo the RPC communication via TCP. You can also get the example code here: https://github.com/JeffreyZksun/golang/tree/master/rpc.

1         Create RPC server

The RPC server listens the remote request and executes the corresponding method.

The steps to create an rpc server is

  1. Define a class with exported method and new an object.
  2. Register the object to rpc. (rpc.Register)
  3. Start TCP listening. (net.Listen)
  4. Accept a TCP connection. (listener.Accept)
  5. Set the new connection to rpc. (rpc.ServeConn)

The server code is

// server.go
package main

import (
    "net"
    "net/rpc"
    "log"
)

type Args struct {
	X, Y int
}

type Calculator struct{}

func (t *Calculator) Add(args *Args, reply *int) error {
	*reply = args.X + args.Y
	return nil
}

func main(){
    cal := new(Calculator)
    rpc.Register(cal)
    listener, e := net.Listen("tcp", ":1234")
    if e != nil {
        log.Fatal("listen error:", e)
    }
    
    for {
        if conn, err := listener.Accept(); err != nil {
            log.Fatal("accept error: " + err.Error())
        } else {
            log.Printf("new connection established\n")
            go rpc.ServeConn(conn)
        }
    }
}

2         Create the client

The steps to create the RPC client are:

  1. Use the RPC dial to create the TCP connection with the remote server. (Note: This is not a normal net connection) (rpc.Dial)
  2. Call the remote method. (client.Call)

The client code is

// client.go
package main

import (
    "fmt"
    "net/rpc"
    "log"
)

type Args struct {
	X, Y int
}

func main(){

    client, err := rpc.Dial("tcp", "127.0.0.1:1234")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    
    // Synchronous call
    args := &Args{7,8}
    var reply int
    err = client.Call("Calculator.Add", args, &reply)
    if err != nil {
        log.Fatal("arith error:", err)
    }
    fmt.Printf("Result: %d+%d=%d", args.X, args.Y, reply)
}
  • markclarkson

    A good example that ‘just worked’. Saved me some time, thanks!