Hands-On Docker for Microservices with Python
上QQ阅读APP看书,第一时间看更新

The characteristics of a microservices approach

The monolith approach works until the point it doesn't. But, what is the alternative? That's where the microservices architecture enters into the scene.

A system following a microservices architecture is a collection of loosely coupled specialized services that work in unison to provide a comprehensive service. Let's divide the definition a bit, in more specific terms:

  1. A collection of specialized services, meaning that there are different, well-defined modules.
  2. Loosely coupled, meaning that each of the microservices can be independently deployed.
  3. That work in unison—each microservice is capable of communicating with others.
  4. To provide a comprehensive service, because our microservice system will need to replicate the same functionalities that were available using a monolith approach. There is an intent behind its design.

In contrast to the previous diagram, the microservice architecture will look like this:

Each of the external requests will be channeled to either Microservice A or Microservice B, each one specializing in a particular kind of requests. In certain cases, Microservice B communicates with Microservice C, not directly available externally. Note that there may be multiple workers per microservice.

There are several advantages and implications to this architecture:

  1. If the communication between microservices is done through a standard protocol, each microservice can be programmed in different languages.
Throughout the book, we will use HTTP requests with data encoded in JSON to communicate between microservices. Though there are more options, this is definitively the most standard and widely-used option, as virtually every widely-used programming language has good support for it.

This is very useful in cases where a specialized language is ideal for a specialized problem, but limiting its use so that it is contained, not requiring a drastic change in the company. 

  1. Better resource utilization—if Microservice A requires more memory, we can reduce the number of worker copies. While on a monolith, each worker requires the maximum resource allocation, now each microservice uses only the resources required for its part of the whole system. Maybe some of them don't need to connect to the database, for example. Each individual element can be tweaked, potentially even at the hardware level.
  2. Each individual service is smaller and can be dealt with independently. That means fewer lines of code to maintain, faster builds, and a simpler design, with less technical debt to maintain. There are no dependency issues between services, as each can define and move them at their own pace. Performing refactors can be done in a more controlled way, as they won't affect the totality of the system. Furthermore, each microservice can change the programming language it's written in, without affecting other microservices.
From a certain point of view, the microservices architecture is similar to the UNIX philosophy, applied to web services: write each program (service) to do one thing and do it well , write programs (services) to work together and write programs (services) to handle text streams (HTTP calls), because that is a universal interface.
  1. Some services can be hidden from external access. For example, Microservice C is only called by other services, not externally. In some scenarios, that can improve security, reducing the attack surface area for sensitive data or services.
  2. As the systems are independent, a stability problem in one won't completely stop the system. This reduces critical responses and limits the scope of a catastrophic failure.
  3. Each service can be maintained independently by different developers. This allows for parallel development and deployment, increasing the amount of work that can be done by the company. This requires the exposed APIs to be backward compatible, as we will describe later.