Table of Contents
In this article, we are going to look at Microservice architectures, their benefits, what makes them different from traditional monolithic architectures, and how to go about setting up monitoring and alerting for them.
What are Microservice Architectures?
According to Martin Flower, the term “Microservice'' had its origins at a workshop of software architects near Venice in May 2011 to describe the common architectural style that many people had been exploring at that time.
Microservices is an application architectural style where a big application is divided into multiple components each individually called a Microservice. The multiple microservices within an application communication using a well-defined Application Programming Interface (API).
Difference from traditional Monolithic architectures
Before the advent of microservices architecture, applications traditionally have been written with a monolithic design in which all the components of an application reside together within a single service.
As the application grows in size and complexity, that's where the disadvantages of monolithic architecture begin to emerge. It becomes very difficult when multiple developers are working simultaneously on a single codebase.
Not just that, a bug or a crash in a particular component of an application can result in an entire application being unavailable for the customers. This is because the entire application being a monolith is always deployed as a single component.
Advantages of Microservice architectures
As with any technological change, dividing a monolith application into microservices comes with its advantages and disadvantages. First, let’s go over the advantages:
- Separation of concerns: Each microservice is responsible for a particular concern within a bigger application. For example, a User service within a big application is just responsible for provisioning users on signup, inactivating a user, and providing the user data to other microservices within an application.
- Separation of owners: Each microservice can be owned and maintained by a distinct team within an organization. For example, a team owning User Service can consist of a couple of engineers, a technical lead, a UI designer, and a product manager who charts out the user stories for the features related to User service.
- Individually scalable and deployable: A microservice is individually deployable and a scalable component within an application. That means if the load on user service is high, it can be horizontally scaled to handle the additional traffic without affecting any other part of the application. As we will see later, this introduces additional challenges of having a service discovery and load balancing within an application.
- Heterogeneous programming paradigms: In a microservice architecture, each microservice can be programmed in a different programming language since each microservice communicates using an application programming interface (API) which could be over HTTP Rest Interface. This allows more flexibility as an appropriate programming language/framework can be chosen depending on the use case which needs to be designed.
Challenges in Microservices Architectures
Microservices architectures have their own challenges. However, with careful application design they can be overcome to the most extent:
- Increased complexity: Dividing a monolithic application into microservices comes with its own complexities. An interservice call between two services which used to be a simple interprocess call in a monolithic application is now a call over the network perhaps using HTTP REST API. We need to accommodate for failure scenarios over these communications such as increased latency and timeouts.
- Segregated view of data: The data which used to reside in a single database in a monolithic application now distributes over multiple databases. This can introduce additional challenges when querying the data since it's not possible to retrieve the data using a single SQL query which could have been possible earlier using SQL joins.
- Transaction management: In a monolithic application, it's easier to do database transactions since all the data is part of a single database. For example, if an operation fails in a monolithic application, the database can roll back the entire transaction for us. In Microservice architecture, rolling back the data once an operation failure can require custom application code. As we will see later, they are design patterns such as Saga patterns to solve the issue like these.
Key Design Patterns for Microservices Architecture
There are few design patterns that have evolved for solving common challenges with microservices architectures. Let’s go over them:
- Event sourcing: In the event sourcing pattern, the sequence of CRUD operation on an entity is recorded as a separate immutable entity in an append-only event store. For example, an order in an e-commerce application might go through various types of updates such as OrderCreated, OrderShipped, and OrderDelivered, etc. Each of these events can be recorded in an event store to act as an event log and to populate some parts of the user interface.
- Saga pattern: Since each service has its own dedicated database in the microservice architecture, how do we ensure consistency during transaction commits and rollbacks? That’s where the Saga pattern comes in. In the saga pattern, each service listens for the domain events which are published onto a message bus such as Kafka or RabbitMQ and performs the local transaction if it's interested in that event. For example, in the case of an e-commerce application, if the inventory service determines that inventory is not available, it will emit the OrderCancelled event which would be consumed by Order Service to cancel the order and Email Service to send the email communication to the user.
- Distributed tracing: In the microservices world, each request to an application might route through multiple microservices each calling internally. In that case, how do we get the trace of the request through multiple microservices logs to troubleshoot any issue? The answer is Distributed Tracing. Systems such as Zipkin allow for tracing a request through multiple microservices within an application.
- Circuit breaker: In a microservices architecture, one service ends up calling multiple other services as a part of a single request. What happens if the called service goes down for some reason? It will result in timeouts and requests might end up getting queued due to the high response times of the requests. In the circuit breaker pattern, when the number of failures reaches a certain threshold, the circuit is said to be open. When the circuit is open, any call to the called service will fail immediately for a certain period. After the period elapses, some requests are allowed to go through the called service and if those requests succeed, the circuit is closed again for normal operation.
Monitoring and Alerting
As we saw above, microservices architectures come with not only benefits but also a lot of increased complexity. Because of additional touchpoints for communication between various microservices, the chances of failures are higher in microservices systems.
Also, the chances of these failures getting unnoticed are even higher because the entire application might not go down because of these failures. Only when a customer or a user is exposed to the functionality which failed, the failure might get exposed.
Hence it is very important to set up proper metrics for monitoring and alerting for any microservices architecture-based application.
Some of the important metrics for microservice application are:
- Database query times
- P95 HTTP Response times
- Number of requests/second
- No. of failed requests/second
To get started right away, sign up for MetricFire’s free trial of our Hosted Prometheus and Grafana. Also, if you have any questions about our products, or about how MetricFire can help your company, talk directly to one of our experts by booking a demo.