A Java™ Reactive Stream Generator/Distributor

Reactive Programming is mainstream. Even Java9 is soon to include interfaces supporting a Reactive Streams publish-subscribe framework (JEP 266.) Reactive Programming is programming with asynchronous data streams. That introduction doesn’t address the fundamental issue of how to create/distribute an event stream. Once you have the stream, there are ubiquitous programs to deal with the stream. Creating/distributing the stream is the subject of this article. (1100 words)
 

Edward Harned (eh at coopsoft dot com, @ed_harned)
Senior Developer, Cooperative Software Systems, Inc.
October 2015 [updated January 2017]

Translations

Russian translation done by Everycloud (Alisa) on January 5th 2015.

Ukrainian translation done by Open Source Initiative (Sandi) on November 2nd 2016.

Preface

  1. A simple problem requires a simple solution.

    For a single Subscriber with a push requirement, a single Publisher can handle that single Subscriber. A single Publisher can also handle multiple Subscribers all with the same push requirement.

    single subscriber/publisher  single subscriber/publisher ... single subscriber/publisher multiple subscriber/publisher
  2. A complex problem requires more thought.

When multiple Subscribers with different requirements (say different filtering requirements) need the same Asynchronous Stream, then a single Publisher with multiple work threads may handle those Subscribers.

single subscriber/publisher with multiple threads

However, when there are many (tens/hundreds) Subscribers, then there is a need for a better solution.

  1. A remote, complex problem requires even more thought.

When multiple Subscribers reside in different locations and the Publisher resides separate from the rest then it's a whole new ballgame.

remote subscriber/publisher with multiple threads

Each Subscriber and the Publisher reside in their own JVM. The more Publishers, the more JVMs. When there are many (tens/hundreds) Publishers, then there is a need for a better solution.

  1. A compound problem requires the most thought.

When there is a combination of single Subscriber, multi-Subscriber, complex and remote Subscribers and there are many (tens/hundreds) of these Subscribers and Publishers, then there is a need for a unique solution.

single subscriber/publisher  single subscriber/publisher ... single subscriber/publisher    multiple subscriber/publisher   multiple subscriber/publisher with threads  remote subscriber/publisher with multiple threads

The Problem

Many Publishers means many JVMs. Having a one-for-one when there are hundreds of Publishers can result in an out of resource condition.

Many Publishers residing within the same JVM requires a method for keeping track of those Publishers. When each Publisher needs a thread, it can result in an out of resource condition.

The Java9 Flow framework (JEP 266) with the SubmissionPublisher Class is exceedingly efficient at spitting out Objects for the consumer tasks, but it can result in Out Of Memory Errors. (You can download the source code for a demo that uses SubmissionPublisher: DemoOut.java below.)

Any way you look at it, volume changes everything.

The Solution

The key to success is creating a semi-permanent Task for each Subscriber (subscriber-object.) The Publisher pushes the operation directly to the subscriber-object's queue; no need for creating a new Task. The subscriber-object then sends the pushed-items to the Subscriber, locally or remotely. Each Task executes in an efficient multitasking microservice with well managed threads. A picture is worth a thousand words.

Tymeac for Reactive Stream generation is the solution for keeping track of multiple push operations from any number of Publishers with the least amount of threads.

Tymeac is

  1. An efficient multitasking service that allows many resource-intensive applications to coexist in a single microservice.

  2. An efficient push engine allowing (Reactive Stream) Publishers to generate asynchronous streams for routing to Subscribers. Tymeac is not a "Publisher" itself. However, Since the Publisher exports the subscription work (filter, etc.) to Tasks the Publisher can run as a Tymeac Task itself and does not need to tie up a thread.

  3. A 100% pure Java push engine. Tymeac does not rely on outside packages to multitask. No preprocessors, no runtimes, no limits on hardware. If your application runs on Java SE, it runs without any modification or additional downloads with Tymeac.

  4. A flexible push engine that supports non-blocking backpressure. You can cancel (with a reason), pause/resume and alter any application at anytime.

  5. A resilient push engine that supports multiple timeout scenarios to handle unresponsive applications.

  6. A scalable push engine since Tasks are separate from the threads.

  7. A full featured push engine. Tymeac comes with nineteen JavaFX GUI's and client program access so you can monitor/alter the executing request and server. And five more JavaFX GUI's for backend setup.

                        Tymeac Menu
     

  8. A tunable push engine. Every Queue and Thread has a management structure to facilitate tuning.

  9. A versatile push engine with support for local access (same JVM) and remote access (RMI.)

  10. A well documented push engine. While JavaDoc is nice for API's, a professional microservice requires professional documentation.


Tymeac supports both communal and distinct processing.

How communal processing works:

Communal means community. In a community, players play nice with each other. Those that don't play nice ruin it for everyone. If a player is not going to play nice, then that player belongs in the "distinct" playground.

The Task is the heart of processing. A Task contains the variables/methods Tymeac needs to multitask requests. All user classes must extend this class. A Task can be

  • actively computing a stream or
  • timed-waiting for a resource (database/file access, internet access, or other service access) The timeout is a way to handle non-responsive accesses,
  • waiting for a send to complete (onNext, etc.)

While that Task is waiting for something, Tymeac can put the Task in suspension freeing the thread for other work. This is simple task-management that most operating systems use to schedule work on processors. Tymeac cannot do a context-switch. Tymeac relies on the Task cooperating by the Task setting pending action (need a send, need a resource) and returning from the method call rather than blocking.

Tymeac uses three queues: Active, Sending and Suspended. Each Queue has a dedicated thread pool.

Active Queue Sending Queue Suspended Queue

The Active Queue is for threads to fetch Tasks [low CPU usage Publishers and subscriber-objects] so the Tasks may compute that which they do. When a Task needs a resource the Task starts the asynchronous access, sets an estimated delay time it believes the access may take (the pending action here is a delay to await completion) and returns unless it also needs to send data. When a Task needs to send a stream (onNext, etc.) the Task sets a send-pending (the pending action) and returns. If there is a send-pending, Tymeac moves the Task to the Sending Queue. If there is no send-pending but there is a delay pending Tymeac moves the Task to the Suspended Queue.

The Sending Queue is for threads to fetch Tasks and use the Subscriber<T> to send the asynchronous stream (onNext/Error/Completed.) Once the send is complete Tymeac moves the Task back to the Active Queue, or, if there is still a delay outstanding, Tymeac moves the Task to the Suspended Queue.

The Suspended Queue is for waiting for an event to complete (delay to elapse or one of those asynchronous accesses to complete.) Once the event finishes, Tymeac moves the Task back to the Active/Distinct Queue.

In this way Tasks cooperate with other Tasks by not tying up a thread with blocking calls. Compute what you need. Relinquish the thread when you need to wait. The system can support hundreds/thousands of active requests with just a few threads.

How distinct processing works:

Tymeac uses one queue: Distinct. The Queue has a dedicated thread pool.

The Distinct Queue is for [high CPU usage Publisher] Tasks to do everything which they do; access resources, wait for those resources. Tasks cannot send using the Sending Queue. Tasks certainly may execute any communication they need, just not using the Sending Queue, that is for Communal Tasks only. Once here, they stay here until the request completes. The difference between a general thread pool and Distinct processing is that Tymeac manages persistence, messaging, queuing, threads, stall detection and recovery, recursion, logging, statistics, the user interface and much more.

A picture from the Solution, above:

Legend

SubscriberSubscriberSubscriber Subscribers
subscriber-objectsubscriber-objectsubscriber-object Subscriber-objects that run as Tasks on Tymeac.
The code that subscribers use to call the Publisher or to call Tymeac directly.
Publisher Publishers that run as Tasks on Tymeac.
Tymeac Tymeac
Communal Communal Request Queue/Threads on Tymeac.
Distinct Distinct Request Queue/Threads on Tymeac.

Picture

local JVM      Remote JVM
            Local JVM                                               Remote JVM's

Conclusion

If you need a secure, reliable, manageable, and fault tolerant Asynchronous Stream Builder/Distributor for any purpose and do not wish to start at the beginning designing and testing one yourself, then you need Tymeac.

References

Download the source code for the article here.

Download the project from SourceForge.net, project: TymeacRSE

JEP 266
http://openjdk.java.net/jeps/266

Introduction to Reactive Programming
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Reactive Streams org
http://www.reactive-streams.org/

About the Author

Ed Harned is a software developer with over thirty years industry experience. He first led projects as an employee in major industries and then worked as an independent consultant. Today, Ed is a senior developer at Cooperative Software Systems, Inc., where, for the last eighteen years, he has used Java™ programming to bring parallel solutions to a wide range of tasks.

© 2015 - 2017  E.P. Harned  All rights reserved