Scaling .NET Microservices Communications With NCache Pub/Sub

NCache is a 100% native .NET / .NET Core open source distributed caching solution from Alachisoft that can help boost your application performance and scalability by  leaps and bounds. It is a fast in-memory distributed cache that is capable of scaling linearly and provides strong cache synchronization features. You can add more servers as needed to be able to scale linearly. This article presents a discussion on how we can take advantage of NCache PubSub for scaling microservices.
 
This article discusses the following points,
  • What is Scalability?
  • What is a publisher – subscriber model?
  • What is Scalability?
  • What are Microservices?
  • What is a Pub/Sub Model?
  • NCache as In-Memory Pub/Sub for Microservices
  • Programming: Publishing and Subscribing Messages To/From a Topic
Pre-requisites
 
To work with and execute the code samples discussed in this article, you should have the following installed in your system.
  • Visual Studio 2019
  • .NET. Core 3.0
You can download .NET Core 3.0 from here.
You can download Visual Studio 2019 from here.
 
Additionally, to be able to work with the code examples illustrated in this article, you should include the following namespaces in your program,
  • Alachisoft.NCache.Client
  • Alachisoft.NCache.Runtime
  • Alachisoft.NCache.Runtime.Exceptions

What is scalability?

 
The term scalability of an application is defined as its capability to be able to meet increased transaction loads without slowing down. In other words, the scalability of an application is its ability to continue to work as fast even when the additional workload is added to meet user demands. A scalable application is one that is adaptable to growing demands (more concurrent users, more transactions per second, etc.) over time. Scalability of microservices-based applications is its ability to handle increased traffic and hence resource demands over time. Note that the ability to scale easily is one of the key benefits of microservices architecture.
 
Note that even though the terms scalability and elasticity both relate to increasing the application's ability to sustain workload, there are subtle differences between the two. While scalability relates to the ability of an application to sustain additional workload using its current or available hardware resources, elasticity relates to the ability of an application to sustain demands for additional workload using current and additional hardware resources that are added on demand.
 
 
Martin Fowler states: "In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery." Reference: https://martinfowler.com/articles/microservices.html
 
Microservices architecture is a variant of service-oriented architecture and can be used to build lightweight services that can be built, tested, deployed and managed independently. Typically, a microservices-based application consists of several loosely coupled services that are built to run on a heterogeneous mix of platforms. In other words, you can take advantage of microservices architecture to build flexible, extensible, independently deployable services running on disparate platforms.
 
Microservices architecture is a conglomeration of disparate technologies and platforms - a result of the ever-changing business needs of the industry. You can build a microservices-based application that can comprise of .NET, Java, PHP and etc. - you are not constrained to using the same technology throughout. In contrast, when working on a monolithic application you are constrained to using the same technology platform - it is extremely different for you to build a monolith with disparate technologies - the obstacles outweigh the advantages and hence it is not worth opting for.
 
Before the advent of microservices architecture, traditional applications used to be built in a monolithic way - the business functionality of the application used to be stored into a single process. As a result, the change cycles were all tied together. Hence if you would need to make a minor change in the application, you would be compelled to rebuild and re-deploy the entire application again. On the contrary, microservices architecture breaks your application's functionality into a number of independently deployable services - these services can be built, deployed and managed independently. You can improve the scalability of your microservices-based application by distributing these services across servers. Most importantly, if a change is needed in one part of the application you can deploy just the service or services that have changed.
 

What is a Publisher-Subscriber model?

 
Publish/subscribe messaging is a type of asynchronous messaging or a type of asynchronous service to service communication to be more precise. This type of messaging is usually used in serverless computing and microservices architectures. In a typical publish-subscribe (commonly known as pub-sub) model, the publisher of the event pushes the event notifications or messages to the subscribers. The pub-sub messaging model facilitates event notifications for distributed applications - it describes the way the publishers and subscribers can connect and communicate with one another asynchronously.
 
The publisher-subscriber model provides the following benefits,
  • Loose coupling
  • Better security
  • Improved testability
  • Improved performance
Figure 1 illustrates how a typical publish-subscribe messaging pattern looks.
 
Scaling .NET Microservices Communications With NCache Pub/Sub 
Figure 1: A typical publish-subscribe messaging pattern
 
As evident from the preceding diagram, there are three components in a typical publisher-subscriber pattern. These include the following,
  • Publisher - this is a component that publishes messages to a communication infrastructure
  • Subscriber - this is the component that subscribes to one or more published message(s)
  • Communication Infrastructure - this comprises of channels and is responsible for providing the necessary infrastructure for the communication to take place efficiently

Why Pub/Sub in Microservices Architecture?

 
The pub-sub model facilitates asynchronous service-to-service communication in microservices-based applications. In a pub/sub messaging system messages are exchanged amongst several application using an intermediary channel known as a topic, sans any knowledge of the sending or receiving applications. The pub/sub model is well suited in microservices architecture primarily because of its asynchronous, event-driven nature - you can take advantage of the pub-sub model to build high performant, reliable and scalable microservices architectures.
 

Pub/Sub Subscription types in NCache

 
When working with microservices-based applications, resilience is of utmost importance - it ensures that one or more microservice can recover from failures and the failure of one part of the application will not bring the entire application down.
 
NCache provides support for several pub/sub subscriptions between the publishers and subscribers. There are two types of subscriptions supported - these include the following (see NCache Docs for details),
  • Pattern-based Subscription
  • Durable and Non-durable Subscription
When using pattern-based subscription the clients can provide style patterns to subscribe to multiple topics. As soon as a subscription is created on a pattern-based topic, the clients receive messages that are published on topics that match the pattern.
 
