Microservices with Spring Boot & Spring Cloud- part1
In this article series, we will explore microservices in the context of Spring Boot by creating a project using Spring Boot and Spring Cloud. We will create sample microservices with their own databases and use Spring Cloud-related services and patterns, such as Spring Cloud Config Server to externalize configurations to a central repository like GitHub, Spring-provided Eureka Server to implement service registry, Spring Cloud Sleuth module to implement distributed tracing, and Zipkin to visualize log information in a user interface. We will use Spring Cloud Provider API Gateway to implement an API Gateway in a microservices architecture. Additionally, we will learn about event-driven microservices architecture with multiple consumers using Spring Boot Kafka message broker for asynchronous communication between multiple microservices. We will also use RabbitMQ to create a couple of microservices and use RabbitMQ as a message broker for asynchronous communication between multiple microservices, i.e., microservices architecture with multiple queues.
Monolith Overview
Before understanding microservices, let's first look at the basic standard way of building applications, known as the monolith application.
A monolith is an architecture pattern where all components are built as a single unit.
Everything is deployed, scaled, and implemented as one application.
All components use a single tech stack.
Developers might impact other components when adding a new one.
The application is deployed as a single unit, so after adding new features or fixing bugs, the entire application is redeployed.
Challenges in Monolith Architecture
As new features are added, the codebase grows exponentially large.
Every component is tightly connected.
It incurs higher infrastructure costs because the entire application is scaled, not just a single component.
Dependency version conflicts can occur if different components require different versions.
Longer release times or delays in going to market because the entire application is redeployed with every new feature or bug fix.
Bugs in any component can affect other components as well.
Microservices Overview
Microservices architecture helps build isolated, loosely coupled, highly scalable services. In microservices whole application is broken down into smaller loosely coupled independent applications that when combined makes entire application.
Characteristics of Each Microservice:
Deployed independently
Scaled independently
Has its own database
Exposes REST APIs
Loosely coupled and developed independently
Challenges Addressed by Microservices Architecture
In microservices architecture, applications are broken down into smaller, independent services, preventing the codebase from becoming too large.
All services are loosely coupled.
Lower infrastructure costs since only specific services are scaled instead of the entire application i.e. can independently scale up highly used services.
Different dependency versions can be used across the services in a microservices architecture.
The release process doesn't take as long since all the services in a microservices are loosely coupled (independent of each other).
All services are independent, so bugs in one service won't affect other services.
Technology updates or rewrites are simpler in microservices architecture.
Microservices are independent of techstack i.e. different techstack is used to implement the microservices and exposing REST APIs.
Microservices Architecture
If you look at the above microservices architecture, the main points to note are that the services (service1, service2) are isolated, deployed, scaled, and implemented independently, and each of these services has its own database associated with it.
The above screenshot also shows that each service can coordinate and communicate with each other internally. There are two types of communication styles.
Synchronous Communication -In synchronous communication, the HTTP protocol is used to make an HTTP request from one microservice to another, and each microservice exposes REST APIs.
Asynchronous Communication - In asynchronous communication, the microservices need to use a third-party component or a message broker like RabbitMQ or Apache Kafka to facilitate asynchronous communication.
Now let's look at the key components of a microservices architecture.
User Interface/client - The client can be a web application, mobile application, or any desktop application.
Service registry- All the microservices register with the service registry. This registry acts as a central directory where each microservice can find and communicate with other services. It helps in managing the locations and availability of services, ensuring that microservices can dynamically discover and interact with each other efficiently.
API gateway- Whenever a client wants to use the REST APIs of backend services, the client first sends a request to the API gateway. The API gateway then routes the request to the appropriate microservice. It discovers the specific microservice's hostname and port using the service registry, enabling it to direct the request to the correct microservice.
Config server- The config server externalizes the configurations of microservices.
Distributed tracing - Distributed tracing maintains the logs or the complete log hierarchy of a particular HTTP call.
Key Considerations for Microservices
Whenever we design a microservice architecture for an application, we will consider the following key points:
How to break down the application into smaller, manageable microservices?
Break down the application into smaller microservices based on business functionality/features, not technical functionalities.
What code belongs in each service?
Separation of concerns means each service should handle one specific job, following the single responsibility principle, and different responsibilities should not be combined into a single service.
How many services we should create? How large or small should the services be?
Microservices should be self-contained and independent from each other. Each service must be developed, deployed, and scaled separately without relying heavily on other services, even though they are part of the same application.
How should services communicate with each other?
Synchronous communication - The client sends a request and waits for a response from the microservice.
Asynchronous communication - The client sends a request and does not wait for a response from the service. Microservices should use a message broker for communication between multiple microservices. A microservice subscribes to the message broker and receives messages from it.
Challenges of Microservices
In enterprise there will be 100s or 1000s of microservices for an application and client will have to remember host and port for each and every service. Whenever new microservice is introduced in the application then the client again have to remember or register this new services host and port.
To fix the above issues, we use an API Gateway, which is a central component or pattern that routes the request to the appropriate microservice.
Each microservice has its own configuration file to maintain its individual settings, and with hundreds or thousands of microservices in an application, updating the configurations for each one can be challenging.
To address this issue, we use a config server to externalize the configuration of multiple microservices in a central location where we can store all the configuration files. Whenever there's a need to change the configuration of multiple microservices, we can make the changes in this central place, and it will reflect across all the microservices.
There might be times when a microservice is down, causing the source microservice that invokes it to not receive a response from the destination microservice. This, in turn, makes the source microservice return an error response to the API Gateway, which then forwards that error response back to the client. However, these continuous calls between services would waste underlying resources.
To address this issue, we use the circuit breaker pattern, which means that at the microservice level, we limit the number of calls to destination microservices whenever they are down.
If there is requirement to scale microservice, then multiple instances are deployed which due to certain reason might go down.
To address this issue, to track if microservices are up and running we go with service registry and discovery which maintains the hostname and port of all the registered microservices and all of its instances.
For internal microservice communication, we need log information to capture the complete call hierarchy between microservices.
We need log information for the entire call from start to end, and to address this, we can use the distributed tracing pattern to identify the complete call hierarchy.
Developers will have to manually implement load balancing, API gateway, centralized configuration, circuit breaker, distributed tracing, and service registry patterns.
To address this issue, Spring Cloud is used, which internally implements all these patterns. Spring Cloud has tools for each of these common patterns that are required across any application.
Spring Cloud circuit breaker module - used to implement circuit
breaker pattern.
Spring Config module - used to implement config server to externalize
the configuration files of different microservices.
Spring Cloud Gateway module - used to implement API Gateway.
Spring Cloud open feign module - used to make a REST API call between microservices.
Spring Cloud sleuth module - used to implement and distributed tracing.
Spring Cloud stream module - used to implement asynchronous communication.
Microservices Implementation
For any application be it from finance domain, ecommerce, we should follow below steps.
Implement microservices based on business requirement using spring cloud and with its own database.
For internal microservice communication use REST template, web client, spring cloud open Feign module.
Implement Service Registry and discovery pattern in our microservices project.
Well, Spring Cloud provides Spring Cloud Netflix Eureka Based Service Registry module.
Implement config server to externalize the configurations of these microservices
into a central place that is git repository using Spring cloud provided Spring cloud config module.
Implement API gateway using Spring cloud provided Spring Cloud Gateway module.
Implement a distributed tracing using Spring Cloud provided Spring Cloud sleuth module along with Zipkin to visualize the tracing log information.
Implement circuit breaker pattern Spring Cloud provided resilience4j framework.
In the coming parts of this article series, we will explore the key components of microservices, communication types, and more. Refer to the articles below.
Microservice overview - https://vineetaparodkar.hashnode.dev/microservices-with-spring-boot-spring-cloud-part1
Microservice architecture components - https://vineetaparodkar.hashnode.dev/microservices-with-spring-boot-spring-cloud-part2
Event driven architecture with Apache Kafka -
https://vineetaparodkar.hashnode.dev/microservices-with-spring-boot-spring-cloud-part3
Event driven architecture with RabbitMQ -
https://vineetaparodkar.hashnode.dev/microservices-with-spring-boot-spring-cloud-part4
Microservices Design Patterns - https://vineetaparodkar.hashnode.dev/microservices-with-spring-boot-part5
Subscribe to my newsletter
Read articles from Vineeta directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Vineeta
Vineeta
Experienced Software Engineer with 4+ years of expertise, specializing in cutting-edge technologies.