A bare-bones microservices architecture implementation using Spring Cloud Netflix (Eureka Discovery Service, Zuul API Gateway, Ribbon Load Balancer, Feign REST Client, and Hystrix).
Note: most of the libraries used in this project are deprecated or in maintenance mode and should be replaced by newer ones. Nevertheless, the main concepts remain the same.
-
One Eureka Server instance, that acts as a Service Registry.
-
One Zuul API Gateway instance.
-
Two microservices that communicate with each other using Feign REST Client and Hystrix as a Circuit Breaker.
The below diagram shows the component architecture.
- First, launch the Eureka Server instance. Eureka has to be deployed in the first place so that all the other services can register with it. After successful deployment, you should be able to access the Eureka dashboard on http://localhost:8010/
In microservices-based applications, service locations and the number of instances change dynamically, so communication between microservices becomes a challenge. One way to solve this problem is by using a Service Registry solution like Eureka. A Service Registry it´s a database of services that can be queried by any service to find the location of any other. Eureka in particular is an implementation of Client-side Service Discovery: clients have to first get the registry from Eureka, and then call the target service (this is normally abstracted by using some Registry aware HTTP client). At start-up, all Eureka Clients register themselves with the Eureka Service Registry, making themselves available for discovery. They also periodically send heartbeats to the Eureka Server to renew their registration, so that Eureka knows they are still alive. This is known as self-registration (another option would be having a 3rd party registration component). In this example, both microservice applications (A and B) and Zuul API Gateway are annotated with the @EnableEurekaClient annotation, which marks them as clients of Eureka.
- After launching Eureka Server you can then launch Zuul API Gateway and the two microservices (A and B) in any order. After the successful deployment of these, you will see them appear as registered instances on the Eureka dashboard.
Both microservices (microservice A and B) listen on random ports for requests, which they get assigned on start-up. You can access them through the API Gateway, which will query the Eureka Service Registry to get the corresponding service endpoint. Accessing these endpoints should result in any of the microservices (A or B) printing the port it is currently listening on and the port of the other microservice.
- Microservice A endpoint: http://localhost:8011/microservicea/test/status/all . Note that the URL is the concatenation of the Zuul API Gateway URL (http://localhost:8011/) + the name of the target service + the resource path. It should print:
- Microservice B endpoint: http://localhost:8011/microserviceb/test/status/all. Should print
Ribbon, a client-side load balancer, is included by default in Zuul API Gateway. It is also integrated with Feign REST Client, which is being used by both microservices to communicate with each other.
- To test the Zuul API Gateway load balancing, after having initiated two or more instances of microservice A, execute the endpoint that prints the random port assigned to service A: http://localhost:8011/microservicea/test/status. It should print a different port each time you execute it. Zuul, by using Ribbon, is load balancing across the two instances in a round-robin fashion.
- To test that Feign REST Client does the same, after having initiated two or more instances of microservice A, you can execute http://localhost:8011/microserviceb/test/status/all. Note that this endpoint, located in service B, will query service A using Feign. Because Feign client is integrated with Ribbon, the port assigned to A should change each time you call the endpoint.
The Feign Rest Client is configured to use Hystrix as a circuit breaker, a common pattern used in microservices architecture to avoid cascading failures. You can test this by executing http://localhost:8011/microserviceb/test/status/all after having stopped service A. You should see a fallback value returned instead of B´s port.
Service A will be detected as being down when the number of consecutive failures crosses the configured threshold, the circuit breaker will trip, and for the duration of the timeout period, all attempts to invoke the remote service will fail immediately. In a real-world scenario, the fallback value would be something that makes more sense, maybe a cached value.