“This is the second day of my participation in the First Challenge 2022.

Hello, I’m looking at the mountains.

Earlier, we talked about the evolution of architecture, including monolithic architecture, SOA architecture, microservice architecture, and no service architecture. The whole process is shown below:

Currently, no service architecture is not mature and can only meet some simple scenarios. So when people design software architectures, the first choice is microservices architecture. Then we talked about how to transform the single architecture into microservice architecture, and recommended the strangling mode to realize the system microservice step by step.

In the process, we run into a major flaw in microservices architecture: the distributed illusion, where distribution is all that microservices are (if and if).

why

The main reason for distributed singleton is that in-process method calls are simply replaced with inter-process remote calls.

As you can see from the figure above, requests for A singleton architecture between modules A and B are implemented through in-process communication (usually method calls); In the microservices architecture, there are REST or RPC calls between the two. Regardless of the differences in process and message notification mechanism, the communication form between module A and module B in the two architectures is exactly the same:

In this case, module A and module B are coupled together, and if either side changes the request contract (method signature or interface parameter), the other side must change it simultaneously. To make matters worse, due to the unreliability and instability of microservices architecture services, which communicate with each other over a network, the probability of errors is greatly increased, making the call relationship between modules more fragile.

The network request between module A and module B is synchronous invocation, occupying one network connection and at least one thread in the request process. If the bearing capacity of the service where module A and module B are located is different, it is likely that the service where Module B is located will be filled up, and subsequent requests from module A will be blocked until the request times out.

So what’s the reason why people don’t realize it’s not a good idea? There are two reasons:

  1. Want to realize the relationship between modules in a single architecture in a microservice architecture;

  2. Want to achieve strong data consistency in distributed applications.

For the problem of data consistency in microservice architecture, we can refer to the summary of data consistency in microservice system.

Now let’s focus on how to solve the first problem.

methods

For the relationship between modules, mainly lies in the communication mode. For the query request, due to data dependence, the coupling between modules is natural. What we want to decouple here is the module call when the data changes (increase, delete, change).

By analogy, what if we want to notify someone of a message? Generally speaking, there are two ways:

  1. Peer-to-peer active notification, directly find the person, call the person, unable to continue to call, to ensure that the other party can receive the message;
  2. Click on a broadcast notification (for example, a group announcement, a company bulletin board, etc.) that requires the recipient to actively check the announcement.

These two modes correspond to the two modes of message passing in our system design: Command and Event.

Commands and Events

A Command is a request that an action must be performed from a source to a destination. This mode has the following characteristics:

  • Clearly know the initiator and the performer, the initiator depends on the performer;
  • Request sending mode is generally point-to-point synchronous request, generally RPC request;
  • An action has occurred or is about to occur and may be cancelled because the performer refuses to perform it;
  • The executor may refuse to perform;
  • The executor will clearly inform the initiator of the execution of the order: rejection, success, failure, etc.
  • In order to ensure the effective contact of instructions, the initiator will repeatedly call the executor when the network times out, so the executor needs to realize the request idempotence.
  • The executor may become the initiator of the next order.

An event is an event published publicly by a producer that has already taken place. It indicates that an action has taken place and some status changes have taken place. Consumers subscribe to these events and then respond to them. This mode has the following characteristics:

  • Events have clear producers, but consumers are not clear and may not even exist;
  • Generally use message middleware to achieve event sending, storage, transmission, etc.
  • An action has already happened, cannot be changed, cannot be reversed, an event is an objective description of what has already happened;
  • The consumer chooses how to handle the message: execute, discard, and so on;
  • After the consumer processing is complete, there is no need to reply to the producer;
  • General message middleware uses “at least once” notification mechanism, so consumers need to implement idempotent message processing;
  • Consumers may become the producers of the next event.

Depending on the request pattern, the dependencies change:

In the instruction mode, module A calls module B, which is A direct call, and module A needs to rely on module B. In the event pattern, module A sends events to the messaging middleware, and other services that need to subscribe to events get them directly from the messaging middleware. This will result in dependency inversion, in which module B depends on module A. This is A good way to decouple module A from module B.

Redefine microservices

Let’s go back to our question:

At this time we will be more clear, because the whole system were used in the command mode, the last time the caller rely on lower level, because it is a synchronous request, rely on, will happen this dependence, will be coupled together, the whole system changes, changes everywhere, also is our social phenomena in attacking monomer architecture often said.

At this point, we can rely on the chain to break with the event pattern. But be careful not to overcorrect by switching to event mode altogether, which would be another fire pit. In general, we will transform the system into something like the following:

According to the specific situation of the business, we can summarize the transformation results:

  1. The request received by service A may be an event or an instruction;
  2. Service A sends instructions to service B as well as events to the messaging middleware;
  3. Service B receives the command and starts executing. After executing, it may send an event to the messaging middleware.
  4. Service C is event-oriented, processed after receiving a message from the messaging middleware, which may send an event or instruction.

It is important to note that there are also operations within each service. For abstraction, the instructions, events and operations in the whole system are shown as follows:

  • Input: Take an instruction or event as input to start the whole business execution;
  • Intra-service operations: There is execution logic inside the service, such as operating on databases, accessing caching services, and so on. Optional, 0-N;
  • Instruction invocation: Synchronous invocation depends on the service, sends instructions, and gets results. Optional, 0-N;
  • Publish events: Publish events in the form of messages, generally to messaging middleware, other publish subscribe messaging middleware, and perform the behavior required by the event. Optional. The number of events is usually 0-1.

The process of architectural design is not either-or, all instructions create coupling, all events make development difficult and boundaries unclear. We need to look at the two models in a rational and balanced way.

At the end of the article to summarize

This paper starts from the distributed single unit trap, tells the problem caused by the distributed illusion, and then solves the problem through the combination of events and instructions. Microservice is a relatively perfect architectural style at present. From monomer to microservice architecture, it is necessary to realize the upgrade of architecture, so the invocation mode will not remain unchanged. This is a trap that we need to avoid when we make a new system.

Recommended reading

  • What are microservices?
  • Microservices programming paradigm
  • Infrastructure for microservices
  • Feasible solutions for service registration and discovery in microservices
  • From singleton architecture to microservice architecture
  • What other options do we have besides microservices?
  • How to effectively use Git to manage code in microservices teams?
  • Summary of data consistency in microservice systems
  • Implementing DevOps in three steps
  • System Design Series how to Design a Short Chain Service
  • System design series of task queues
  • Software Architecture – Caching technology
  • Software Architecture – Event-driven architecture

Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow.