LabVIEW Craft
  • Blog
  • About
  • Contact
  • Tools
  • Resources

Message Centric vs. Data Centric Communication

12/7/2016

4 Comments

 
Jon McBee
I recently gave a presentation at a local user group on message transport abstraction and how it can be used to abstract communication between actors running in LabVIEW and thin clients running in web browsers.  During the presentation an interesting question came up regarding why I chose to make communication between my actors data centric instead of message centric.  This question triggered a lot of thinking on my part and after a few days of rumination I decided that a blog post on the topic was warranted.
Picture
​​During the presentation I held a mini code review during which I showed a version of a simple actor that used a mediated message bus to communicate with other actors through abstracted message transports. In my implementation actors register as publishers and subscribers of data on the message bus and their actions are driven by data arriving in their inbox.  I pointed out the subtle difference between this data centric implementation and the Actor Framework, in which messages typically map to actions being requested from Actor to Actor.  The question raised was along the lines of, "What benefit does this implementation have over how the Actor Framework does it?".  It is a good question and I am going to attempt to provide a good answer below.
​Before diving directly into the technical details let me preface the discussion by defining what I mean by data centric communication and message centric communication within the context of actor oriented programming.  In this discussion I will refer to actors (lower case "a") in the general sense as a system of concurrently running software modules that receive messages and perform some kind of computation on them.  There is a well known framework written by National Instruments known as the Actor Framework (upper case "A") that implements the actor model and I will be comparing Actor Framework Actors to my own implementation of actors.
Picture
​In general actors communicate with each other by sending asynchronous messages that are stored in mailboxes until they are processed.  This discussion focuses on two different ways to structure the messages that are sent between actors.  We will first look at how the Actor Framework uses the command pattern to send message centric messages between Actors, where each message typically maps to an action that one Actor wants another Actor to perform.  We will then look at my actor implementation which uses the mediator pattern to send data centric messages between actors, where each message typically maps to a specific data type.  This discussion will focus on how the difference between data centric and message centric communication impacts the overall design of an actor oriented program.
The Actor Framework
If you are already familiar with how the Actor Framework handles messaging between Actors then feel free to skip ahead to the next section.  As a note, I decided not to discuss the low-coupled messaging pattern that can be used to communicate in a more loosely coupled fashion between Actors, mainly because I discussed it in detail in this blog post.
Picture
Let's start by looking at how the Actor Framework handles messaging.  The image above is the block diagram for Actor Core.vi, this block diagram is essentially a typical consumer loop where the queue is replaced by the "Priority Queue" object and the case structure is replaced by the Do.vi, which is nested within the Receive Message.vi.
Picture
Picture
The Do.vi's job is to take incoming messages as they are dequeued from the Priority Queue, and dynamically dispatch based on the message class type to run the appropriate method of the Actor.  This dynamic dispatching on the Do.vi is an implementation of the Command Pattern, a well known behavioral design pattern.
Picture
Note that the Do.vi is a method of the message class not the Actor, and that when the Actor dequeues a message object it will dynamically dispatch on the message classes Do.vi within Actor Core.vi.  Each Do.vi calls one of the Actors methods and this is how the Actor does work.  For a deep dive on low coupled messaging in the Actor Framework see this post.
Picture
One thing I want to point out is that the Do.vi has an Actor.lvclass object as an input and that within the Do.vi the Actor.lvclass is cast to a more specific child (Beta.lvclass in the image above).  Once the to more specific class has cast the object type to Beta.lvclass one of Beta.lvclass's methods can be called, in this case it is the Update Data.vi.  This messaging scheme tends to necessitate one message class for each Actor method that needs to be invoked by message.
Picture
With the command pattern based mesaging scheme the messages tend to map to actions or commands.  The Actor Framework's use of the command pattern makes the message itself the primary means of interaction between Actors.  With this message centric approach developers write applications that focus on sending messages between Actors.
Mediator Pattern Actors
Now let's take a look at the mediated message bus actor implementation I was presenting on at the LVUG.  This implementation uses the mediator pattern as a publish-subscribe mechanism for passing data between actors (for more information see these blog posts: Get/Set Control Values by Index, Publish/Subscribe with the Actor Framework, and You've Got Mail).  The mediator acts as a message bus that allows actors to register anonymously as publishers and/or subscribers of data.  In order for a publisher actor and a subscriber actor to communicate they have to agree on two things; the name of the data and the type of the data. 
Picture
Topics provide the basic connection between publishers and subscribers.  Each Topic has a unique name and a data type.  The data type is a subclass of a Message class where each child maps to a particular data type.
Picture
Picture
Notice that these messages are not command pattern messages, instead of mapping to an action each message simply represents data.  Because the data messages are classes more complex data types can be introduced as children of the data Message Interface.  Each data message has a key that acts as a name for the data in the message.  As an example, a Topic for temperature data could be created with the name Temperature and the type Message Double.lvclass.  All temperature data would be published to this Topic.  As data points from individual sensors are published the name of the sensor could be included as the key for the message.  This makes the application scalable because new topics don't need to be created as new temperature sensors are added to the system.
Picture
The image above shows the actor core for a simple mediator actor.  In this case the actor has registered as a subscriber to a Topic named Broadcast, which handles all temperatures in the system.  When a message comes in from the Broadcast Topic the actor handles it by casting the message class to the expected type (in this case Message Double.lvclass) and then calling the appropriate method to act on the data.
Picture
Notice that the command pattern is not in use, the message does not contain any information about an action to be performed when the data is delivered, it simply delivers the data.  In the example above the "Reactor Temperature", "Precursor Temperature", "Stop Valve Temperature", "Cone Temperature", and "Plasma Temperature" cases in the case structure all call methods that use the Message Double.lvclass data type meaning that there are 5 actor methods using 1 message class.   Compare this to a command pattern implementation that would necessitate the creation of 5 message classes, one for each actor method.
Picture
Notice that the to more specific type cast happens on the data message and not on the actor as it did within the Do.vi of the Actor Framework.  The Actor Framework is message centric and so actors need to be cast to ensure that they can handle the specific message, where as the mediated actor is data centric and so data messages need to be cast to ensure that they are of the type that the actor expects.  This is a subtle yet important difference.
Conclusion
Now that I have provided context for both message centric and data centric communication let's try to answer the original question, "What benefit does this implementation have over how the Actor Framework does it?".
​Message centric communication usually sends "verbs", while data centric communication usually updates "nouns".  In a verb-based system, actors interact directly with each other.  Actor A sends an Update Reactor Temp message to Actor B, where the payload of the message is the reactor temperature.  In a data centric noun-based system actors interact with the data, not directly with each other, which reduces coupling.  Actor A broadcasts a new reactor temperature to the temperature Topic and if actor B is subscribed to the Topic it will receive the new temperature.  Verb-based systems need many commands and actions, and this set grows with the number of actors. Noun-based systems support only a few actions on the data, and that set does not grow with the number of actors.
In practice I have found that most applications need a mixture of message centric and data centric communication and I have built my actor and mediator implementation to account for this.  Organizing messages as either command (message centric) or data (data centric) message types has made my code easier to write, easier to read, reduced coupling between my actors, and lowered the class count in my projects.  Each of these aspects increases the simplicity of my codebase and that is how I measure success.

