“Discovering” Microservices in Spring Cloud
Seek and ye Shall Find
If you have been a java developer for any length of time, you have come across spring boot, and chances are you have heard of spring cloud.
We are provided the following introduction to Spring Cloud in its documentation
Spring Cloud provides tools for developers to quickly build some of the common patterns in distributed systems (e.g. configuration management, service discovery, circuit breakers, intelligent routing, micro-proxy, control bus, one-time tokens, global locks, leadership election, distributed sessions, cluster state).
This all looks great and sounds even great. But your head almost starts to hurt, “configuration management, service discovery, circuit breakers, intelligent routing….”. What is it? How does it work? What is the meaning of life?
Well! I don’t think I can answer the last one, but I can start with the basics of spring cloud’s discovery service and how you can communicate between services. That is close enough, isn’t it?
What you need
- Eureka Server
- Eureka Client
- Patience (Little bit)
Setting up the Eureka Server
All you need to set up a spring boot application as the discovery service is the eureka-server dependency,
to mark the main application class with @EnableEurekaServer,
and to provide eureka specific properties
Here, we say run the spring boot application that is our eureka server on its default port of 8761. Of course, this port can be any open port. In that case we have to point all our registering services to that port. Here, we are keeping it simple.
When we say, “registerWithEureka: false”, we don’t want this service to be registered with the Eureka service.
and with “fetchRegistry: false”, we are disabling caching of registry information locally. Trust me, this saves a lot of hair pulling.
FIRE IT UP ….. and EUREKA!!
In the last section we saw how to set up a eureka server. But, the problem with our eureka server is that it is not discovering anything, because there is nothing to be discovered.
Lets create clients that will be discovered by our eureka server.
To create a client that is discoverable, we have to make sure that we annotate the service’s main application with @EnableDiscoveryClient.
This allows the eureka server to discover your service and register it with the eureka server.
Here comes the fun part. Once we have set up multiple such clients, we are ready to communicate between these services.
Inter Service Communication
There are three ways we can communicate between services and access APIs written in separate services
- Using the Spring Cloud Discovery Client
- Using the ribbon load balanced rest template, or
- Using the feign client
1. Spring Cloud Discovery Client
Here we will use the DiscoveryClient to first get all the registered instances of a service and then use our trusty RestTemplate to make the actual API call like this.
There is so much not ideal with this. For instance, we cannot autowire the RestTemplate and we have to be the one responsible for instantiating it. This is because once we annotate our service with @EnableDiscoveryClient, all RestTemplates now have a Ribbon interceptors which modifies the urls that are being created.
Another issue and a major one is how we are getting the service instances and manually getting the instance on which to serve the request. This robs us of the opportunity to balance the load across service instances.
This is where the load balanced rest template comes into play.
2. Load Balanced Rest Template
To use a Ribbon aware rest template, we need to create a RestTemplate bean with the @LoadBalanced annotation, this allows us to “autowire” RestTemplate in our client code.
Now, the only difference between the DiscoveryClient and this is that this allows us to use the natural service name that is registered with the eureka server to communicate with that service.
Here “module-two” is the registered name of the callee module. This is so much cleaner. We don’t need to select which instance of the service the request need to be made to. Spring Cloud will take care of serving requests in a round robin manner.
3. Feign Client
This is an alternative to the ribbon aware load balanced rest template. To Enable Feign Client we need to annotate our application class with the @EnableFeignClients annotation.
and then creating a java interface with the @FeignClient annotation and providing the registered service name of the callee.
This defines a feign client for a service named “module-two”. Here you can define all the endpoints that you need to access from your caller service.
You might have noticed that the definition of the endpoint is exactly how you would define an endpoint in a spring controller. This keeps our client very natural and easy to read and use.
Please feel free to leave your thoughts. Any kind of feedback is highly appreciated and encouraged.