Reactive Programming

My foray into reactive programming began sometime last year. I had been seeing blogs and articles referencing this programming paradigm which appeared to offer more in terms of scalability than traditional blocking and imperative programming patterns. I did a deep dive into the concept and have since started using Reactive Programming on my projects.

So what is reactive Programming?

Reactive Programming is a programming paradigm that relies on the propagation of events in a system as a means of communicating change. These events are propagated through continuous streams of information, in which subscribers can plug into these streams and orchestrate code in response to those events. The paradigm is declarative, asynchronous, and non-blocking by nature.

Reactive Programming as a concept isn’t particularly new. In fact, it’s based on the Observer design pattern which is one of the oldest design patterns employed in software architecture. Why RP is interesting though is because it exposes several advantages over traditional synchronous programming patterns which suffer scalability issues as the number of requests starts to increase. Let me explain.

Contrasting Traditional Request-Response blocking model with Reactive Non-blocking model.

A Client requests a resource on a Server by making an API call to one of the Server’s endpoints. The Server on accepting the request spins up a new thread to cater to the client’s request. All the processing that pertains to the client’s request happens within the context of the spun-up thread and the client must wait (block) while this processing is ongoing. When processing is completed, the response is returned to the client and the thread is closed. But there are a few problems with this approach:

  • Most servers run bounded thread pools; meaning there is an upper bound on the number of threads that can be run within the server. A traditional synchronous model will cause you to max out your thread pool when you’re experiencing high traffic. Since you’re essentially spinning up a new thread for each incoming request.
  • Because services block until they receive data from upstream, there is an under utilisation of CPU and system resources with components essentially idle while waiting for response.

Consider a practical example: A waiter at a restaurant.

Say you’re at a restaurant, hungry as hell. You place your order with the waiter hoping he returns with your food on time. The waiter (Client) heads to the kitchen and gives your order to the chef (Server). While the food is being prepared, the waiter waits with the chef for about 10mins and when your food is ready, the waiter packages it nicely and serves you at your table. After serving you, the waiter moves on to attend to the next customer.

You can see the inherent flaw with the waiter’s process. First of all, for a good 10mins, while the waiter is attending to you, he does not take any orders from any other customer. This will leave a lot of frustrated customers at the restaurant who will feel unattended. Moreover, while the waiter is in the kitchen waiting for your food to be ready, you are not aware of what’s going on because you’re not given any feedback on the status of your food.

A better approach will be a reactive model, where the waiter places your order with the chef and informs the chef to buzz him when the food is ready. This frees the waiter to go on and take the next customer’s order. The waiter can attend to several customers and drop their orders in the kitchen and when he’s notified that food is ready, he can pick up the food and serve the waiting customer. This approach models the reactive programming paradigm. The waiter (Client) subscribes to the “food is ready” event from the chef (Server) and executes appropriate action (serving the customer) when the event is triggered.

Properties of Reactiveness

The essential properties of a Reactive system are outlined in the Reactive Manifesto. Systems built from the ground up with full reactive support are such that the different sub-components are wired together in declarative style syntax to actualize reactive streams. The following characteristics were outlined in the Manifesto:

  • Responsive: points to liveness and availability. Reactive systems can do more work on available hardware than their non-reactive counterparts, hence are more responsive.
  • Resilient: points to fault tolerance. When systems are resilient, they are capable of maintaining responsiveness even in the presence of faults.
  • Elastic: ability to maintain responsiveness even as workload increases.
  • Message Driven: meaning that communication is largely asynchronous. Also, there is loose coupling of components. This is because each component only needs to worry about sending, receiving messages and implementing their localised logic.

Now we’ve established an understanding of reactiveness, we can take a look at Observer; which is the first inspiration for reactive programming.

The Observer Pattern: Publish and Subscribe

The Observer Pattern is a behavioral design pattern that allows multiple objects (Observers) to receive notification of state changes on an object (Subject). The subject maintains a reference to its subscribed observers and pushes updates to them as they happen.
java.util package has a primitive implementation of the Observer pattern (now deprecated).

Observable

  • observers: List<Observers>
  • addObserver(o: Observer)
  • removeObserver(o: Observer)
  • notifyObservers(data: Object)

Observer

  • update(o: Observable , data: Object)

Diagrammatic Representation of Observer Pattern. source: Wikipedia

The observable maintains a list of observers which is updated via the addObserver and removeObserver methods. In order to notify registered observers, the observable uses notifyObservers which internally calls the update method on all the registered Observers.

public void notifyObservers(Object arg) {
    Object[] arrLocal;

    synchronized (this) {
        if (!changed)
            return;

        // initialize local reference to observers array
        arrLocal = obs.toArray(); 
        clearChanged();
    }

    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}


The issue with this approach is that each observer is notified via a synchronous call to the update method. Meaning each observer does not receive notification until the previous observers in the loop have completely processed the update. Unless of course, the observer handles the update asynchronously. Even so, there’s a chance an error from an observer’s update call can cascade out of the loop meaning subsequent observers in the loop are starved of the notification.

Java’s primitive observer pattern implementation was deprecated as of Java 9. Now, we have a reactive streams implementation in the java.util.concurrent.Flow class, in keeping with the Reactive Manifesto.

There are other implementations of reactive streams in the java ecosystem:

  1. RxJava
  2. Reactor
  3. Vert.x
  4. Akka Streams

I personally use Reactor on my projects, though I also have respect for RxJava. I have no experience with Vert.x and Akka Streams. I haven’t felt the need to explore them as Reactor and RxJava have sufficed for my reactive coding needs.

In subsequent notes, I’ll explore concepts in the Reactor module. And dive deeper into the Reactive Programming paradigm.