Introduction to the Flex Message Service

 

Introduction to the Flex Message Service

Sean Neville

Brightcove

Seth Hodgson

Adobe

Important note: Effective with the release of Adobe LiveCycle ES, the Adobe Flex Data Services 2 server product has been rebranded as a Solution Component of LiveCycle ES. This article was written based on Flex Data Services but will likely work as is with LiveCycle Data Services ES. Any articles referring to or using ColdFusion and Flex Data Services are not compatible with LiveCycle Data Services ES. To learn about the new capabilities of LiveCycle Data Services ES, see the tutorials in the LiveCycle Developer Center and read about Adobe LiveCycle Data Services ES.

At the heart of Adobe Flex Data Services 2 is a message bus: a hub that routes messages back and forth between services and clients. Understanding this message bus is understanding the core architecture of Flex Data Services.

One specific service is the Flex Message Service, which surfaces the basic messaging functionality of the message bus in the form of a publish-subscribe API for Flex clients. The Message Service adds durability and clustering features to the basic message functionality. The Message Service enables Flex and Flash clients to use publish-subscribe messaging to integrate with enterprise back ends that use message-oriented middleware (MOM), including Java Message Service (JMS) systems. The Message Service also defines the infrastructure behaviors for the Flex Data Service, which adds data-oriented features to the Flash platform as a subclass of the publish-subscribe Message Service.

This article illustrates the basic asynchronous publish-subscribe pattern and how it forms the architectural spine of Flex Data Services. An application usage example illustrates the architecture in action both with and without a JMS back end, and leads to discussions of the channel and endpoint constructs, which encapsulate protocol and network behaviors. This article also explains message destination clustering and touches briefly on how to extend the service with custom channels and custom service adapters.

Requirements

To make the most of this article, you need to install the following software:

Important note: Effective with the release of Adobe LiveCycle ES, the Adobe Flex Data Services 2 server product has been rebranded as a Solution Component of LiveCycle ES. This article was written based on Flex Data Services but will likely work as is with LiveCycle Data Services ES. Any articles referring to or using ColdFusion and Flex Data Services are not compatible with LiveCycle Data Services ES. To learn about the new capabilities of LiveCycle Data Services ES, see the tutorials in the LiveCycle Developer Center and read about Adobe LiveCycle Data Services ES.

Flex Builder 2 (SDK Included)
Flex Data Services 2
Prerequisite knowledge

Basic familiarity with Flex development. General familiarity with JMS a plus but not required.

The publish-subscribe message pattern

Asynchronous messaging has formed the basis of enterprise systems for decades, not only in the form of MOM used for enterprise integration, but also in the underpinnings of client-server technology; in application servers from Java to .NET; and even in RPC mechanisms from CORBA and synchronous SOAP, which may be viewed as special cases of messaging.

The core premise is simple: a producer creates a message and sends it to some logical destination, while a consumer attaches itself to the destination and listens for messages sent to it. Producers and consumers, commonly known as message agents, perform actions and otherwise collaborate by exchanging messages through a common hub, which is the destination.

The system must know the structural definition of a message, although that structure may be translated across a variety of protocols and represented in a variety of formats along the way. Similarly, the system must know the message semantics to some degree—wherever message contents cause business logic to execute across all the platforms a message may touch during its journey. The producer must be able to receive acknowledgment that its messages were reliably received; the destination must be able to correlate messages and potentially persist them in order to reliably and durably route them to all the interested consumers; and there are a variety of protocols, mechanisms, and design policies that add or detract from the quality of service provided by message publication and consumption. The core producer-destination-consumer relationship that supports message passing is the gist of the pattern.

Figure 1 shows an illustration of publish-subscribe messaging.

Publish-subscribe messaging

Figure 1. Publish-subscribe messaging

In simple RPC applications of messaging, there may be no consumers at all. Instead, a producer may publish a message and receive an acknowledgement which, in addition to informing the producer that the message was received, also contains information that can be interpreted as some sort of logical result of a synchronous remote invocation. In fact, this is the case with the three Flex RPC services: HttpService, WebService, and RemoteObject. The Flex Message Service, however, uses both producers and consumers. Since the Flex data service is a subclass of the Message Service, it also leverages producers and consumers.

