About Tomasz Dziurko

ZooKeeper, Curator and How Microservices Load Balancing Works

How Zookeeper makes sure that every worker happily gets some stuff to do from job delegating manager.

Apache ZooKeeper is a tool to register, manage and discover services working on different machines. It is an indispensable member in technology stack when we have to deal with distributed system with many nodes which need to know where their dependencies are launched.

However ZooKeeper is pretty low level and even standard use-cases require many lines of code. That is why Apache Curator was born – a much more friendly and easier to use wrapper library over ZooKeeper. Using Curator we can deliver more with less code and in a much cleaner way.

“Guava is to Java what Curator is to ZooKeeper” – Patrick Hunt, ZooKeeper committer

Load balancing microservices with ZooKeeper

We are accustomed to situation when there is a load balancer deployed in front of our application. Its role is to make sure that every single node gets more or less same amount of traffic.

In a microservices world situation is the same but we have a significant advantage over a monolithic approach: when we need to scale application we don’t have to duplicate whole system and deploy it on a server powerful enough to run it smoothly. We can concentrate only on a small module that needs to be scaled so cost of scaling is much lower (both in terms of server cost and development needed to prepare module for deployment in many instances).

But as our system grows, we end up with many scaled modules, each of them requiring separate load balancer. This seems troublesome as our infrastructure is very complex even without them. Luckily if we use ZooKeeper as a service orchestration and discovery tool we can use built-in load balancing capability without introducing any additional complexity into our microservice architecture.

To present how out of the box load-balancing works in ZooKeeper we need two services: worker which will be deployed multiple times and manager delegating tasks to registered workers.

Simple worker

Let’s start with creating a simple worker that will listen on a given port and return some result when asked to perform its job. To implement this tiny microservice we will use Groovy, Undertow light-weight servlet container and of course ZooKeeper and Curator.

Our worker will consist of one, small class with main method that does three things:

For a brevity I will omit steps 1 and 2 here, you can check complete source code on GitHub project. Our worker will have only a single endpoint GET /work which returns a response with name of the called worker:

Lines 2-3: we create and start CuratorFramework client wrapping all operations we want to perform on ZooKeeper instance. For a simplicity we use localhost with default port (in general it should be the URL to the running instance of ZooKeeper)

Lines 4-9 create ServiceInstance representing our worker. We pass all “contact details” required to call this worker from other microservices

To check that worker is running you should open browser on http://localhost:18005/work and see “Work done by Worker_1” text there. To verify that worker has properly registered itself in ZooKeeper, launch command line client:

cd /bin
./zkCli.sh

and then execute ls command to see one node registered under /load-balancing-example/worker path:

Simple manager

Now, as we have worker listening on /work for requests, we can create simple manager service delegating tasks to its subordinates. Main method looks quite similar to one in simple-worker project, the main difference is that we do not register in ZooKeeper, we only create ServiceProvider which role is to (surprise, surprise) provide us with instance of worker. So basic workflow is:

Wait for requests on /delegate

Get instance of worker service from ZooKeeper’s ServiceProvider

Call worker’s /work and return results of its execution

To create ServiceProvider we have to create CuratorFramework client, connect to ZooKeeper and then fetch ServiceProvider for service with given name, worker in our case:

Manager doesn’t know anything about its workers. Only thing he know is that service is registered under specific path in ZooKeeper. And it is that simple no matter if we have multiple workers launched locally or spread across different servers in different countries.

Out of the box load-balancing with ZooKeeper

Imagine situation that manager gets so many tasks from his CEO that he needs more than one worker to delegate job. In a standard case we will be forced to scale workers and place load balancer in front of them. But ZooKeeper gives us this feature without any additional work.

What is most important here is that to utilize all these four new workers, manager doesn’t require any changes in the code. We can launch new worker instances as traffic increases or shut them down when there is nothing to do. Manager is decoupled from these actions, it still calls ServiceProvider to get instance of worker and passes job to him.

Work done by Worker_1
Work done by Worker_2
Work done by Worker_3
Work done by Worker_4
Work done by Worker_1
Work done by Worker_2
Work done by Worker_3

How is it implemented under the hood? By default ServiceProvider uses Round-robin ProviderStrategy implementation which rotates instances available under given path so each gets some job to do. Of course we can implement our custom strategy if default mechanism doesn’t suit our needs.

Summary

That’s all for today. As you can see in by using Apache ZooKeeper and Curator we can live without separate load balancers that need to be deployed, monitored and managed. Infrastructure in a microservices architecture is pretty complicated even without them.

Newsletter

Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

Email address:

Join Us

With 1,240,600 monthly unique visitors and over 500 authors we are placed among the top Java related sites around. Constantly being on the lookout for partners; we encourage you to join us. So If you have a blog with unique and interesting content then you should check out our JCG partners program. You can also be a guest writer for Java Code Geeks and hone your writing skills!

Disclaimer

All trademarks and registered trademarks appearing on Examples Java Code Geeks are the property of their respective owners. Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries. Examples Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.