Improving the Performance of Your Rich Internet Applications with Macromedia Flash Communication Server MX
Let's say you have successfully created a really cool Rich Internet Application (RIA) using Macromedia Flash, Flash Remoting, Coldfusion MX (or another application server), and a database management system (such as MySQL or any other DBMS).
Your site's visitors are probably very happy about the better user experience provided by your amazing Macromedia Flash UI. They're also probably experiencing shorter load times. Your app only sends data across the wire whenever it needs to update data; it's not reloading the whole UI every time.
Okay, now brace yourself: What if I told you that you could reduce those loading times by up to 100%? Yes, you read correctly. There is a way to cut loading times down to virtually 0 seconds. Impossible you say? Read along then...
Introducing Macromedia Flash Communication Server MX
Macromedia Flash Communication Server MX is a communication server that lets you create multiuser applications like you've never been able to do before. You can combine audio, video and data to create powerful Rich Internet Applications-and the end-user only needs Macromedia Flash Player to use them!
Read more on the product page for additional information on what Flash Communication Server can do.
One very powerful feature of Flash Communication Server MX is the SharedObject class. This class offers real-time data sharing between multiple client movies and objects that are persistent on the local or remote location. For more info about SharedObjects, see the documentation that comes with Flash Communication Server.
Now for the exciting part: A little known feature of Shared Objects (and Macromedia Flash Player) is that you can create a local copy of the content of a shared object that lives on the server. This tutorial uses this feature to show you how achieve an incredible goal: reducing data-loading times to 0 seconds.
Requirements
To fully understand this article, you must be comfortable with Macromedia Flash MX (and ActionScript), Macromedia Flash Remoting, ColdFusion MX (CFCs, in particular), and general database concepts. You must also have a minimal knowledge of SQL.
-
Download the sample code (TXT)
(Note to Windows Internet Explorer users: Copy and paste sample code snippets from the sample code text file. Other users can copy the code directly from the tutorial web pages.)
The myLinks Rich Internet Application (RIA) Example
In this article you will examine a very simple RIA called myLinks. The RIA, myLinks, primarily presents users with a list of your favorite Internet links.
You will keep your list of links in a database. You can update this list with SQL in a ColdFusion page (or a dynamic page of your choice), or from another RIA. In this tutorial, you'll use the ColdFusion with Macromedia Flash combination as follows:
- Use Macromedia Flash MX to build the user interface-a simple listbox.
- Use Flash Remoting and ColdFusion MX components (CFCs) to query a mySQL database.
This architecture may sound familiar to you if you've built an RIA previously. (This example is probably much simpler than what you have already created.)
The myList Application's Standard RIA Functions
Using the architecture I described above, imagine what a "transaction" looks like in the myList application. Imagine a three-day scenario with a fictional user using myList.
Figure 1: Traditional RIA Architecture
Day 1: The user logs onto your website for the first time. Here's what happens:
-
The user interface SWF makes a Flash Remoting call to the ColdFusion component (CFC) which is /cfdocs/myapp/getLinks.cfc, using code similar to the following:
#include "NetServices.as" NetServices.setDefaultGatewayUrl("http://mydomain.com:8500/flashservices/gateway"); gatewayConn = NetServices.createGatewayConnection(); getlinks_cfc = gatewayConn.getService("cfdocs.myapp.getLinks", this); //links_lb is the listbox, the only asset on the stage links_lb.addItem("Loading..."); getlinks_cfc.getAllLinks(); -
The getLinks.cfc executes a query to the database. Here's the CFC code that executes the query:
<cfcomponent> <cffunction name="getAllLinks" access="remote" returnType="query"> <cfquery name="Links" dataSource="my_db"> select * from links </cfquery> <cfreturn Links> < /cffunction> < /cfcomponent> - The database returns the entire list of links (100 items) to your CFC.
-
The CFC returns the whole recordset to your Macromedia Flash application, which uses the following code to populate the listbox with the results:
function getAllLinks_Result(val) { links_lb.setDataProvider(val); } function getAllLinks_Status(error) { trace("getAllLinks_Status:"+error); }
In short, your user receives 100 links from the database. (She is happy with the experience and goes about her business!)
Day 2: An hour after the user left your site on Day 1, you found two great websites and added them to your database through your custom password-protected website administrator application. Now, it's the next day, and the same user returns to it to check out your links collection.
Steps 1 to 4 occur again, but this time, the user receives a list of 102 links.
Day 3: The next day, the user returns to your site. When she logs on, she receives the same 102 links again (assuming you haven't made any additions or changes to your list).
Let's look at the values again:
day 1: 100 links
day 2: 102 links
day 3: 102 links
There is nothing wrong with this process and it's still a lot better than plain HTML, right?
Improving Your RIA With Flash Communication Server MX
The scenario I just described was the "before"-before you decided to use Macromedia Flash Communication Server into the picture. To reach our goal, you will learn how to change the architecture to look like this:
Figure 2: New RIA Architecture with Flash Communication Server MX
It appears that we're making things more complex by adding extra two steps for each transaction, right? You're right, but it's worth it. Let's see what happens.
Install Flash Communication Server MX. If you don't own Flash Communication Server MX, purchase it now or download the trial version.
For the fastest possible results, you can opt for an all-in-one deployment scenario by installing all three server-side components-Flash Communication Server MX, ColdFusion MX and mySQL-on the same machine. Although this is fairly demanding on your hardware, this solution works well because it makes it easy to develop, or works well for a site that doesn't have much traffic. (This is definitely a good development scenario; I have all of the above installed on my laptop.)
A more likely production environment is an installation that has Flash Communication Server on one dedicated machine, ColdFusion on another, and the database management system on a third. All of these machines should be part of the same intranet and have very fast connections between them.
Move Flash Remoting to the server side. When installed in a standard environment as described above, Flash Communication Server and the database can communicate with each other very quickly (through ColdFusion), so "polling" the database once a minute does not tax the server software too much.
And that's precisely what you're going to do. You will build a server-side ActionScript for Communication (ASC) script that makes a Flash Remoting call once a minute, saving the results in a Shared Object. Here are steps for accomplishing this task:
- Create a directory called "myApp" in your /flashcom/applications/ folder.
- Create a text file called main.asc in the newly created /flashcom/applications/myApp/ folder.
-
Insert the following code into the text file:
load( "NetServices.asc" ); //This will get called when the application first starts application.onAppStart = function(){ trace("onAppStart called"); NetServices.setDefaultGatewayUrl("http://localhost:8500/flashservices/gateway"); gatewayConn = NetServices.createGatewayConnection(); getlinks_cfc = gatewayConn.getService("cfdocs.myapp.getlinks", this); this.so = SharedObject.get("links",true); this.interval = setInterval(getData,1000); } //Prevent Garbage Collection from unloading this application application.onAppStop = function() { trace("onAppStop Called, returning false"); return false; } function getData() { getlinks_cfc.getAllLinks(); } application.getAllLinks_Result = function(val) { clearInterval(this.interval); this.interval = setInterval(getData,60000); //get results every minute trace("Result:"+val.getLength()); this.so.lock(); //Loop through the results for (var i=0; i<val.getLength(); i++) { //Remove definitions introduced by NetServices.asc val.items[i].unshift = null; val.items[i].registerClass = null; val.items[i].__ID__ = null; //Get local copy of the current link var oldObj = application.so.getProperty(i); var dirty = false; if (oldObj == null) dirty = true; // The slot is new else { for (var j in oldObj) { // loop through the fields of the saved object if (oldObj[j] != val.items[i][j]) { //A field is different than its saved copy dirty = true; break; } } } if (dirty) { trace("Slot "+i+" is dirty"); this.so.setProperty(i, val.items[i]); //Saving in the shared object } } this.so.unlock(); }
Code description for main.asc: Here's an analysis of the code above:
- The code loads NetServices.asc. This is just like doing #include "NetServices.as" on the client side.
- The code defines what needs to happen when the application first starts: It sets up the Remoting Connection (just like you would do in Flash MX) and then defines a variable (named so), which is a SharedObject that will contain a copy of the database table. Finally, it sets an interval to call the getData function in one second.
- Since you want this application to keep running forever, the code tells Flash Communication Server MX not to unload it when it runs its scheduled Garbage Collection code. The code does this by defining an application.onAppStop function that returns false.
- The getData function is really simple, it invokes the CFC. (It's the same CFC described before, there's no need to touch it at all.)
- The code defines the callback handler, getAllLinks_Result. Notice that it's attached to the application object and not to the root like it was in the previous scenario. This method is a little more complex so it's worth going through it.
- The code clears the interval, only to set it again, this time to execute getData every 60000 milliseconds (=1 minute).
- Next, the code obtains a lock on the SharedObject, which we release at the end. (I'll explain why we do this later.)
-
Then the code loops through the result recordset (the list of links), and for each slot:
- It cleans up some undesirable properties that loading NetServices.asc introduced (the joys of prototype-based languages...).
- It gets a local copy of the current slot and saves it in a temporary variable called oldObj.
- It decides whether the newly arrived slot is either new or if any of its fields have changed since the last time it ran getData.
- If needed, it saves the slot in a local copy (the so SharedObject).
Running the Flash Communication Server-Side Script
Now it's a good time to test your new code.
Open the Communication App Inspector and connect to your Flash Communication Server. Type "myApp/_definst_" in the "App/Inst:" text field under the list of active app instances, and click "Load". The application instance will appear in the list. Select it and click "View Detail." Now click "Reload App," you should see something like this:
App Reloaded onAppStop Called, returning false Unloaded application instance myApp/_definst_ ***** running netserviceswrapper Loading of app instance: myApp/_definst_ successful ***** running NetServices ***** running recordset ***** running RsDataProviderClass onAppStart called
Followed by "Result:100" once every minute.
If you see this, excellent-you have the Flash Communication Server application in sync with the database. From now on, the following happens every minute:
Figure 3: Flash Communication Server, ColdFusion Server, and Database Communication
- The main.asc calls the CFC.
- The CFC makes an SQL query to the database.
- The database returns the whole list to the CFC.
- The CFC returns the while list to the main.asc, which saves the changes in the so Shared Object.
This is a lot of traffic, but it's all behind the scenes, in the comfort, speed and security of your own intranet. Now, it's time to code the client side.
Changing the Client
Here comes the final and most exciting part, which explains some of the coding choices made so far.
Let's look at the code first:
nc = new NetConnection();
nc.onStatus = function(info) {
trace(info.code);
so.connect(this);
}
nc.connect("rtmp://myFlashComServerDomain.com/myApp");
so = SharedObject.getRemote("links",nc.uri,"/");
so.onSync = function(list) {
trace("onSync:");
for (i in list)
trace(i+":"+list[i].name+","+list[i].code);
updateList();
}
function updateList() {
links_lb.removeAll();
for (var i in so.data) {
links_lb.addItem(so.data[i].link);
}
}
Code Description for the client-side code: Here's an analysis of the code above:
- The code creates a NetConnection to the Flash Communication Server (nc).
-
You define the local copy of the remote SharedObject, with this line:
so = SharedObject.getRemote("links",nc.uri,"/");. Since this is the line that does all the "magic," here's a word-by-word analysis of the code:-
The variable
sois a SharedObject. -
getRemotetells Macromedia Flash that the SharedObject comes from the server. -
linksis the name of the SharedObject. (Notice that it's exactly the same as defined in main.asc.) -
nc.uriindicates that communications about this SharedObject occur over the previously defined NetConnection, and that it saved the remote copy of the SharedObject on the Flash Communication Server. - The last parameter ("/") tells Macromedia Flash Player to create a local copy of the contents of this Shared Object in the root of the ".../Application Data/username/websitename" folder. This is just like a path that you specify when saving a local Shared Object. (For more info about SharedObject.getLocal, look at the Macromedia Flash MX and Flash Communication Server documentation.)
-
The variable
- The code defines an onSync handler. This is a substitute for the client-side Remoting calls we did in the "before" scenario. Your app magically calls it whenever anybody (the server, or even another client) changes the contents of the SharedObject. The onSync handler traces a list of the changes and calls updateList() to repopulate the listbox.
- The updateList function is pretty straightforward, all it does is clear the listbox and repopulate it with the data saved in the so SharedObject.
That's it! And there is even less client-side code than in the "before" scenario.
Let's see how the magic happens, and how it does, in fact, reduce our loading times dramatically. To do so, let's run through the "transaction" once again.
Figure 4: Connection on Day 1
Day 1: We already know how the Flash Communication Server Application stays in sync with the database by calling the CFC once a minute. Here's what happens when a client loads your SWF:
- The client connects to Flash Communication Server and connects the Shared Object.
- The client automatically compares the content of the Shared Object on the server with the local copy (saved in the local shared object). At the time of the first visit, the local copy is empty, so the client gets an onSync with 100 changes in it (one per saved link).
- The client then populates the listbox and saves the 100 links locally. (So, no improvement here-we got 100 slots with our previous solution; we get 100 slots now.)
- Now your user disconnects, and you add two links to the database.
- The next time the Flash Communication Server receives the full query results from the database (within a minute), the code in main.asc will recognize that two slots are new and will save them in the SharedObject.
Day 2: Now it's the next day and your user comes back to your site. This is what happens:
Figure 5: Connection on Day 2
- The client connects to Flash Communication Server and connects the Shared Object
- The client automatically compares the content of the Shared Object on the server with the local copy (saved in the local shared object). Now the client already has 100 links saved, so the onSync will only transport the two new links to the client! The client saves the two new links in the local copy of the SharedObject and repopulates the list.
If we had used the "before" scenario (without Flash Communication Server), we would have used 102 slots, now we are only using 2! Now that's an improvement!
Day 3: Now let's come to day three of our scenario: The user returns to your site, and you haven't updated the database at all.
The client connects, compares local copy with remote copy, and there are no differences found in the client and server data. The client downloads zero slots over the wire, and the list simply populates with the data saved in the local copy of the SharedObject. We did it! Amazing!
Before and After: Comparing the Results
The following table compares the number of data updates required by our "before" and "after" scenarios:
| 1st Solution (Pure Flash Remoting) | 2nd Solution (Flash Remoting running with Flash Communication Server MX) | |
|---|---|---|
| Day 1 | 100 updates | 100 updates |
| Day 2 | 102 updates | 2 updates |
| Day 3 | 102 updates | 0 updates |
Pretty impressive! Take a minute to bask in the glory of your new and improved application.
Note: You simply compare Shared Object content by keeping a version number both on the client and the server. Think of it as a revision number. When the client connects, it sends its saved version to the server (say, version 5). If the server's copy of the Shared Object is 7, the server will then send each client the changes between version 5 and 7.
Also, I owe you one last bit of code explanation: In the main.asc, why did you use .lock and .unlock on the Shared Object? Well, obtaining a lock is a little bit like telling the server "wait until I'm done before you send updates through onSync." So in our example, even though there are two changes to the links table, we only send one onSync message, containing both changes.
Extra Super Bonus Feature
Now let's imagine that you update the database while a client actively browses your page. Here's what would happen:
Figure 6: Automatic Synching of Client/Server
- You update the database by adding a new link
-
Within one minute, Flash Communication Server MX queries the database and gets the new link. The server notices that the link is new and runs this code to save it in its local copy:
this.so.setProperty(i, val.items[i]); //Saving in the shared object
Here's the extra-super bonus feature:
Like magic, setting a property in a slot of the SharedObject immediately triggers an onSync on each client connected to the Flash Communication Server. All of the users browsing your page at the time of the update will see the list of links updated, without selecting or clicking Refresh-now that's cool!
Conclusion
In this tutorial, you have seen how to optimize your Flash Remoting-based Rich Internet Application by using a little-known feature in Flash Communication Server MX and Macromedia Flash Player.
Macromedia Flash Communication Server offers much more functionality than what I have shown here. Some examples are multiway audio/video streaming in real-time, recording and playback of custom ActionScript events, and a lot more. Check out the Macromedia Flash Communication Server product page for details. I am sure you will get many ideas on how to enrich your RIAs even more than before.