Golang JSON RPC Services using net/rpc/jsonrpc

RPC or not to RPC

Yesterday I've played with JSON RPC Server and decided that I'll write something about it because didn't found much resources about writing this kind of services in Golang.

As reference I've used post from Michael Crosby http://crosbymichael.com/golang-json-rpc.html and I've tuned it a little to work out of the box on recent Go (1.4).

It's one file example so server is runned in go-routine (will close after execution of last line).

Arith code was borrowed from all_test.go (in net/rpc/jsonrpc lib directory) file for better post readability

Working example (and many others) can be found on my github account:

https://github.com/exu/go-playground/blob/master/47-json-rpc/rpc.go.

package main

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

type Args struct {
    A, B int
}

type Reply struct {
    C int
}

type Arith int

type ArithAddResp struct {
    Id     interface{} `json:"id"`
    Result Reply       `json:"result"`
    Error  interface{} `json:"error"`
}

func (t *Arith) Add(args *Args, reply *Reply) error {
    reply.C = args.A + args.B
    return nil
}

func (t *Arith) Mul(args *Args, reply *Reply) error {
    reply.C = args.A * args.B
    return nil
}

func (t *Arith) Div(args *Args, reply *Reply) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    reply.C = args.A / args.B
    return nil
}

func (t *Arith) Error(args *Args, reply *Reply) error {
    panic("ERROR")
}

func startServer() {
    arith := new(Arith)

    server := rpc.NewServer()
    server.Register(arith)

    l, e := net.Listen("tcp", ":8222")
    if e != nil {
        log.Fatal("listen error:", e)
    }

    for {
        conn, err := l.Accept()
        if err != nil {
            log.Fatal(err)
        }

        go server.ServeCodec(jsonrpc.NewServerCodec(conn))
    }
}

func main() {

    // starting server in go routine (it ends on end
    // of main function
    go startServer()

    // now client part connecting to RPC service
    // and calling methods

    conn, err := net.Dial("tcp", "localhost:8222")

    if err != nil {
        panic(err)
    }
    defer conn.Close()

    c := jsonrpc.NewClient(conn)

    var reply Reply
    var args *Args
    for i := 0; i < 11; i++ {
        // passing Args to RPC call
        args = &Args{7, i}

        // calling "Arith.Mul" on RPC server
        err = c.Call("Arith.Mul", args, &reply)
        if err != nil {
            log.Fatal("arith error:", err)
        }
        fmt.Printf("Arith: %d * %d = %v\n", args.A, args.B, reply.C)

        // calling "Arith.Add" on RPC server
        err = c.Call("Arith.Add", args, &reply)
        if err != nil {
            log.Fatal("arith error:", err)
        }
        fmt.Printf("Arith: %d + %d = %v\n", args.A, args.B, reply.C)

        // NL
        fmt.Printf("\033[33m%s\033[m\n", "---------------")

    }
}

When we run above program, we should receive:

❯ go run rpc.go


Arith: 7 * 0 = 0
Arith: 7 + 0 = 7
---------------
Arith: 7 * 1 = 7
Arith: 7 + 1 = 8
---------------
Arith: 7 * 2 = 14
Arith: 7 + 2 = 9
---------------
Arith: 7 * 3 = 21
Arith: 7 + 3 = 10
---------------
Arith: 7 * 4 = 28
Arith: 7 + 4 = 11
---------------
Arith: 7 * 5 = 35
Arith: 7 + 5 = 12
---------------
Arith: 7 * 6 = 42
Arith: 7 + 6 = 13
---------------
Arith: 7 * 7 = 49
Arith: 7 + 7 = 14
---------------
Arith: 7 * 8 = 56
Arith: 7 + 8 = 15
---------------
Arith: 7 * 9 = 63
Arith: 7 + 9 = 16
---------------
Arith: 7 * 10 = 70
Arith: 7 + 10 = 17
---------------
comments powered by Disqus