Reactive Microservices — 2

Kapil Raina
7 min readJun 15, 2021

--

This is an 8 part series on reactive microservices. [1 , 2 , 3, 4, 5, 6, 7, 8]

Microservices — the architecture style of developing single responsibility, cohesive, autonomous, domain driven services is a departure from a “monolithic” architecture. The very nature of microservices is that is a distributed system. If microservices are layered on existing architecture patterns, programming models, platforms and stack, which basically was meant for (non-distributed) monoliths, then microservices architecture will only be as much mature and effective.

Thus “Reactive Microservices”.

Microservices-based architectures that have their foundation in the core concepts of Reactive systems:

asynchronous and message-driven, highly resilient against failures, elastically scalable on demand, and consistently responsive.

The Reactive — Microservice Yin-Yang

Reactive journey leads to microservices or a well-planned Microservices journey leads to reactive systems. There is a huge fertile common ground between these two concepts. Microservices provides a pervasive application for Reactive principles.

Microservices, at its core, is the way independent, distributed services connect to form a user journey of an application. Individual, independent services are straightforward, the real value lies in composability. The composability of microservices is thus all about distributed systems and the way this core distributed nature is handled. Reactive is a natural and mature way to do precisely that.

Reactive Microservices

Patterns

Asynchronous Integration — Event Driven — Async Communication — Reactive@System

When a system moves from being a monolith to a distributed microservice, the complexity in the co-ordination between microservices increases manifold. Due to large number of distributed microservices, synchronous calls (like synchronous REST) between them would quickly lead to another spaghetti like behavior which was originally a bottleneck in the monolith. This tight coupling between microservices results in a chatty services that quickly gives away the advantages which are to be gained from microservices, especially if the service cohesiveness is low and it depends on multiple other microservices to complete a user request or a transaction.

(Ref: https://www.simplethread.com/youre-not-actually-building-microservices/).

Reactive Manifesto’s Message-Driven trait leads to an Event-Driven (also Event-First) architecture. Message driven microservices application can be based on events or actor-model or a combination of both.

Message-Driven is the cornerstone of building reactive microservices where integration between microservices is based on asynchronous messages. Relying on synchronous REST creates coupling that affects productivity, scalability, and availability.

With reference to Domain Driven Design concepts, events enable message passing within and outside the bounded context, which doesn’t create coupling between services. Events themselves don’t force asynchronous message passing, but it is the recommended design to leverage complete decoupling.

Event and message are often used synonymously but reactive microservices have formal definitions:

  • Message: Is any payload sent over the wire.
  • Event: Is a type of message that announces an important fact in domain like completion of processing or a state change. Event is not directed for a specific consumer. Events are immutable and always from past. Events “happen” when commands are processed. Reacting to an event whenever it happens is core of being event-driven. Event and Reaction are related by Cause & Effect or causality.
  • Command: Is a type of message that is directed at a consumer that has the responsibility to fulfil it. It denotes intent of an action within a system that is likely to change the state. Commands are transactionally consistent.
  • Query: Is a type of message that is directed at a consumer that is responsible for returning view data without any side effect of state change. Queries require response in timely fashion.

Reactive Microservices can employ async event driven philosophy in many ways:

Event Notification

System sends event message to notify other systems of a fact in domain, without making any assertions as to how the event would be processed. Thus, notification carries state of fact already happened.

Event Notification

Event-Carried State Transfer

Microservices own their state in their isolated data store which is not shared with any other microservice directly and is accessible only over an API. There are use-cases where the data from a microservices need to be used in real-time by another microservice but making network calls to the API of the owning microservice is not an option. Instead the owning microservice publishes the state change events that the interested microservice can consume and maintain their own read-only set of same data. When needed, this local copy of data is used to optimally query and compose the response.

Event Carried State Transfer

Event-Sourcing

It is a design technique where the state of a sub-domain is stored as a set of events in an event-store, without maintaining a current state. Traditional state-based persistence fails to capture the intent and lose the ability to know how current state has come about. In ES, Current state is always derived from the events from event-store. At any point state of the system can be built by running the events in sequence.

ES eliminates needs for Update and Delete from CRUD

Event Sourcing

CQRS

As a design concept, Command Query Responsibility Segregation doesn’t rely on events, but it is commonly combined with event driven techniques like event sourcing, since separating command and query design and stores fits ideally in an event driven model. It simply acknowledges the fact that write models may be inefficient as read models and thus should be managed separately, even using different types of databases.( https://martinfowler.com/bliki/PolyglotPersistence.html)

Command component, where the state changes and processing happen, just need to send the results out as events to one more many query processors which are optimized for view and search querying model.

When used with ES, it solves the problem of having to query the event-store or multiple event-store to answer a query. Events are inefficient to query since all of events would have to be replayed to build aggregate states and then search among them. And more so of queries are spanned across multiple aggregates.

Even without ES, it is inefficient to use aggregate root as starting point to answer queries.

CQRS

Event-Triggers

Event Trigger

Asynchronous Execution — Non-Blocking Parallelism — Async IO — Reative@Application

Async design should be pervasive bother across services as well within the computation of a single service wherever possible [*]. Most reactive frameworks enable concurrent async non-blocking execution methods like java Completable Future and RxJava Observable & Reactive Streams. Non-Blocking is an optimization over concurrency. Modern CPUs come with multiple cores, so running tasks concurrently optimizes the processing since more threads can take advantage of multiple cores. But it is not enough to spawn threads for complex operations that can handle the IO separately. Getting stuck in a blocking state a thread consumes resources and results in contention especially at scale. Typical cases of thread blocking are IO blocking (file, network) or database access using blocking drivers.

The solution many reactive frameworks and tools employ is to isolate blocking into a different (OS)threads, freeing the main-worker thread or event-loop that has been assigned to handle the edge request to complete other tasks.

The async or concurrent execution in reactive programs is also paired with event-driven result aggregation that doesn’t block the client thread after initiating async computations, in a wait state to aggregate the results. The reactive programming constructs such as streams processors, ensure that the main thread or worker threads are freed up immediately.

Asynchronous integration and computation together provide maximum concurrency and ensure better compute resource utilization and improved performance by reducing the latency that sync execution has due to thread contention issues. The responsiveness aspect of reactive manifesto relies strongly on these two.

[*]: Concurrency across servers is “Distributed/Distribution”. Concurrency in the same server is “Parallelization”. The need for two types of processing is similar i.e. optimize processing combined with asynchronous execution.

Few References:

This is an 8 part series on reactive microservices. [1 , 2 , 3, 4, 5, 6, 7, 8]

--

--

No responses yet