Hello friends, how is your day? I hope it’s great! Welcome to another article from the Microservice Architecture series. Our previous article was regarding the API Gateway and now we know the importance of it. Do you remember the Netflix example from the previous article? It reminds us of the importance of microservices and the way it is being implemented in the market. In this article, we will talk about another important aspect of Microservice Architecture, it is known as Service Discovery. We have discussed a little bit about service discovery in our previous article. So let us discuss it in detail.
The microservice architecture in itself is based on implementation. So each and every aspect of the microservice architecture must prove its worthiness, or else it is just useless. So let us understand the usefulness of service discovery with the help of an example. Consider that you are writing some piece of code that invokes a service. It has a REST API or a Thrift API. For making a request, the code needs to have an IP address and the port address of a service instance. In short, there should be a network location. When it comes to the traditional application methods, they could be running on some physical hardware and the network locations are static. So the code can read the network locations from some configuration file and the file is occasionally updated. But with modern cloud-based microservices applications, there are some difficulties.
The service instances are assigned network locations dynamically. The set of service instances also change dynamically due to things like autoscaling, failures, and upgrades. So the code from client-side has to be more elaborate in terms of the service discovery mechanism.
So there are two different types of service discovery patterns. One is known as the client-side discovery pattern and the other is the server-side discovery pattern. Let us have a look a both these patterns in detail.
Usually, services stay in touch with each other. I mean to say that in most of the applications, they need to call one another. A service invokes another service using language level or procedure call in a monolithic application. While in the traditional distributed systems, services run from fixed locations like hosts and ports. They can easily call using HTTP/REST or some kind of RPC mechanism. The modern microservices-based application run in a virtualized environment. Here, the number of instances for a service and their locations change dynamically. For making this possible, one must implement a mechanism that allows the clients to make requests to the dynamically changing service instances.
So what is the problem here? The problem is that how will the client, the API gateway, or some other service find out the location of a particular service instance? Here, each instance of any service exposes a remote API like HTTP/REST, etc at some particular location. The number of services and their locations keeps changing dynamically. Also, the virtual machines and the containers are assigned dynamic IP addresses. The number of service instances could vary dynamically. With all such prominent forces, what could be the possible solution?
The problems seem to be big, but the solution is quite small. While making a request to any service, the client can obtain the location of a particular service instance. This could be done by querying the service registry because it has all the information about the locations of all the service instances.
On the server- side as well, the same problem persists. Here also, the services call each other and in a monolithic application, the services invoke each other through procedure calls or through language-level methods. The same concept like client-side persists for the traditional distributed system and for the modern microservice architecture based applications. The problem on the server-side is also the same! How would the client, API Gateway, or any other service discover the location of a service instance? Here as well, each instance of a service exposes some remote API like HTTP/REST, etc. The number of service instances and their locations keep changing. The virtual machines are assigned dynamic IP addresses. Also, the number of service instances might vary dynamically. So what is the solution?
This time as well, the solution is quite short and sweet! The client makes a request via router or load balancer. It runs on some well-known location. The router should query a service registry which is present in the router or which might be built into the router. The request is then forwarded to the respective service instance.
The service registry is an important aspect of the service discovery. It includes the database or could be called as the meta-data containing the network locations of the service instances. The service registry should be highly available and up to date. Clients should be able to cache network locations from the service registry because the problems on server-side and client-side are solved with the help of cache. But this information becomes out of date and clients are unable to discover the service instances. The service registry is a cluster or group of servers and they use a replication protocol for maintaining consistency. A good example of service registry is Netflix Eureka. It provides a REST API that registers and queries service instances. ETCD, Consul, and Apache Zookeeper are some other examples of service registry. But how do the clients of some service or the routers know about the available instances of a service?
The solution is to implement a service registry that serves as a database of services, their instances, and their locations. These service instances are registered with the service registry on startup. They are deregistered with the shutdown. The client asks service registry to find the available instances of some service. The service registry in return might invoke a service instance health check API for verifying if the service is able to handle the request or not.
We have just discussed that service instances should be registered and deregistered from the service registry. So for this, we need to understand the service registration options. There are different ways to handle it. The first option is for service instances to register themselves, it is known as the self-registration pattern. The second option is related to some other system component that manages the registration of service instances and the third-party registration pattern. So let us have a look at these options.
The application is either from the client-side service discovery pattern or from the server-side service discovery pattern. The service instances should be registered with the service registry during startup and deregistered during the shutdown. The problem is, how do this registration and deregistration occur? Additionally, the service instances that crash and the already running service instances that are incapable of catering to the requests should be unregistered. So what is the solution?
A service instance registers and de-registers itself with the service registry. With a startup, the service instance registers itself, it includes host and IP address. It also makes itself available for discovery. The client has to renew its registration periodically. A service instance also sends heartbeat requests for preventing its registration from expiration. In this way, the registry knows that the service is alive. During the shutdown, service instance unregisters itself. Overall, this is handled by a microservice chassis framework. Eureka Service Registry is an example of the self-registration pattern.
In this pattern as well, the main problem is: how would service instance register and deregister itself from the service registry? The rules remain the same. The service instances should be registered on startup and deregistered on shutdown. The ones that crash and the ones who aren’t able to cater to the requests must be unregistered. So what is the solution or working strategy in the third-party registration pattern?
Here, some third party is responsible for registering and deregistering service instances. When the service instance starts up, the registrar should register the service instance and when it shutdowns, the registrar should unregister the service instance. Netflix Prana, AWS Autoscaling Groups, Joyent’s Container buddy, and Registrator are examples of third party registration patterns.
In this article, we have again discussed another important part of the microservice architecture. We begin the article by discussing the concept of service discovery. The worthiness and usefulness of service discovery are covered in the next section. In that, we discuss two discovery based patterns for discovering the services. The patterns are known as the server-side discovery pattern and the client-side discovery pattern. Then, another small but important part of this section is discussed. It is known as the service registry. This is mandatory for service discovery, without registration, discovery isn’t possible.
So we then discuss service registry in detail and then study two different options for registering service instances. It can either be done using the self-registration pattern or by using the third party registration pattern. Both the patterns yield efficient results. Overall, we have covered the whole procedure for discovering services. I hope that this article has been interesting and would prove to be of good help to you. Till then, keep learning and keep practicing. See you soon guys!
Here is the link to the previous article of this series.