<iframe src="//www.googletagmanager.com/ns.html?id=GTM-KXSCJR" height="0" width="0" style="display:none;visibility:hidden">

Writing your first RPC in Go

Kunal Anand on Sep 25, 2014


On top of developing runtime application self-protection tools, the Prevoty Engineering team is always looking for better technologies to manage our cloud and on-premise service-oriented architectures. A package of the Go standard library that we use extensively is net/rpc - this particular package simplifies the approach and LOC when it comes to developing your own RPC. If you're unfamiliar with Go, then I highly recommend that you take the tour - it's worth it.

In this post, I'll walk you through the construction of a primitive key/value cache, complete with client and server implementations that communicate over TCP. We'll also write unit tests to see how our the overall system works. The best part: no external dependencies are required.

This project, called rpc_tutorial has been pushed to a public GitHub repository.

Cache Protocol/Specification

Before we write a server or client implementation, we need to define a few things - types and functions (found in rpc.go). We'll setup three types below:

The RPC structure allows us to encapsulate and re-use cache functions that augment stored keys/values. Within RPC, we define a field called cache. For the purpose of this post, we'll stick to using simple in-memory maps. You'll also see the usage of a mutex as we want to make things relatively thread-safe.

CacheItem is a structure that contains the key/value that will shuttled between our clients and servers.

Requests is just a structure of integers that we'll be incrementing from our RPC functions. We'll also be able to return this structure to clients.

Our RPC constructor is straight-forward:

Now that we've defined our primitives and RPC constructor, we'll need to implement the functions we need to make this cache, well, a cache. We'll keep things simple and only support the following functions: get, put, delete, clear and stats

Get - returns a cache item for the given key

Put - stores a cache item, acknowledges storage

Delete - deletes a cache item for the given key, acknowledges deletion

Clear - clears all cache items, acknowledges clear

Stats - returns basic cache statistics

One caveat to using net/rpc is that your RPC functions have to adhere to a particular signature: only two arguments are allowed, the second argument must be a pointer (used in our code to return acknowledgements/data to clients) and an error is always returned.

Server Implementation

Now that our RPC and its functions have been defined we can develop our first server implementation (see file server.go).

...and that's all it takes to get a minimal server implementation! We register our cache RPC, which is returned back from the constructor we just wrote in rpc.go. For our example, we'll have our RPC listen via TCP on port 9876.

At this point, you should be able to do a go build without running into any errors and have a lonely cache server waiting for some action.

Client Implementation

Building a client (see client.go) is a little more involved than the server implementation. We'll start by creating a Client structure.

dsn is going to be in the form of "localhost:9876" and timeout is going to express a timeout for the client to connect to the server.

Implementing the client's functions are going to mirror what we setup in rpc.go:

Notice how we invoke a remote function in the form of Type.Function with the variables that correspond to the net/rpc signature that we covered above.

Testing + Wrap-Up

Now that we've got the server/client pair, let's put it together via some simple tests (see file client_test.go). Here is the test setup:

As you can see, we'll use the newly created RPC client constructor to instantiate a cache client that we'll use for our tests. Instead of showing all of the unit tests, I'll highlight the significant bits:

Since these tests will execute linearly, we've attempted to model a lifecycle: starting with a cold get, which will fail, to a working cache put and a succesful get. Now that we've got these tests, let's start the server and run our tests just by using the `go test` command.

You should see the following in your terminal:

...and there you have it - a fully working cache using net/rpc! Best part was that we didn't have to handle serialization, de-serialization or even network transport. Those implementation details are handled by Go's standard library.

As mentioned above, this project can be found in our public GitHub repository. Feel free to fork the code or send any pull requests our way.

Happy hacking!

Back to blog

Kunal Anand

Kunal Anand is the co-founder and CTO of Prevoty, a next-generation web application security platform. Prior to that, he was the Director of Technology at the BBC Worldwide, overseeing engineering and operations across the company’s global Digital Entertainment and Gaming initiatives. Kunal also has several years of experience leading security, data and engineering at Gravity, MySpace and NASA’s Jet Propulsion Laboratory. His work has been featured in Wired Magazine and Fast Company. He continues to develop the patented security technologies that power Prevoty’s core products. Kunal received a B.S. from Babson College.

Find me on:

Topics: Prevoty Technology