NCache supports two types of durable subscriptions - Shared Durable Subscription and Exclusive Durable Subscription. In the former case, multiple subscribers can subscribe to a subscription and a round robin method is used to send messages to the subscribers. In the latter case, there is just one active subscriber for each subscription. Requests for new subscribers are not processed as long as the connection is active.
 

Topic and Content-based Pub-Sub Models

 
In a publish-subscribe model, messages are selected using either of these two ways: topic-based systems and content-based systems. Note that in a typical topic-based system the messages that are published to a topic are actually received by all the subscribers of the topic instantly. In a content-based system, messages are only delivered if certain constraints and criteria as defined by the subscriber are satisfied.
 
Note that in a queue a message reaches only one subscriber. On the contrary in a topic a message reaches each and every subscriber. While topics are well-suited for publisher-subscriber model, queues are a good choice for point-to-point.
 
In most pub/sub systems the publishers post the messages into some intermediate message broker or an event bus. The broker routes the messages from the publishers to the subscribers and may optionally also prioritize messages before they are routed.
 
 
The publisher/subscriber model of messaging is typically used in distributed applications as a way to exchange messages amongst several applications in a decoupled way - without these applications having to know the originator from where the message came from. Incidentally, these messages are then exchanged using an intermediary channel - known as a topic.
 
Scaling .NET Microservices Communications With NCache Pub/Sub 
Figure 2: Pub/sub dedicated cache
 
NCache is well suited for in-memory pub/sub for event-driven communication in microservices-based applications. You can enable the pub-sub feature in NCache by defining a topic that can be used by the services to publish events or to subscribe to it. A publisher is a component (or a microservice) that publishes a message to the NCache topic. The events are published to the NCache message broker. Each subscriber microservice takes advantage of an event handler to handle the appropriate event as soon as a publisher microservice has published a message to the NCache message broker. The entire process is captured in Figure 3 below.
 
Scaling .NET Microservices Communications With NCache Pub/Sub 
Figure 3
 
The simplified code snippet given below illustrates how messages can be published to NCache event bus.
  1. [Route("CheckoutItems")]  
  2. [HttpPost]  
  3. public async Task < ActionResult > CheckoutItemsAsync([FromBody] Item item) {  
  4.     var items = _itemService.GetItemAsync(item.Id);  
  5.     //Write your code to build the message here.  
  6.     //Assume that the message instance is named eventMessage.  
  7.     try {  
  8.         _eventBus.Publish(eventMessage);  
  9.     } catch (Exception ex) {  
  10.         _logger.LogError(ex, "Error publishing event");  
  11.         throw;  
  12.     }  
  13.     return Ok(items);  
  14. }  

Publish/Subscribe messages to/from a Topic

 
Assuming that your application is connected to the cache, the following code snippet illustrates how you can publish messages to the cache.
  1. try {  
  2.     string topicName = "demoTopic";  
  3.     ITopic demoTopic = cache.MessagingService.GetTopic(topicName);  
  4.     if (demoTopic != null) {  
  5.         //Write code here to build the message instance  
  6.         demoTopic.Publish(demoMessage, DeliveryOption.All, true);  
  7.     } else {  
  8.         //There is no topic in the name specified.  
  9.     }  
  10. catch (Exception ex) {  
  11.     //Write your error handling code here  
  12. }  
To delete a topic, you can take advantage of the following code snippet:
  1. try {  
  2.     string topicName = "demoTopic";  
  3.     cache.MessagingService.DeleteTopic(topicName);  
  4. catch (Exception ex) {  
  5.     //Write code error handling code here  
  6. }  
Assuming that the application is connected to the cache, the following code snippet shows how you can subscribe to a non-durable subscription.
  1. try {  
  2.     string topicName = "demoTopic";  
  3.     ITopic demoTopic = cache.MessagingService.GetTopic(topicName);  
  4.     if (orderTopic != null) {  
  5.         ITopicSubscription demoSubscriber = demoTopic.CreateSubscription(MessageReceived);  
  6.     } else {  
  7.         //There is no topic in the name specified.  
  8.     }  
  9. catch (Exception ex) {  
  10.     //Write your error handling code here  
  11. }  
  12. private void MessageReceived(object sender, MessageEventArgs args) {  
  13.     if (args.Message.Payload is Item item) {  
  14.         //Write your code to perform some operation here  
  15.     } else {  
  16.         // Error occurred  
  17.     }  
  18. }  
Assuming that the cache is already connected, the following code snippet shows how you can take advantage of the CreateDurableSubscription method to create a durable subscription.
  1. try {  
  2.     string topicName = "demoTopic";  
  3.     string subscriptionName = "demoTopicName";  
  4.     ITopic demoTopic = cache.MessagingService.GetTopic(topicName);  
  5.     IDurableTopicSubscription demoSubscriber = demoTopic.CreateDurableSubscription(subscriptionName, SubscriptionPolicy.Shared, MessageReceived, TimeSpan.FromMinutes(30));  
  6. catch (Exception ex) {  
  7.     //Write your error handling code here  
  8. }  
  9. private void MessageReceived(object sender, MessageEventArgs args) {  
  10.     if (args.Message.Payload is Item item) {  
  11.         //Write your code to perform some operation here  
  12.     } else {  
  13.         // Error occurred  
  14.     }  
  15. }  

Summary

 
The Microservices architecture consists of a collection of loosely coupled modules that can communicate with each other through simple APIs. The Pub/sub model is an elegant way for services to communicate in a typical microservices-based application.
 
You can take advantage of NCache as a messaging broker for asynchronous communications between microservices using the Pub/Sub model. This article presented a discussion on how we can work with the pub-sub model using NCache for improving the scalability of microservices-based applications.