Publish-subscribe messaging is not the only common message pattern in the enterprise: Point-to-point topologies are also prevalent. The difference between the publish-subscribe pattern and point-to-point messaging is that in the latter, messages are queued in the destination and delivered only to one consumer. This pattern is very useful in system-to-system use cases. You can tailor publish-subscribe messaging so that your application delivers a message to zero or only one consumer, based on the evaluation of message criteria that connected consumers may establish. But fundamentally, publish-subscribe messaging links many consumers to the same logical destination, while point-to-point topologies ensure that only one consumer receives any given message. The Flex Message Service focuses on publish-subscribe messaging to link rich clients to one another and to common enterprise back ends.

A simple Flex messaging application

Before delving into the design of the Flex messaging infrastructure, create a very simple application to get some messages flowing through the system. Figure 2 shows a very simple chat application that includes just enough functionality to create simple text messages and send them to all other instances of the same application running on any number of client hosts.

A simple chat application based on Flex

Figure 2. A simple chat application based on Flex

The application has a text box for message input and a text area to display messages as they arrive. A button on the panel allows the output to be cleared. Obviously, this application is bare bones, with no logins or the like, but it illustrates the publish-subscribe message flow in a client application.

Here is the code listing for the application:

	<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
pageTitle="Simple Flex Chat" creationComplete="chatSubscriber.subscribe()">

<!-- Messaging Declarations -->

<mx:Producer id="chatPublisher" destination="MyTransientTopic" />
<mx:Consumer id="chatSubscriber" destination="MyTransientTopic" message="receiveChatMessage(event)" />

<!-- UI Declarations -->

<mx:Panel title="Simple Flex Chat">
<mx:TextArea id="output" width="385" height="220" />
<mx:TextInput id="input" width="385" enter="sendChatMessage()" />
<mx:ControlBar horizontalAlign="center" width="385">
<mx:Button id="clearBtn" label="Clear" click="output.text =''" />
</mx:ControlBar>
</mx:Panel>

<!-- Event-Handling Script -->

<mx:Script>
<![CDATA[

import mx.messaging.events.MessageEvent;
import mx.messaging.messages.AsyncMessage;

private function sendChatMessage():void
{
var msg:AsyncMessage = new AsyncMessage();
msg.body = input.text;
chatPublisher.send(msg);
input.text = "";
}

private function receiveChatMessage(msgEvent:MessageEvent):void
{
var msg:AsyncMessage = AsyncMessage(msgEvent.message);
output.text += msg.body + "/n";
}

]]>
</mx:Script>

</mx:Application>

Note the use of the <Producer> and <Consumer> tag declarations. Both tags declare which destination they are associated with. The <Consumer> tag also declares which ActionScript method handles all message events dispatched when messages arrive for the consumer. The messaging script, invoked each time the user presses the Enter key with the input text box in focus, composes a new message instance. In this case, it is a basic AsyncMessage (other message types include the RPC messages and data messages). The producer publishes the new message using the producer.send(message) invocation. In the Flex <Application> tag, the creationComplete event handler subscribes the consumer to its destination after all Flex components have been created. Internally, the Flex client and server infrastructure will handle the encoding and transmission of the message, return acknowledge events, handle message correlation, handle the pushing (or potentially polling) of messages to the consumer, and so forth.

