Mule developers build integration solutions that must cater to high volumes of realtime and batch data. In some cases, the size of payloads can be larger than memory. In others, API solutions may have to handle high rates of API calls per second.
Horizontal scalability, whereby Mule instances are deployed across multiple virtual and physical machines, is costly. Thus, a single Mule instance must utilize to the maximum underlying processing and storage resources as it handles traffic. Its ability to handle more traffic with more underlying resources is what we refer to as vertical scalability.
Mule 4 addresses vertical scalability with a radically different design using reactive programming at its core. With the aim to maximize throughput, our engineers have focused their efforts on achieving higher concurrency through non-blocking operations and a much more efficient use of CPU, memory, and disk space.
What is Mule 4?
Mule 4 is the newest version of Mule runtime engine which uses reactive programming to greatly enhance scalability. A Mule application is an integration application which incorporates areas of logic which are essential to integration. These logical domains include:
- Connectivity
- Transformation
- Enrichment
- Validation
- Routing
- Error Handling
- Security
A Mule application is developed declaratively as a set of flows. Each flow is a chain of event processors. An event processor processes data passing through the flow with logic from one of the said domains. Mule 4 uses reactive programming to facilitate non-blocking execution of the event processors. This has a significant impact on a Mule application’s ability to scale the amount of events it can handle concurrently. Each event processor belongs to a module. Modules are added to Mule runtime engine as required by Mule applications. Mule 4 is entirely extensible through custom modules. The Mule SDK, a new addition to the Mule 4 ecosystem, enables you to extend Mule with new modules. Custom modules benefit from the same scalable reactive capabilities of Mule runtime engine.
Reactive programming
The advent of single page web apps and native mobile apps has fueled users’ appetite for fast and responsive applications. We want data to be made available now. We have become accustomed to push notifications. Architectures in the back-end have evolved to cater to front-end responsiveness and drive elasticity and resiliency through event driven microservices. Reporting requirements have evolved from entirely retrospective analysis to include real-time predictive analytics made possible by event stream processing.
Reactive Programming (Rx) arose in the .Net community to give engineers the tools and frameworks needed to cater to this appetite. It quickly spread to other languages. It became JavaRx in the Java world and Spring has fully embraced it with Project Reactor. Reactor now lies at the heart of Mule 4’s internal architecture.
Reactive merges the best from the Iterator and Observer design patterns, and functional programming. The Iterator pattern gives the consumer the power and control over when to consume data. The Observer pattern is the opposite: it gives the publisher power over when to push data. Reactive combines the two to get the beauty of data being pushed to the subscriber when it is available but in a way that protects the subscriber from being overwhelmed. The functional aspect is declarative. You can compare functional composition of operations to the declarative power of SQL, the difference being that you query and filter data in realtime against the stream of events as they arrive. This approach of chaining operators together avoids the callback hell which has emerged in asynchronous programming and which makes such code hard to write and maintain.
Spring Reactor Flux
Try out the online tutorial on Reactor. Here’s an example from an article published on InfoQ:
The Flux API represents a stream of events. We create a flux from the array of words: [“the”, “quick”, “brown”, “fox”, “jumped”, “over”, “the”, “lazy”, “dog”]. As we process each word in the array we form another flux from all the letters in the word and merge each new flux into one for all the words using the .flatMap() operation. We then purge out repeating letters, sort them and merge them with a flux of incrementing integers [ “1. a”, “2. b”, … ] using the .zipWith() operation.
A key takeaway is that all of the above is declarative. Through functional composition we can declare how we want processing to be done without actually implementing it. But nothing happens till we subscribe. Subscribing to the final flux produces the stream of strings we see in the output.
In my next blog post, I’ll dive deeper into reactive programming and non-blocking code execution in Mule 4. You can try Mule 4 today and download our whitepaper, Reactive programming: Foundations for high scalability in Mule 4, to get started.