Microservices 101: Understanding the architecture
September 21, 2020
“Microservices” is truly the buzzword of these recent years. You’ve heard it mentioned in various tech-related contexts, at conferences, on IT giants’ websites. So much is written around the topic, and yet you might (very understandably) stumble through the ABCs of microservices. Are they good or bad, promising new horizons or detrimental for your sustainable, carefully crafted applications? It might be wise to start with a bird’s eye view. Don’t look any further — because you have found our blog.
This article is the first in a series that explains the very basics of this architectural style, gradually going deeper into more specific matters. In the upcoming parts, I will discuss frameworks, containers, choosing a native image vs. runtime approach, and the role of Java in building and supporting microservices. You can read part two now if you’d like. This one will serve best to those who are just getting to know the architecture and pondering whether it is wise to move from monoliths.
Prefer speaking to reading? Let us know, and our expert engineers will guide you through the convoluted world of the microservice architecture, let alone help with migration if your company needs it.
What exactly are microservices?
The microservice architecture is an alternative lightweight method of organizing software as opposed to monoliths. This juxtaposition is the key to understanding the nature of our subject. Let’s take a quick dive into the recent history of software.
Not long ago, applications were mostly developed on a single codebase as centralized structures. Some, the simplest, served one task only, others contained layers. Think of a CRM that has dozens of functionalities and is still a monolithic system. And now think of the time spent on updating or troubleshooting: it took ages! One change caused this monstrosity of an application to stop the world and rearrange everything.
Still, the single-tiered style of software development was great for its purposes… until applications became more prevalent on mobile devices and the cloud. When your back-end data is on different and numerous gadgets, monolithic architecture won’t cut it. Just imagine every added feature leading to potential bugs, and a push to another OS needing to redesign the entire thing.
Enter the service-oriented architecture (SOA). It was an attempt to solve the downtime issue by dividing the structure into discrete units of similar functionality, or services. SOA indeed has helped the developers: services can be accessed remotely, update independently, and transmit information via a communication protocol. The problem is that an SOA service is an excessively broad concept, which implies that they could very much resemble monolithic applications on a smaller scale. Also, complex message formats and Web services standards do not facilitate communication. As a result, an SOA-based application can be even slower and require more processing power.
Microservices happen to be a variant of SOAs. This modern interpretation defines services clearly to avoid software bloat. Smaller in size (micro) services are processes that communicate over a network through language-agnostic protocols, like APIs. It gives developers more freedom in choosing production tools since they don’t have to rely on enterprise service buses, other services and the way they couple. Then, thanks to advances in containerization and especially tiny Alpine Linux containers, parts of an application with microservices have become even more autonomous. Now app components can be controlled individually while running simultaneously on the same hardware. Microservices in containers give way to cloud-native computing along with efficient and scalable applications.
Why use them?
I can come up with multiple instances where you might find this architecture not indispensable per se but quite handy. Think of functionality, simple and straightforward: managing a user, booking a ticket, topping up an account, something along those lines. Microservices are great to avoid spaghetti coding and build clear interfaces.
Previously, software management was reduced to handling the lifecycle of a certain entity through CRUD operations. Here’s what a typical programming abstraction looked like: a data table (database) with a small application pulling data from it. The app received standard HTTP requests by type: create, modify, retrieve, and destroy. This will be also relevant as an example of a simplest classic microservice that interacts with nothing else but its storage space.
When it gets separated from the system, we can forget about the database links to the rest of it even existing because of a unified interface. Now it is possible to make changes to the data, replace the database entirely, it doesn’t matter. What’s more, we can scale this part of the system independently. Say, an enterprise only launches some function on a single server but then sees its capacity is not enough. With the microservice architecture, it may spread the deployment to 10 servers and start balancing requests.
Up/downgrading won’t come as easy for monoliths. Sure, these structures are simpler in nature; they can scale up by replicating the app and running multiple copies. Over time, it will become harder to follow. Each new instance will access all of the data and resources, increasing I/O traffic, memory consumption, and making caching inefficient. Then you’ll notice that some nodes utilize CPU and storage in different proportions, which slows down the whole system — but you won’t be able to do anything with it. Monolithic software products thus lose their acclaimed stability.
Are there any downsides?
Clearly, it’s not all butterflies and rainbows. Before switching to microservices, the CTO and engineers in your organization should understand whether this concept aligns with the company’s business goals. Remember that before everything runs like a Swiss watch, the team might spend countless hours and effort planning, drafting documentation, testing, redesigning, testing again… Which is sometimes wiser to put into working with what you already have. What if you’re uncertain? Taking advice from professionals in one way to gain confidence. Click the button below, leave your details, and discuss your situation with our experts.
Let’s talk about the obvious drawbacks of the microservice architecture.
In the case of monoliths, apps are contained within the same virtual memory space, and requests are sent to a physical server. Microservices communicate using protocols (HTTP/HTTPS, AMQP, REST, TCP). It implies a bunch of consequences:
- constantly moving traffic across networks,
- request failures and other web-related errors
- a need to, possibly, encrypt and repackage data into some other forms
Luckily, you don’t need to bother. Modern solutions cover all these tasks and even more. Choose one and stick with it: Apache Kafka, Redis, RabbitMQ, so on and so forth.
When an application is oversaturated with functions, microservice managing may become a complicated endeavor. Imagine a network of 10 data silos where each addresses another, sometimes concurrently. How to tackle this problem? Decouple services with event streaming platforms (Kafka, Kinesis, Spark Streaming), simplify the service-to-service communication with a service mesh (Istio, OSM on Kubernetes), or redesign the system. Optimally, your goal is to avoid this:
…by turning it into this:
Note that the first scheme doesn’t feature any connection between message queues (represented by “MQ” cylinders) and databases.
Also, if we look at the upper level, right below the user, we can see that it retains substantial complexity. Don’t forget that it’s rare to have only one app copy within a service. When they are numerous, it is necessary to understand where to address requests in case of a failure.
A service mesh network is meant to facilitate request delivery. Inside it, requests do not leave their separate infrastructure level, routed between microservices via proxies. Individual proxies here are called “sidecars” because they go alongside each service or application.
The sidecar proxy design pattern is also used outside of microservices. It eases the tracking and maintenance of applications by abstracting particular features away from the central architecture: interservice communication, security, and monitoring.
And what about the advantages?
- Scalability. I’ve already mentioned it, but repetition doesn’t spoil the prayer. Microservices are best if you have plans of scaling your app up or down (vertically with CPUs/memory/storage), out or in (horizontally with nodes/desktops/servers). Such flexibility is granted by each service being independent. Plus, it is possible to alter the application dynamically: turning components on and off to balance computational loads. This feature of the microservice architecture is the reason for its wide use in cloud deployments.
- Cloud-native apps. Speaking of the cloud. When replicated, monolithic instances require copying all contained data. Due to their sometimes immense size, such software can only run on full VMs. Microservices seem to have been made for Docker containers, where they are encapsulated along with runtimes, frameworks and libraries, even an operating system. This way, every business function is truly separated from one another, maintained and deployed in the cloud as one unit.
- Undemanding maintenance. Smaller pieces of functionality are easier to test and debug. Assume an enterprise supports a monolithic application spread onto thousands of copies. It would be bizarre and dangerous to update only a segment of machines. Consequences in this case are unpredictable. The microservice architecture is much more straightforward: here’s an interface, its implementation, and a definite number of services. Replace some of them and don’t touch the rest. Combined with continuous delivery, this strategy drives you closer to delivering failproof products to your customers.
- High resiliency. This point directly follows from the previous one. Since an application is decoupled into independent services, it’s more resistant to errors. A bug in one part of the code will not cause the entire system to shut down. Instead, you’ll only have to fix a service.
- Overall economy. A benefit in three parts:
- Building your application as individual, loosely coupled components means short release cycles. An enterprise has no need for a complete redesign to modernize one service, takes advantage of continuous delivery with cross-functional teams, and deploys services independently. All of these vastly decrease development time, accelerating time-to-market.
- Containerized images with microservices use file systems such as UnionFS, AuFS, OverlayFS. They create layers that describe how to recreate actions done to a base image. As a result, only layer updates multiply, leaving containers as lightweight as they are, saving valuable resources, and reducing company expenses.
- When functionalities are separate and autonomous, you can assign small teams to work on each. They don’t even have to be at the same place, which is wonderful in the age when remote offices are becoming ever more popular. Operating on powerful and pricey machines is an unnecessary luxury, too. Your engineers will be able to deploy any microservice with the simplest devices (read “x86 32 bit”). This architecture is practically capable of driving infrastructure costs down.
- A wide selection of tools. The microservice architecture helps avoid vendor lock-in. If components don’t depend on one another, they may run on different frameworks, use a number of libraries, or any other tools. The only condition is to establish communication via messaging protocols. You might even choose several languages to write code for a single application, surprisingly. That’s the beauty of it, as there are so many alternatives on the market: Scala, Kotlin, Groovy, Closure. But even with this rich variety, I still believe that Java suits microservices best. Why? I’ll explain in my upcoming posts.
This article is the first step in understanding microservices. I’ve given you the history behind the architecture, compared it to monoliths, and provided with some possible uses. We’ve also touched upon the tools that make a network of functions in your application less convoluted than it may seem. Microservices are still a young technology that is going to flip the industry around. Although not perfect, in my view, its pros outweigh the cons by a long way.
Tune in next time, when I’ll talk about but how microservices are designed, how they work and how to run them.