This is not a terribly useful chat (it can't even tell who is writing the messages, everyone is left deviously anonymous), but it enables the message flow between multiple clients and the server-side message destination, which is core to the Message Service. I revisit this application and add features using more of the Message Service API below.

Meanwhile, one piece still missing is a declaration related to the destination. In the MXML code above, the producer and consumer both reference the destination by name. But where is this destination created?

Since the destination exists remotely on the server, it is configured in a server-side XML file packaged with the Flex Data Services libraries in a J2EE web application. The configuration of services and destinations is the same across all of the Flex Data Services; that is, whether you are configuring a RPC RemoteObject, a DataService, a WebService or a MessageService component, the format is essentially the same. You declare a set of services in XML, and within each of those services, you configure destinations. You also declare things like security constraints and network channels that you want to make available, as well as any adapters you would like to plug into services, to link them to various back-end resources.

The message destination used by the producer and consumer above is named MyTransientTopic. You configure it as follows. The word transient is in the name because this particular destination is not durable. That is, it will not persist messages to ensure that they are later delivered to consumers that may be offline. If the service goes down, it will not reload its cache from storage. The configuration includes a channel declaration to control which network protocol (or protocols) this message destination will use to transmit messages.

<service id="message-service"
class="flex.messaging.services.MessageService"
messageTypes="flex.messaging.messages.AsyncMessage">

<!-- other destinations snipped -->

<destination id="MyTransientTopic">
<properties>
<server>
<durable>false</durable>
</server>
</properties>
<channels>
<channel ref="my-rtmp"/>
</channels>
</destination>

</service>

Many optional properties exist that control various network and server behaviors, that enable and control destination clustering, and that configure adapters to link Flex publish-subscribe messaging to back-end messaging systems such as JMS and others. The brief syntax above is only an introduction. As we add additional elements to the example in the following sections, refer to the product reference documentation for comprehensive descriptions of all available options.

The Flex messaging architecture

Step a level above the chat example and notice how its message usage and configuration apply to the Flex Data Services. Figure 3 provides an overview of the message bus architecture.

Message bus architecture

Figure 3. Message bus architecture

Whenever an operation is performed—the publishing of a message to a message destination, the invocation of a remote method, or the modification of data in a managed data collection—the Flex client framework composes a message. The framework employs a producer to send the message to the necessary destination. In the case of the Message Service, the application developer creates and controls the producer directly, while in the case of the RPC and data services it is the framework's duty to create producers and compose and send messages internally.

The client knows which channels the destination has been configured to employ, and it selects the first available channel for message transmission. The channel takes care of formatting and translating the message into a network-specific form and delivering it to a remote server endpoint. The destination and channel information is available to the client as a result of compile-time access to the XML configuration, which the server uses to manage services at runtime and the compiler uses to embed a portion of the configuration into the client.

The server-side channel endpoint unmarshals the message in a protocol-specific manner and then passes the message in generic Java form to the message broker. The broker performs authorization checks and routes the message to the appropriate service, based on the message type. The first service found that can process the message receives it, locates the destination the message targets, and potentially employs a service adapter if the destination requires one for back-end access. The service and its adapters may perform logic based on the incoming message, such as invoking operations in the case of an RPC service, or managing subscriptions in the case of an adapter-less Flex Message Service destination.

Finally, a service or service adapter may request that the message be sent to other connected clients, causing the message to flow back into the broker and out of the appropriate endpoints to other connected clients. This is a true push operation if the channel is capable of real-time push, or it may be a cache-for-poll operation if the channel relies upon HTTP polling or other non-real-time protocol. It could be a mix of the approaches, with some consumers polling and some accessing the data in real time, depending on how the destination's channels are configured. The service and destinations are decoupled from the actual network operation and protocol-specific message formats, as is the basic ActionScript developer code.

The Flex infrastructure sends special Command Messages along the same path. These sorts of messages represent infrastructure operations, such as the act of subscribing, rather than the publishing of application message data.

Various behavioral aspects and controls can be attached to the channels, services, and adapters. The system itself can be extended to add new channel types, new services, and new adapters in a modular fashion. The following sections explore these advanced topics by expanding upon the simple chat Flex application.

Exploring the Flex Message Service and ActionScript API

In this section, you'll improve the chat example and delve a little deeper into the Message Service API. A good place to start is learning when the server receives a message and learning when an error occurs during message processing.

Any time a message is sent, it must be acknowledged for the client to know that it was properly received. The remote service sends an AcknowledgeMessage to the client to satisfy this requirement. The client framework also fires an event for application code when these acknowledgments occur.

You can add the acknowledge event as an attribute to the MXML tags used above, but remember that everything that you can do in MXML tags you can also do in ActionScript. The reverse is not true: you cannot do everything in MXML tags that you can do in ActionScript. Instead of using tags to declare the producer and consumer, you can move the declarations to ActionScript. This is a good place to add the acknowledge message event handlers as well. Since this is beyond the glue-code script and into more verbose code, it's also a good idea to maintain the ActionScript in a separate file instead of maintaining it directly in the MXML. The following ActionScript example, simplechat.as, instatiates the producer and consumer and listens for acknowledge events.

import mx.messaging.Consumer;
import mx.messaging.Producer;
import mx.messaging.events.MessageAckEvent;
import mx.messaging.events.MessageFaultEvent;
import mx.messaging.events.MessageEvent;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AsyncMessage;
import mx.utils.ObjectUtil;

private var chatPublisher:Producer;
private var chatSubscriber:Consumer;

private function createChat():void
{
chatPublisher = new Producer();
chatPublisher.addEventListener(MessageFaultEvent.FAULT, onProducerFault);
chatPublisher.destination = "MyTransientTopic";

chatSubscriber = new Consumer();
chatSubscriber.addEventListener(MessageAckEvent.ACKNOWLEDGE, onConsumerAck);
chatSubscriber.addEventListener(MessageEvent.MESSAGE, receiveChatMessage);
chatSubscriber.destination = "MyTransientTopic";
chatSubscriber.subscribe();
}

private function onProducerFault(faultEvent:MessageFaultEvent):void
{
output.text += "[Error: " + faultEvent.message.toString() + "]/n";
}

private function onConsumerAck(ackEvent:MessageAckEvent):void
{
output.text += "[Got ack for subscribe or unsubscribe operation]/n";
}

// Note: these are the event-handling methods from above, now moved into the separate source file:

private function sendChatMessage():void
{
var msg:AsyncMessage = new AsyncMessage();
msg.body = input.text;
chatPublisher.send(msg);
input.text = "";
}

private function receiveChatMessage(msgEvent:MessageEvent):void
{
var msg:AsyncMessage = AsyncMessage(msgEvent.message);
output.text += msg.body + "/n";
}

Pulling this code out into a separate file requires that a few changes be made to the MXML file: Delete the producer and consumer tags, replace the current script tag with <mx:Script source="simplechat.as" />, and finally, update the creationComplete attribute in the Application tag to invoke the createChat() function. This adds a separate file containing additional ActionScript, but it reduces the size of the original MXML file and removes all programmatic logic from it, providing a cleaner separation of concerns.

At the heart of the API and architecture is the message itself. It would be handy while learning and debugging to add a console that does nothing other than print the raw contents of messages as they are sent and received. This will give you a chance to inspect the message structure, which consists of the message properties, headers, and body.

Note that the receiveChatMessage function receives a MessageEvent with a message property: this is the raw message pushed from the server to the client. The message can be viewed in its entirety by adding a new TextArea (id="rawout") to the MXML file and updating the receiveChatMessage handler function with the following line to print the message contents: rawout.text += "Message: " + ObjectUtil.toString(msgEvent.message) + "/n";

Figure 4 shows a screen shot of the message dump.

The updated interface displaying the raw message dump

Figure 4. The updated interface displaying the raw message dump

You could add similar code to dump the contents of the original AsyncMessage as composed and sent by the producer.

AcknowledgeMessage uses correlation identifiers that point to the original message id to maintain the reference, despite the asynchronous nature of the message transmissions and despite the lack of conversational state. The messages also contain timestamps, time-to-live indicators, a map of headers, a body containing arbitrary object graphs (in this case the body is a simple string of text), and so forth.

Headers are of particular interest to application code. Developers may add headers to messages and have both other clients and the server-side Message Service alter logical behavior based upon header values. The headers property of the message is an arbitrary ActionScript Object, meaning it contains name-value pairs. To illustrate header usage, you can expand the example to add a header indicating the name of the current user. After attaching this name as a header to the messages that the user sends, you can update consumer code to interpret it and to know who composed a given message, who is available to chat, who recently abandoned the chat, and so forth.

Since adding a user interface to display a list of users present is tangential to the messaging code, I'll skip it for now. The more relevant piece of the message API is header usage, illustrated in the following updated event-handling code:

private function sendChatMessage():void
{
var msg:AsyncMessage = new AsyncMessage();
msg.headers["chatuser"] = username;
msg.body = input.text;
chatPublisher.send(msg);
input.text = "";
}

private function receiveChatMessage(msgEvent:MessageEvent):void
{
var msg:AsyncMessage = AsyncMessage(msgEvent.message);
var msgSender:String = msg.headers["chatuser"].toString();
output.text += "[" + msgSender + "]: " + msg.body + "/n";
}

Now, this won't compile without additional code to set the username variable to something meaningful. The syntax describes how headers may be added and interpreted, however. A full example is left as an exercise for independent exploration and might include a login facility, a list of participants as they join, and leave the room that could be maintained through a set of messages published with additional headers, and so forth.

In addition to consumer client code using headers, the server itself can also use these application-supplied headers. The dominant example is selector evaluation. A consumer may supply an optional selector expression to filter its subscription and expect the server to perform the filtering on its behalf. This selector expression is like the WHERE portion of a SQL-92 query (it mirrors JMS selector expressions):

chatSubscriber.selector = "username = 'Smith'";
chatSubscriber.subscribe();

This informs the Message Service that the client would like to receive messages published to the destination, but only if the messages contain a header named username with a value of Smith, which satisfies the selector criteria. The server evaluates the expression for each consumer as needed using a SQL parser. If the expression is not satisfied, the server does not send the message to the consumer.

In order to reach a consumer with the above selector, the producer would need to add the appropriate header to the message prior to publishing it:

msg.headers["username"] = "Smith";

In this example, you can use selector expressions to conduct private conversations and to create dynamic destinations or new chat rooms branched off the main pre-configured destination. More sophisticated enterprise examples may employ selectors for filtering market data and the like. For a complementary approach to managing the routing of messages to specific consumers or groups of consumers that avoids the overhead of evaluating all consumer selectors when your routing criteria is simple, refer to destination subtopics using the ActionScriptAdapter with the MessageService in the product documentation.

A few remarks on durability are warranted before ending the introductory exploration of Message Service usage. Without employing a service adapter, the Message Service can provide some degree of message durability. Based on configuration, the service will persist messages in a file-based store and if the service or destination fails, it will still be able to broadcast the messages to consumers that may not have received them. The persistence mechanism is pluggable, so could be extended to employ relational databases and the like, but the only implementation provided initially is the File Store Manager. For applications with broadly distributed destinations requiring durable consumers and high volumes of messages, it is advisable to use an adapter to link the Flex Message Service to a JMS implementation and delegate durability to JMS instead of relying upon the embedded file store. Flex doesn't aim to replace middleware; it aims to link middleware to rich clients in the best way possible. Middleware is a valuable part of the chain, particularly for concerns such as enterprise-level back-end message durability.

Message security

You can protect destinations with security constraints, which force authentication and authorization checks prior to allowing a client to send and receive messages to the destination. Flex Data Services is J2EE-based, and therefore employs standard J2EE role-based security. It leverages the J2EE server for authorization and then checks the authenticated principal's roles against the roles declared in a destination's security constraint.

To secure a message destination in this manner, you would generally update the destination's server-side XML configuration to reference one of the service-wide Flex security constraints:

<!-- In the security section of the flex-enterprise-services.xml main configuration file: -->
<security-constraint id="registered-users">
<auth-method>Custom</auth-method>
<roles>
<role>users</role>
</roles>
</security-constraint>

<!-- In the MessageService destination configuration file: -->
<destination id="MyTransientTopic">
<!-- other destination properties snipped -->
<security>
<security-constraint ref="registered-users"/>
</security>
<destination>

This indicates to the server that only members of the J2EE users role are permitted to send and receive messages to the message destination named MyTransientTopic and that all others will receive error events. The definition of the users role is managed in the J2EE server using LDAP, a relational data store, or whichever other mechanism may be integrated into the server. Flex is decoupled from the details.

In addition to authentication and authorization of users, message integrity itself must often be maintained. Do not transmit sensitive messages across insecure channels. When making destinations durable, beware: the built-in file store mechanism, for example, does not provide message encryption.

The JMS service adapter

Service adapters exist to bridge Flex destinations to back-end systems. In the case of an RPC service like RemotingService, an adapter exists for invoking methods on Java objects. In the case of DataService, an adapter exists for linking data message operations to Hibernate, for example. In the case of the Message Service, Flex provides an adapter to a JMS provider.

This JMS adapter enables the Message Service to delegate subscription management, durability, and the like to an external JMS implementation such as those found in J2EE servers.

This adapter also enables Flex clients to send and receive messages to other non-Flash producers and consumers attached to the same JMS destinations. These other clients could be a Java client, but they are more likely to be Java server-side processes external to the Flex Data Services runtime. Finally, this adapter provides one of the mechanisms available for bridging external processes—from database triggers to RPC services to Java actions embedded in classic J2EE web applications—into Flex clients.

To publish and subscribe to a JMS destination, you don't need to change any Flex client code. The MXML and ActionScript remain the same. The server-side configuration is all that changes. You must update the server-side configuration to include the new adapter declarations, which in turn contain properties describing how the JMS adapter should interact with the external JMS destination.

The following example reconfigures the MyTransientTopic destination to use the JMS adapter:

<destination id="MyTransientTopic">

<adapter ref="jms"/>

<properties>

<!-- For specifics on JMS, please reference the
Java Message Service specification or your
J2EE server documentation -->
<jms>
<!-- whether the adapter is performing topic (pub/sub) or queue (point-to-point) messaging -->
<!-- optional element, defaults to Topic -->
<destination-type>Topic</destination-type>

<!-- the javax.jms.Message type which the adapter should use for this destination -->
<!-- supported types: javax.jms.TextMessage, javax.jms.ObjectMessage -->
<message-type>javax.jms.TextMessage</message-type>

<!-- name of the JMS connection factory in JNDI -->
<connection-factory>jms/flex/TopicConnectionFactory</connection-factory>

<!-- name of the destination in JNDI -->
<destination-jndi-name>jms/topic/flex/simpletopic</destination-jndi-name>

<!-- name of the destination in JMS -->
<!-- optional element, defaults to the destination id -->
<destination-name>FlexTopic</destination-name>

<!-- the JMS DeliveryMode for producers -->
<delivery-mode>NON_PERSISTENT</delivery-mode>

<!-- JMS priority for messages sent by Flash producers -->
<message-priority>DEFAULT_PRIORITY</message-priority>

<!-- the message acknowledgement mode for the JMS adapter -->
<!-- none of these modes do not require any action on the part of the Flex messaging client -->
<!-- AUTO_ACKNOWLEDGE - the JMS provider client runtime automatically acknowledges the messages -->
<!-- DUPS_OK_ACKNOWLEDGE - auto-acknowledgement of the messages is not required -->
<!-- CLIENT_ACKNOWLEDGE - the JMS adapter should acknowledge that the message was received -->
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>

<!-- the JMS session transaction mode -->
<transacted-sessions>false</transacted-sessions>
</jms>
</properties>

<channels>
<channel ref="my-rtmp"/>
<channel ref="my-polling-amf"/>
</channels>

<security>
<security-constraint ref="registered-users"/>
</security>

</destination>

Service adapters are designed to be extensible, such that independent software vendors may craft their own adapter implementations for back-end resources that Flex Data Services do not support out of the box. This extensibility enables the Message Service to bridge to non-JMS messaging middleware. This is an advanced topic, however, and is beyond the scope of this introduction. For more information, please see the product reference documentation.

Message channels

To send messages across the network, the client and server employ channels that encapsulate message formats, network protocols, and network behaviors in a way that decouples these three concerns from services, destinations, and application developer code.

Although the channel mechanism is extensible—meaning the message bus is designed to allow new channels to be created and inserted to work with existing services—several channel types are included in the Flex Data Services distribution. These are the Real Time Media Protocol (RTMP) channels, the Action Message Format (AMF) channels, and the HTTP channels.

RTMP is a real-time protocol built on top of TCP/IP for the purposes of media streaming. It contains interleaved audio, video, and data content. Flex uses only the data portion of RTMP, and performs no audio or video streaming; the Flash Media Server employs RTMP for audio and video streaming. RTMP is the only real-time push channel provided by Flex Data Services.

The AMF channels enable binary representations of ActionScript object graphs, similar to the data portion of RTMP, but the AMF binary content is delivered in the body of an HTTP (or SSL-wrapped HTTPS) POST message. Being binary, this channel provides significant performance and bandwidth advantages over XML and text; but being HTTP, it does not enable real-time push. One of the AMF channels allows for polling, and the XML-based server-side channel configuration allows developers and administrators to control the polling intervals employed by the AMF polling channel without addressing it in application code.

The HTTP channel functions similarly to the AMF channel as far as the network is concerned. It delivers messages in the bodies of HTTP (or HTTPS) POST messages. It differs from AMF, however, in that the messages are formatted XML instead of in binary form, allowing network monitors and firewalls to inspect the message content if necessary. This channel also provides a REST network strategy for message delivery.

Any channel can be used in conjunction with any service destination, although some combinations are not logical. For example, an RTMP channel may be used to reach a RemoteObject destination or any other destination in any other service. Similarly, an AMF polling channel may be employed by a DataService consumer. But if a non-polling channel is configured for a publish-subscribe Message Service destination then only the destination's producers use that channel, and the framework will look for a different channel configured in the destination when it comes to consumers. It is not logical for a consumer to use a non-polling non-real-time channel, since it would never be able to receive its messages without a server push or a periodic client poll.

Channels attain a priority associated with the order of their configuration declarations. If a destination has three channels configured, then the client attempts to use the first channel. If it is unable to use the first channel, it will fall back on the second listed channel. This tactic is useful when, for example, a destination prefers to employ RTMP but the client temporarily finds itself on a subnet environment that has a firewall against binary network I/O. The client in this example could fall back on HTTP polling.

Channels are designed to be extensible, such that independent software vendors may craft their own channel implementations and integrate them into Flex Data Services. This is an advanced topic, however, and involves both client-side ActionScript code and advanced Java code to create the channel endpoint, and is beyond the scope of this introduction.

Clustering and the Flex Message Service

You can cluster destinations across multiple instances of Flex Data Services. For example, a destination with a specific id in the Message Service running on one server federates with a destination under the same id and service on another server. This form of software clustering is intended to run on top of, and complimentary to hardware load balancing, as Flex destination clustering is intended primarily for failover and high availability rather than load balancing or parallel processing.

Essentially the way destination clustering works is that after any one cluster node (a single instance of the Flex Data message broker) receives a message for a destination and processes it, it begins to broadcast (push or cache-for-poll) the message to all consumers attached to the destination on that node. As part of this process, it also informs all peer nodes to broadcast the message to all of its consumers attached to the destination. The peer nodes generally do not re-process the message, they merely receive a copy of it from the original node and then broadcast it to all of its clients. For stateful node commands, such as those for clustered Data Services that do not rely on a common coordinator back-end behind the nodes, the original node can inform the peers to re-process the message altogether instead of merely broadcasting it, but this option is uncommon for the Message Service.

When a client subscribes to a clustered destination, the server will respond with the subscription acknowledgment as usual, but the handshaking process will also deliver cluster node URLs to the client. The client can use these URLs to fall back on in the event that the original host fails. For producers and consumers of the Message Service, this means that when a client has subscribed and its primary node becomes unreachable, it will contact one of the peer nodes in the list of URLs it received, and resubscribe on the new node if necessary. When the client resubscribes, it will get a new list of URLs denoting nodes that belong to the cluster.

Note that Flex requires the JGroups library for cluster discovery, so that the various message brokers across cluster nodes can locate one another and respond to node failures and new node joins.

Also note that the client receives the list of peer node URLs at runtime only after first successfully reaching the primary host, which is typically the host that delivered the SWF file in the first place. This means that if the server fails in the small window between delivering the SWF file but before the SWF file fully loads and its agents connect to the services, then the client will not be capable of failing over, as it will not yet have its list of peer node URLs.

Where to go from here

The Flex Message Service is designed to enable reliable publish-subscribe messaging under a simple development model and to bridge that model to message-oriented middleware, particularly in the Java Message Service. The Message Service also illustrates the core of Flex Data Services, with messaging and the message broker hub acting as the heart of all of the services in the system.

Find more details on the Message Service and how to use it in the Flex Data Services reference documentation.

About the authors

Sean Neville used to be a principal scientist at Adobe and one of the creators of Flex at Macromedia. He also served as architect of Flash Remoting and JRun, and represented Macromedia on the Java Community Process Executive Committee. He now extends the Flash platform through Brightcove, a company at the forefront of broadband television.

Seth Hodgson is a computer scientist on the Flex Data Services development team. Prior to this dream job, he worked on the web frameworks and applications that ran macromedia.com, along with a healthy dose of enterprise application integration.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值