Building a Load Balancer in Go

Setup a load balancing proxy server using Go

Stephen Wayne
Better Programming

--

What is a Load Balancer?

A load balancer is a common form of proxy server that distributes client requests between one or many backend servers that will do the actual work.

A proxy is stand-in for some other entity — it can behave like, or act in place of that other entity. In (web) software, we often view a proxy as a node in the network that essentially forwards requests and responses between clients and backend servers. If I go to a friend’s home and talk to them through their Ring doorbell, that doorbell is acting as a proxy between us. Likewise, a proxy server provides a gateway between a client (me) and some backend server (my friend). Like the doorbell, it acts as an intermediary; I can make a request to a server (talk to my friend) without gaining privileged access to that server’s resources (entering the house).

Proxy servers can provide added security, performance, and observability to an internet system.

Today, we will be building a load balancing proxy server using Go!

Building the server

We’ll start by defining a LoadBalancer — this object will contain basic information about the server including the backend servers that will handle the proxy request, as well as the port that the load balancer will be listening on.

By including a list of Servers — an interface that represents a set of functions — rather than a hard-coded type, we can use this load balancer with a variety of objects that implement the basic Server interface without having to change the underlying code. For clarity’s sake we are also including a constructor (NewLoadBalancer), but we could just as well have done with constructing the LoadBalancer directly.

We now need an object that implements the Server interface:

What good is code without error handling? For this example we’ll do a very simple form — if we see an error, bail!

Next, we’ll write a helper to instantiate our servers. This code will take an address and create a new simpleServer instance that has a proxy. We’ll be using the proxy to process any requests we wish to send to this server.

Now, let’s set up our main function to start the load balancer. We will configure it to handle any request that comes in, and we’ll listen on the port defined for this LoadBalancer. We will define a few simpleServer instances. The addr is the URL that we will be redirecting requests to.

Okay, so we have the basic structure of our server configured, along with most of the parts we need. The last step is to define what happens when our LoadBalancer receives a request.

We use getNextAvailableServer() to determine which backend server is best suited to handle our request, by some load balancing algorithm (in this case we use a round-robin paired with a health check). Once we know which server to forward the request to, we make the request on that server’s proxy. The proxy will route the client’s request to the target, and the target’s response back to the client.

Now all that’s left to do is run the server and make a request!

Running the server

You can clone the full code from my GitHub, or generate it by following this tutorial.

Once you have the server code, navigate to the directory in a terminal window and type go run src/main.go . You should see terminal output indicating the server is running.

You can now go to localhost:8000 in your browser, and you should be redirected to one of the addresses defined in servers. As you refresh, the load balancer proxy server will redirect you to the various URLs defined.

Note that your browser may remember the identity between localhost:8000 and one of the server addresses given, so it might be better to use an incognito window. Also note that the defined links may or may not give you an error from each of their respective hosts (e.g. going to / at https://google.com yields an error).

Happy Go-ing!

--

--

Backend cloud engineer at HashiCorp. Former Electrical Engineer turned to the dark side.