Have you wondered why there are gaps between bridge segments? Because materials used in constructions of bridges usually expands in summer and contracts in winter. Without these “breathing” spaces, the bridges develop deep cracks that could lead to dangerous path holes. Worst, destabilize the bridge support and strength.
Similarly, developing tightly coupled Microservices creates friction between the moving parts. Problems in one part percolate into another — leading to slow performance and failures. For example, websites usually go down during high traffic times. High traffic makes CPU run full throttle and makes RAM run out of storage.
Have you used SendGrid to send emails? It sends email in an asynchronous process. It will accept email request, line it up for processing, and pick to process eventually.
This Asynchronous processing has 2 major advantages. One – load leveling during high traffic. Second – auto healing after temporary service failures.
By decoupling services from incoming requests, we are enabling worker processes to continue without any interruptions.
Load leveling can be achieved by buffering incoming requests. Buffering in itself can be done in various ways. Message queues are very often used to buffer incoming messages. Sometimes even relation and NoSQL databases are used for load leveling.
In my opinion, message queues are the best choice for the job. As databases can itself be a point of failures, what’s the fun in introducing another? Queues on the other hand store data in files on storage disks. They are more reliable than databases because of this simple design.
Microservices can be involved in a multi-tent and multi-dependency. Meaning — their communication paths are criss-cross with each other. They have managers to serve, and juniors to mentoThisese many-to-many relationship create confusion especially when there are failures. Worst, this confusion can become havoc when there are several failures.
Enabling applications to automatically heal from service failures, and faults will help business applications keep workflow smooth. Operations will return to normal once the failure state is gone. Managers and leads can relax even if these failures happen during odd hours.
Queue centric pattern:
Asynchronous processing and Loose coupling in Microservices can be achieved through Queue centric pattern. Queue centric pattern helps our systems to react gracefully to faults, failures, and slow performances in dependencies.
We focus on solving speed and safety problems using multiple queues. A single message is passed on between queues. Each queue corresponds to domain specific purpose.
You can also have a queue for each dependency — doing so faults could be retired easily. In fact each and every dependency exhibit different types of faults. When we put a queue per dependency, we can customize our retrial mechanism.
Queues are a reliable medium to send messages between resources, or applications.
Worker processors at each level pick a message, processes it and drops it into next bucket on completion. Failures during the processing will prompt for another trial; eventually pushing onto the next bucket.
Such containerized processing improves speed and safety. It decouples dependencies and resources, permits retrial in the case of failures, and allows load leveling to support throttling in the case of high volume.
If apps are tightly coupled to each other, an outage in one place could disastrously slow down complete or partial business operations.
Also, lossy message communication could leave data inconsistent and/or unusable across apps.
To implement QCP, we will require
- A Queue storage toolkit.
- A Repository class for queue operations.
- A worker process to pull queue messages.
Queue storage could be MSMQ, ActiveMQ, or ZeroMQ. I myself have extensively used cloud Storage queues in our Cloud apps. Together with WebJobs StorageQueue reduce the amount of boilerplate coding. It is easy, and faster to develop QCP code using Web jobs.
A repository class will provide primitive operations like SendMessageToQueue(string message, string queueName);
GetMessageFromQueue(string queueName). These methods help us add and retrieve queue messages.
A worker processor could be scheduled to call GetMessageFromQueue method every 1 minute or so to process new messages. That’s it, you Queue centric pattern code base is ready.