Guttershark Actionscript 3 Library
I’ve been working quite a bit recently to get this Flash Actionscript 3 library ready. Check out the first beta.
I’ve been working quite a bit recently to get this Flash Actionscript 3 library ready. Check out the first beta.
A friend sent me this link a couple weeks back to a short spotlight on Flexible Rails. Check it out
I’ve been working a lot lately with Akamai for streaming content. Minus many problems I’ve encountered, and having to write an AS3 AkamaiNCManager class. It’s pretty straight forward.
For a quick background on the what I’m going to show you - checkout the FLVPlayback documentation and look at the ncMgr property. Then read about the INCManager and NCManager. If you read the source code of the NCManager class, it’s pretty straightforward, and you can see that attempts to connect to your streaming application are made numerous times, first to port 1935, then to port 443, then to port 80 over RTMPT (http tunneling), then to port 443, over RTMPS.
That’s quite a few connection attempts to go through, which can cause delays in streams playing. Generally it’s not really a big deal. But in this case - a pretty serious application, I don’t want my apps attempting all of those, or just in that order.
Preferably I’d rather have it first check RTMPT over port 80, because port 80 is always open. Then check port 1935, then 443. It was pretty straight forward after diving into it a bit. I thought I’d share how to re-order the way that the NetConnection’s are attempted in the default NCManager class.
First thing, we need to update the AS3 source NCManager class. Find that source file, and open it up. There is a variable declared like so:
//ORIGINAL:
flvplayback_internal static const RTMP_CONN:Array = [
{ protocol: "rtmp:/", port:"1935" }
,{ protocol: "rtmp:/", port:"443" }
,{ protocol: "rtmpt:/", port:"80" }
,{ protocol: "rtmps:/", port:"443" }
];
//UPDATED:
flvplayback_internal static var RTMP_CONN:Array = [
{ protocol: "rtmp:/", port:"1935" }
,{ protocol: "rtmp:/", port:"443" }
,{ protocol: "rtmpt:/", port:"80" }
,{ protocol: "rtmps:/", port:"443" }
];
All I did here is update it so that it’s not constant, it’s now a var. Note that instead of changing the value of the variable in your class, just change it from const to var. So that in other flash applications, you don’t inherit something in the AS3 codebase you forgot about.
So now to update the variable:
package
{
use namespace flvplayback_internal;
public class MySomething
{
public function MySomething()
{
//change the values.
NCManager.flvplayback_internal::RTMP_CONN = [
{protocol: "rtmpt:/", port:"80" },
{protocol: "rtmp:/", port:"1935"},
{protocol: "rtmp:/", port:"443"}
]
}
}
}
Now you can successfully change the order of connection attempts.
I had to share this with anyone who would listen because this is so friggin cool. I’ve been working on this project at work and we’re using Flash Remoting for services, and we are expecting millions of hits, the most being 7 million a day, but averaging in the hundreds of thousands of users per day.
So we had to devise a way to stress test the server software. That’s the screen capture I posted a couple days ago. But anyway, I setup 7 computers that all had JMeter running - hitting our internal dev server. Total there were 2000 threads, sending 28 requests per thread. In 9 hours, the server handled 37 million requests without anything being a problem. That’s crazy!
The other thing that was so crazy is that each request or “transaction” hits the MS SQL server 5 times per transaction. The server side software is awesome. Chris Crissmond and Darrell Thurmond had written it, its all free threaded - basically one transaction takes 0 seconds. It’s written in C# and SQL Server. These guys did an amazing job.
Another thing that happens per transaction is hitting the M$ Live Search API. M$ themselves told us we wouldn’t be able to get more than about 40 transactions per second on hardware we had (32 bit, 3 gigs ram, dual core something). But we peaked at processing about 300 requests per second. The server we had didn’t even care about what was happening, memory was at about 540MB, and the cpu was staying between 25-40% usage.
The next morning, we had the Live Search team from M$ asking us how the hell we did that. Pretty friggin cool. Also note that JMeter bursts requests, it’s not constantly sending requests. So we probably could have done 90 million in 9 hours no problem. Crazy.
Another successful AMF project. Go AMF!
John Farrar had asked about SSR 2 - more information on SSR and what benefits / things you get with it. I thought I’d be a little more specific and outline what it is and what you get. Bare with me if I start out by pointing out the obvious.
So, I guess here’s the obvious. SSR is a small AS3 remoting library. Stands for “Super Simple Remoting” because it’s simple. My goal when writing it was to make one library that works in Flash and Flex and handle logic that should be in every application. Personally I like using this in both Flash and Flex because I don’t forget how to use it. I always forget how to use RemoteObject so this makes a standard library for either.
So here’s an example of a bare minimum setup of SSR to make a remoting call.
import com.niarbtfel.remoting.RemotingConnection;
import com.niarbtfel.remoting.RemotingService;
import com.niarbtfel.remoting.events.FaultEvent;
import com.niarbtfel.remoting.events.ResultEvent;
var rc:RemotingConnection = new RemotingConnection("http://localhost/myGateway",3);
//gateway
//3 is the amf format
var rs:RemotingService = new RemotingService(rc,"MyService",4000,3,true);
//rc is the remoting connection to use for this service
//"MyService" is the service target on the server.
//4000 is the amount of time a call waits before retrying the call.
//3 is the amount of retries to attempt per call.
//true turns on call limiting, which means that the same exact call can't be made while one is waiting for a response or timeout.
//callbacks for calls, keep reading.
function onr(re:ResultEvent):void{}
function onf(fe:FaultEvent):void{}
//callbacks for calls, keep reading.
function onrArgs(re:ResultEvent,args:Array):void{}
function onfArgs(fe:FaultEvent,args:Array):void{}
rs.myMethod([],onr,onf,false); //make a call
//[] = call arguments
//onr is the result callback
//onf is the fault callback
//false - don't return the original arguments sent in the request to the callback functions
var mym:String = "myMethod";
rs.apply(mym,[],onrArgs,onfArgs,true); //make a call
//[] = call arguments
//onr is the result callback
//onf is fault callback
//true - return the original arguments sent in the request to the callback functions
This is the simplest form of setting up flash remoting with SSR without any event listeners on the RemotingConnection or RemotingService. Events for RemotingConnection and RemotingService are documented in the class files. You get events for things like disconnects, retries, timeouts, etc.
Each RemotingService can have a timeout specified for each call going to that service, as well as a maximum attempts allowed per call. If one call is made, but doesn’t receive a response within the timeout specified, it’s tried again. This happens over and over until the maximum attempts is reached, and a timeout event is dispatched.
RemotingService has an option to turn on call limiting. Which makes the remoting service smart about what to call and what not to call. If you made a call to “MyService.myMethod” with parameters [”something”,”something else”], it would not let you make that same call until the first one has completed, or has timed out. You’d still be able to make calls to any other service method.
Another new feature is caching. Say you make calls to some service numerous times, but the data is always the same. Instead of doing that you can enable the service to use caching. Here’s a snippet from above with caching in place:
var rs:RemotingService = new RemotingService(rc,"MyService",4000,1,true); rs.remotingCache = new RemotingCache(-1); //-1 specifies that the cache should never expire. (supply a time in milliseconds for an expiration if needed)
Now every call made through this remoting service will be cached. If the exact same call is made, the results are pulled from cache instead.
SSR also has paging built in with a RemotingPager. Here’s a snippet on how that works:
import com.niarbtfel.remoting.RemotingConnection;
import com.niarbtfel.remoting.RemotingService;
import com.niarbtfel.remoting.events.FaultEvent;
import com.niarbtfel.remoting.events.ResultEvent;
import com.niarbtfel.remoting.paging.RemotingPager;
import com.niarbtfel.remoting.paging.PageResponder;
var rc:RemotingConnection = new RemotingConnection("http://localhost/myGateway",3);
var rs:RemotingService = new RemotingService(rc,"MyService",4000,3,true);
var rp:RemotingPager;
var pr:PageResponder // just to include in compilation
var totalRecordsOnServer:int = 400; //should come from server instead.
//callbacks for calls, keep reading.
function onrArgs(re:ResultEvent,args:Array):void
{
rp = new RemotingPager(rs,"myMethod",PageResponder,["someParameter"],re.result as Array,totalRecordsOnServer,50,2,1);
myButton.addEventListener(MouseEvent.CLICK, onbc);
}
function onfArgs(fe:FaultEvent,args:Array):void{}
function onbc(me:MouseEvent):void
{
if(rp.hasNext())
{
trace(rp.next());
}
else
{
trace("no more pages");
}
}
rs.myMethod(["someParameter",0,50],onrArgs,onfArgs,true); //make a call to get the initial data.
//0 is the offset
//50 is the pagesize
In this example, a remoting pager is created in the result callback of the first call. A remoting pager needs at least the first page in order to be created. The RemotingPager takes care of “pre-fetching” pages with buffer options. In the above example, where the RemotingPager is created, 50 is the pagesize to grab, 2 is the amount of pages to buffer / pre-fetch. And 1 is the amount of pages that are left in the buffer before triggering another buffer fill / pre-fetch. This is the simplest form of paging, but the RemotingPager has methods for getting certain pages, getting them all, seeking, etc.
UPDATE: I forgot to mention RemotingService.MaxTimeoutsBeforeHault.
With RemotingService.MaxTimeoutsBeforeHault, you can control how many timeouts are allowed from any call, before all remoting calls are stopped. Here’s a snippet:
RemotingService.MaxTimeoutsBeforeHault = 3;
var rs:RemotingService = new RemotingService(rc,"MyService",4000,3,true);
rs.addEventListener(CallEvent.SERVICES_HALTED, onch);
function onch(ce:CallEvent):void{}
rs.myMethod([],onR,onF,true);
When you set this property, any call that causes a timeout, is kept track of, and if 3 timeouts happen from any call, no other remoting calls will be made. Each attempt after the services are halted, dispatches the CallEvent.SERVICES_HALTED event.
In conclusion - SSR gives you logic out of the box that should be available anytime we are using Flash Remoting. And gives you numerous other features. SSR 2 is documented very well in the source files.
I’ve beefed-up SSR with numerous new features and improvements. I’ve updated OSFlash with new examples, and included a couple examples in the download. I’ve also changed the package structure, as I’m untying myself from the rubyamf package. Download and enjoy.
UPDATE: Check out the SSR 2 Breakdown.
I took some time to do a screen capture of AMF stress testing. It’s much easier to understand when you can see how to set it up. Check it out.
A while back I was tasked with coming up with a way to do stress testing for AMF. I looked at a bunch of tools, and a couple in particular. Those being curl-loader, httperf and JMeter. The only tool out of the box that supported “arbitrary binary support” was JMeter. AMF is sent in an HTTP request as arbitrary binary, not a regular multipart upload. Which is cool, but not many tools support that.
Anyway, I’m going to keep this short - here is a rough set of tools that will get you stress testing your AMF gateways. Make sure to read the “readme” in the zip file, this has enough instruction on the different tools, and the source for it all, so you can read through it, alter it, use it to your advantage.
Also, the capture client only works with SSR right now, but I’ve included the source for everything so you could go in and update the part that I left out which was support for Remote Object. It shouldn’t be a ton of work, sorry I couldn’t get that done.
I’ll be watching the RubyAMF mailing list for any help requests / questions about this. Hopefully this is really straight forward, and the readme does a pretty good job of explaining what’s going on with everything.
Enjoy.
There were a few volunteers that left comments on the “State of the Onion” post. Thanks! Can anyone interested leave a post at the google groups forum at http://groups.google.com/group/rubyamf/
Thanks again
Hey Dudes!
First off, I apologize for this post being so late. It should have been about a month ago. I’m moving on from RubyAMF. It’s been such a good time talking and meeting people who supported my efforts on RubyAMF. I couldn’t say thank you enough - but I could write a loop for you.
while(true)
{
trace("THANK YOU");
}
Put this in Flash and try it. I know you’ll love me.
Anyway. I put a ton of effort toward RubyAMF, and they haven’t gone unnoticed, it’s so cool. And I’m glad I was part of helping out make strides for remoting in Ruby.
I’m definitely not done with AMF though. Trust me :). I must say AMF is by far my favorite thing to work with when using Flex or Flash. It’s kind of awkward that I’m so passionate about AMF, but it’s an amazing creation from Macromedia with so many benefits.
As mentioned, I’m definitely not done with AMF, or writing / releasing cool stuff. First off. I’m sitting on PythonAMF. It’s not ready for a release yet. I need to do quite a bit of testing yet but it will happen.
I’ve also got another version of SSR done that I will release beginning of February. It includes great new additions. Here’s a quick breakdown of the new features.
Call limiting - Turning this on puts a remoting service in a state that will not allow duplicate calls to the same remoting service if a response has not been received from a previous one. Finally, this is built in so we don’t have to have scattered logic all over to handle this. This is extremely important because you can’t risk someone figuring out that you don’t limit calls made to the server, and then they smash your server. Ouch.
Caching - If a previous request is called again, it comes from cache instead. This includes options to expire cache after a certain time or after the cache has been hit a certain amount of times.
Paging - Yeah! Now there is a paging mechanism for those flashers (or flexers) who don’t get to use remote object. Or just don’t want to use remote object. Page buffering is built in, so you can set the buffer size to say 3 pages, and anytime there is only a certain amount of local pages (which you can specify) it retrieves the buffer pages. It has an iterator like interface so it’s all taken care of. As well as allowing “releaseAll,” some might remember that from Flash 8.
Paging is also built in a way that allows you to extend the default PageResponder (which you don’t have to touch by default). But this is nice if you want to put paging on a certain remoting service call, but the results you want to page are nested within the result object you get back.
I’ve also got a custom display renderer mechanism with the same iterator like interface that allows you to use a pager, (with say 50 items per page at a time), and have an interface for only displaying 10 at a time. So it’s like sub paging on the actual pager.
I think that’s it for new features. If I think of any others I added I’ll let you know.
Another issue I’m addressing is AMF stress testing. So far I’ve only seen the Flex Stress Testing framework which could possibly be used to test AMF gateways. But the amount of users it can act as to hit the server with is pretty limited, and the tool is so overwhelming to get setup.
AMF stress testing is exactly like it sounds. Test your AMF gateways with millions of hits. This is very important when working on something that is expected to have high volume traffic. I’ve experienced numerous sites that had high volume traffic and the server side couldn’t handle it. One in particular was remoting and just couldn’t take the hits. (Not because of AMF inefficiency, but because of server inefficiency.)
So as a way of spreading the word, that we can’t just blindly put up remoting gateways for a web site and not think anything of it. I’ve come up with a very simple way of stress testing AMF.
Here’s a quick rundown of how this all works.
There are a couple tools involved. First, a “capture client”, which is a simple Flex app that you fill out your service target, method name, arguments, amf version, and if it’s remote object or not. When that request is sent, it’s sent to a local “capture server.”
The “capture server” is a ruby webrick server that takes an incoming request and writes the raw post to a file (the AMF).
The next step in the process is JMeter. JMeter is an all around stress testing tool for many different web protocols. Jmeter was the only one I’ve found so far that allows for sending a POST request with “arbitrary binary data” (from RFC1867 secion 3.3).
That’s how the Flash player sends over AMF to the server, as stated in RFC1867, AMF is just arbitrary binary. Anyway, since the AMF is just in the body, all you really have to do is send an HTTP Post request to the gateway, along with the binary captured from the capture server. And voila, you can hit the gateway as many times as you want triggering your remoting software to react and perform the same operations as what would occur when a flash / flex request comes in. This is also very important, because it’s the software you write that get’s triggered for every remoting call. We can’t just hit an index.html page and assume everything is ok, when actually none of your server software is being tested.
The other piece to the puzzle is validating response. So far I’ve been using a tool called PlasticSniffer. It’s a very simple packet sniffer that will allow you to see the requests / responses of AMF. Generally you can see the UTF data in the response, which is just some text so you’d be able to read most of what’s going on. With some awkward characters tossed in.
So Anyway, I have been very busy working on more AMF goodies and will continue to be involved with AMF. Please stand by for SSR2 and that AMF capture tool, with JMeter instructions to get that running.
I had a great time with RubyAMF. And hope to hear from all of you in the future.
-Aaron