Search code examples
node.jsgorpc

Implement RPC client Node.js with server written


I implement RPC server using Go. Now, I want to write the client using Node.js, how can I make RPC call from Node.js client to a Go server.

Here is the code of the server written in Go.

server.go

package main

type HelloService struct{}

func (p *HelloService) Hello(request string, reply *string) error {
    *reply = "Hello " + request
    return nil
}

func main() {
    rpc.RegisterName("HelloService", new(HelloService))

    // run rpc on port 1234
    listener, err := net.Listen("tcp", ":1234")

    if err != nil {
        log.Fatal("ListenTCP error: ", err)
    }

    // use for-while for serve client
    for {
        conn, err := listener.Accept()
        log.Println("New connection: ", conn)

        if err != nil {
            log.Fatal("Accept error: ", err)
        }

        go rpc.ServeConn(conn)
    }
}
 

Solution

  • The net/rpc package uses encoding/gob as the default wire format. It's a Go specific binary serialization format which is efficient, but, in practice, only useful when communicating between Go applications.

    However, net/rpc supports utilizing different codecs for (de)serializing data and the net/rpc/jsonrpc package provides a JSON-RPC 1.0 complaint codec implementation.

    Here's an example of using it, along with a Node.js client which utilizes the jayson package (nothing particular about it, just the first one I stumbled across that supports JSON-RPC 1.0):

    server.go

    package main
    
    import (
            "log"
            "net"
            "net/rpc"
            "net/rpc/jsonrpc"
    )
    
    type HelloService struct{}
    
    func (p *HelloService) Hello(request string, reply *string) error {
            *reply = "Hello " + request
            return nil
    }
    
    func main() {
            rpc.RegisterName("HelloService", new(HelloService))
            listener, err := net.Listen("tcp", ":1234")
            if err != nil {
                    log.Fatal("ListenTCP error: ", err)
            }
            for {
                    conn, err := listener.Accept()
                    if err != nil {
                            log.Fatal("Accept error: ", err)
                    }
                    log.Printf("New connection: %+v\n", conn.RemoteAddr())
                    go jsonrpc.ServeConn(conn)
            }
    }
    

    client.js

    const jayson = require("jayson");
    
    const client = jayson.client.tcp({
            host: "172.17.0.2",
            port: "1234",
            version: 1,
    });
    
    client.request("HelloService.Hello", ["chuckx"], (err, response) => {
            if (err) throw err;
            if (response.error) throw response.error;
            console.log("response:", response.result);
    });
    

    Output

    Server
    $ go run server.go
    2021/03/12 06:46:21 New connection: 172.17.0.3:51016
    
    Client
    $ node client.js
    response: Hello chuckx
    

    Note that the net/rpc package is no longer accepting new features, so you may be better off using something like gRPC for more features and better cross-language compatibility.