RPC with Go, what is it?
Tags: go, rpc
Published: 2021-07-28 14:18:33

OK, so we're going to have a look at RPC with Go. So what is RPC? RPC (remote procedure call) is a distributed computing concept where a call and response mechanism applies to the execution of a function to achieve an outcome and receive a result. 

## Say what?

But what does that mean? Well, we could look at it as follows: 
- We have a client who preps a function name and the arguments required to be sent with it. 
- An RPC server hosting this function receives this.
- The server executes the remote process request.
- The server responds to the client with the outcome and result.
- the client receives the response _(outcome)_ and data _(result)_ and goes about its business merrily on to the next task. 

## OK. Show me in Go

![morpheus show me](https://nerdheist.com/wp-content/uploads/2017/09/post-38723-Morpheus-show-me-gif-Imgur-KFiG-1.gif)

### Prerequisites
- The server needs to expose the service. This is needed for the client to call and connect to the service. 
- go provides two libraries of interest `net/rpc` and `net/jsonrpc`.

We're going to have a project that contains two sub-projects, so let's create a folder for the project and within it create two subfolders, `client` and `server`. 

Ideally we now have a structure that looks like the following:
```
rpc-banner-demo
├── client
│   └── main.go
└── server
    └── main.go
```

### The RPC server 

We're going to create an RPC server with a function that returns a message that could be used as a banner message or some other purpose for which you need to return a string. 

```go
package main

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

// an RPC server in Go

type Args struct{}

type BannerMessageServer string

func (t *BannerMessageServer) GetBannerMessage(args *Args, reply *string) error {
	*reply = "This is a message from the RPC server"
	return nil
}

func main() {

	// create and register the rpc
	banner := new(BannerMessageServer)
	rpc.Register(banner)
	rpc.HandleHTTP()

	// set a port for the server
	port := ":1122"

	// listen for requests on 1122
	listener, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("listen error: ", err)
	}

	http.Serve(listener, nil)
}
```
Things to note:
- we're creating a function called `GetBannerMessage` of the `BannerMessageServer` type.
- we need to register it.
- Our server requires a port, 1122 in our case. 

### The RPC client

Now we create the client that will call our server and handle/use the response and result. 

```go
package main

import (
	"log"
	"net/rpc"
)

// rpc client

type Args struct{}

func main() {

	hostname := "localhost"
	port := ":1122"

	var reply string

	args := Args{}

	client, err := rpc.DialHTTP("tcp", hostname+port)
	if err != nil {
		log.Fatal("dialing: ", err)
	}

	// Call normally takes service name.function name, args and
	// the address of the variable that hold the reply. Here we
	// have no args in the demo therefore we can pass the empty
	// args struct.
	err = client.Call("BannerMessageServer.GetBannerMessage", args, &reply)
	if err != nil {
		log.Fatal("error", err)
	}

	// log the result
	log.Printf("%s\n", reply)
}
```
Things to note: 
- note that we have to specify the hostname and port in our client. `localhost` and `:1122` in our demo case. 
- note that we have an empty args struct because we don't need it for our demo. If we needed it we can define the argument struct at the top of the file and make more complex use of our setup. 
- we call the `service.function` and the reply will be assigned to the address of of our local variable `reply`.
- we simply log the outcome and end. In a more complex example we might act on this data, it may even feed into another RPC function call. 

### Run, Forest, run...
OK so now we're in a position to run our code and see this magic unfold before our eyes. There is a couple of options here, I'm opting to run both programs from the root folder of our project. We'll need two terminals, so here you can split your terminal in VSCode. 

in one run `go run server/main.go`, in the other run `go run client/main.go` and marvel at your results

I hope you found this fairly trivial RPC example useful as a RPC client/server setup overview and as a potential springboard to your own functions and setups that actually have a real purpose. 

Best wishes.