Jon McBee is the Founder and Managing Partner at Composed Systems and is a Certified LabVIEW Architect, Certified LabVIEW Embedded Developer, Certified TestStand Developer, an NI Certified Professional Instructor, a LabVIEW Champion, and a Certified ScrumMaster
I refer you to the following links for more information on this topic:
  • "What's the Difference Between Message-Centric and Data-Centric Middleware"
  • "The Actor Model in 10 Minutes"
  • "Actor Programming Without an Actor Framework"
4 Comments
Uwe Frenz
12/13/2016 06:01:51 am

Good explanation!
The huge amount of message classes needed for a larger AFW application is what I think the biggest problem in changing to AFW.
AND - this is probably a one-way transition.

Reply
Steen Schmidt
1/30/2017 11:58:05 am

Great post Jon!

Your mediator is not quite the Mediator pattern though, is it? I mean, a Mediator is assumed to know the ruleset of what to do with state changes. The Mediator basically becomes a large switchboard of rules, it implements a big part of the collaboration complexity between objects (to decouple those objects from each other). So Mediator is hard to reuse (but decouples).

Observer pattern: Observers can at runtime subscribe to a Subject object, which they are then notified of state changes to. When an Observer is notified of a state change, the Observer pulls what data it needs "from the cloud" based on that state change notification. So the Subject just notifies of state change to itself, put the subsequent "pull" both couples the observers to their brethren and adds latency. Pull is inefficient.

Your "Message Center" smells like a good combination of both: Message Center hosts multiple Subjects, but each Subject is more full than in Observer, it's the complete data so no subsequent pull for more data is necessary. This means decoupled objects (each Object just knows about Message Center), but each object still embed it's own control logic, so no problem with reusability of a Mediator. We use a Message Center approach as well, for the reasons mentioned.

Cheers,
Steen

Reply
James Powell
3/16/2017 06:04:47 am

Steen Wrote:
"Observer pattern: Observers can at runtime subscribe to a Subject object, which they are then notified of state changes to. When an Observer is notified of a state change, the Observer pulls what data it needs "from the cloud" based on that state change notification. So the Subject just notifies of state change to itself, put the subsequent "pull" both couples the observers to their brethren and adds latency. Pull is inefficient.”

Just a pet peeve, but it always annoys me that a perfectly good “pattern” with a clear English meaning (“observe”) gets attached to implementation details that contradict that English meaning. If you have to “pull” changes, then you did NOT observe them.

Reply
data center link
10/25/2018 04:39:52 pm

thanks for the information, very usefull!!

Reply



Leave a Reply.

    RSS Feed

    Tags

    All
    Abstract Messaging
    Actor Framework
    Agile
    AI
    Asynchronous Programming
    Best Practices
    C#
    Complexity
    Continuations
    Control Values By Index
    Craftsmanship
    Dependency Inversion
    Dependency Viewer
    Futures/Promises
    Genetic Algorithm
    Liskov Substitution
    Malleable VIs
    Mediator Pattern
    .NET
    Object Oriented
    Object-Oriented
    Packed Project Library
    Parallelism
    Polymorphism
    Pub/Sub
    RawCap
    Root Loop
    Scrum
    Task Parallel Library
    TCP/IP
    TDD
    Test Engineering
    UML
    Unit Test
    VI Scripting
    VI Server
    WireShark

    Archives

    April 2019
    July 2018
    October 2017
    March 2017
    February 2017
    January 2017
    December 2016
    July 2016
    June 2016
    May 2016
    April 2016
    March 2016
    February 2016
    January 2016
    December 2015
    November 2015
    October 2015
    August 2015
    February 2015
    January 2015

    LabVIEW Blogs

    Eyes on VIs
    LabVIEW Hacker
    VI Shots
    LabVIEW Artisan
    Software Engineering for LabVIEW
    ​Wiresmith Techology
Picture
Powered by the Panda Ant

  • Blog
  • About
  • Contact
  • Tools
  • Resources