ajax to the moon
DESCRIPTION
CommunityOne presentation about offline Ajax, Ajax push, and Ajax performance issues.TRANSCRIPT
Ajax to the Moon
Dave Johnson
CTO and Co-founderNitobiwww.nitobi.com
2007 CommunityOne 2
Agenda
1. Who I Am
2. Offline Ajax
3. Pushing Ajax
4. Ajax Performance
5. Summary
2007 CommunityOne 3
Who I Am
• Nitobi Enterprise Ajax Podcast• Enterprise Ajax book (Prentice Hall)• blogs.nitobi.com/dave
2007 CommunityOne 4
What Do I Do?
• Nitobi co-founder• Located in Vancouver, Canada• Ajax user-interface components
for the enterprise
2007 CommunityOne 5
2007 CommunityOne 6
Clients
2007 CommunityOne 7
Agenda
1. Who I Am
2. Offline Ajax
3. Pushing Ajax
4. Ajax Performance
5. Summary
2007 CommunityOne 8
Offline Ajax
http://www.flickr.com/photos/natearcher/378519540/
2007 CommunityOne 9
Know your users
2007 CommunityOne 10
Why Offline
• Hard at work• Get on a plane• Keep on working
2007 CommunityOne 11
http://www.flickr.com/photos/plakboek/339017053/
2007 CommunityOne 12
Why Offline
• Hard at work• Get on a plane• Keep on working• Get Internet connection and sync data
• More common than you may think!
2007 CommunityOne 13
2007 CommunityOne 14
2007 CommunityOne 15
But Seriously
• Internet connections do not grow on trees• Try going to Sun HQ and getting on their network!
2007 CommunityOne 16
How Does it Work?
• Work offline• Store data locally when browser is closed• Send data back to server when connected
2007 CommunityOne 17
2007 CommunityOne 18
Storage
• Cookies• Firefox / Internet Explorer only• Flash• Install software
2007 CommunityOne 19
Cookies
• 4Kb of storage per domain• Easily deleted – probably suspected as Spyware
2007 CommunityOne 20
Firefox 2.0 Offline Storage
• Cookies on steroids• Data not available across sessions• 5Mb of storage by default• A bit buggy at the moment but Firefox 3 should
have global storage across sessions
2007 CommunityOne 21
<html> <head> <script type=”text/javascript”>function saveSession(myparam,myvalue) { // will save the attribute myparam with value myvalue sessionStorage[myparam] = myvalue;}function loadSession(myparam) { // will retrieve myparam from sessionStorage var myresult = sessionStorage[myparam]; return myresult;} </script> </head> <body> <form id="myform" name="myform"> <input type="text" id="myvalue" name="myvalue"> <input type="button" value="Save" onclick="saveSession('myattribute',myform.myvalue.value)"> <input type="button" value="Load" onclick="alert(loadSession('myattribute'))"> </form> </body></html>
2007 CommunityOne 22
Internet Explorer userData Behavior
• Access data across sessions• 128Kb limit for Internet (512Kb for Intranet)• Relies on Internet Explorer Behaviors• What about HTML Applications?
2007 CommunityOne 23
<html> <head> <script type=”text/javascript”>function save(myparam,myvalue) { $(“storageInput”).setAttribute(myparam, myvalue).save("mydata");}function load(myparam) { return $(“storageInput”).load("mydata").getAttribute(myparam);} </script> <style>.storage { behavior:url(#default#userData); display:none;} </style> </head> <body> <form id="myform" name="myform"> <input type="text" id="myvalue" name="myvalue"> <input type="button" value="Save"
onclick="save('myattr',myform.myvalue.value)"> <input type="button" value="Load" onclick="alert(load('myattribute'))"> <div id=”storageInput” class=”storage”></div> </form> </body></html>
2007 CommunityOne 24
Flash SharedObject Storage
• Access data across sessions• User definable storage limit (default of 100Kb)• Back to Flash 6 (98% adoption)
2007 CommunityOne 25
Flash Movie
import flash.external.ExternalInterface;
function saveAttribute(datastore, paramname, paramvalue) { mySharedObject = SharedObject.getLocal(datastore); mySharedObject.data[paramname] = paramvalue; mySharedObject.flush();}
function loadAttribute(datastore,paramname) { return SharedObject.getLocal(datastore).data[paramname]; }
ExternalInterface.addCallback("saveAttribute", this, saveAttribute); ExternalInterface.addCallback("loadAttribute", this, loadAttribute);
2007 CommunityOne 26
<html> <head> <script>function thisMovie(movieName) { if (navigator.appName.indexOf("Microsoft") != -1) return window[movieName]; else return document[movieName];}function saveData(store, param, pvalue) { thisMovie(‘myStorage’).saveAttribute(store,param,pvalue);}function loadData(store, param) { return(thisMovie(‘myStorage’).loadAttribute(store,param));} </script> </head> <body> <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000“ codebase="…" width="1" height="1" id="myStorage" align="middle"> <param name="movie" value="localstore.swf" /> <param nam="allowScriptAccess" value="always"> <embed src="localstore.swf" allowScriptAccess="always" width="1" height="1"
name="myStorage“ type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> </body></html>
2007 CommunityOne 27
<html> <head> <script>function thisMovie(movieName) { if (navigator.appName.indexOf("Microsoft") != -1) return window[movieName]; else return document[movieName];}function saveData(store, param, pvalue) { thisMovie(‘myStorage’).saveAttribute(store,param,pvalue);}function loadData(store, param) { return(thisMovie(‘myStorage’).loadAttribute(store,param));} </script> </head> <body> <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000“ codebase="…" width="1" height="1" id="myStorage" align="middle"> <param name="movie" value="localstore.swf" /> <param nam="allowScriptAccess" value="always"> <embed src="localstore.swf" allowScriptAccess="always" width="1" height="1"
name="myStorage“ type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer" /> </object> </body></html>
2007 CommunityOne 28
Apollo
• Adobe’s new runtime – currently Alpha• Offline capable, runs from your computer• A lot more than just offline!
• Flex + WebKit (i.e. Safari) in one
2007 CommunityOne 29
Web Proxy
• Proxy auto-config – browser proxy settings• JavaScript file with FindProxyForURL• Forward requests to a local proxy for interested
domains• Used by the Dojo Toolkit
2007 CommunityOne 30
Salesforce.com
Local Proxy
2007 CommunityOne 31
Whole Hog
• Web server• Database• Run locally• Zimbra uses this approach
2007 CommunityOne 32
Issues
• Performance• Concurrency
• Your own data or shared?
• End user requirements• Are they a captive user?
• Penetration
2007 CommunityOne 33
End user reqs / Penetration Performance Concurrency
Cookies
Browser Specific
Flash
Proxy
Whole Hog
2007 CommunityOne 34
End user reqs / Penetration Performance Concurrency
Cookies
Browser Specific
Flash
Proxy
Whole Hog
2007 CommunityOne 35
End user reqs / Penetration Performance Concurrency
Cookies
Browser Specific
Flash
Proxy
Whole Hog
2007 CommunityOne 36
End user reqs / Penetration Performance Concurrency
Cookies
Browser Specific
Flash
Proxy
Whole Hog
2007 CommunityOne 37
End user reqs / Penetration Performance Concurrency
Cookies
Browser Specific
Flash
Proxy
Whole Hog
2007 CommunityOne 38
Agenda
1. Who I Am
2. Offline Ajax
3. Pushing Ajax
4. Ajax Performance
5. Summary
2007 CommunityOne 39
Pushing Ajax
http://www.flickr.com/photos/vidiot/69075298/
2007 CommunityOne 40
Watch out for your thumb
2007 CommunityOne 41
Why Push?
• Keep client up to date• Collaboration
• Documents• Communication
• Time sensitive data
2007 CommunityOne 42
How Does it Work?
Big scary InternetInnocent MySpace user MySpace Evil EmpireTM
2007 CommunityOne 43
Polling
• Means to the same end• Easy to build
• Request update from server every N seconds• Support thousands of concurrent users?
users / pollTime = requests per second
2007 CommunityOne 44http://www.flickr.com/photos/roadhunter/68017710/
2007 CommunityOne 45
How Does it Work?
Big scary InternetInnocent MySpace user MySpace Evil EmpireTM
thumb twiddling …
2007 CommunityOne 46
Push
• Keep connection to server open• Server writes to stream when ready• Support thousands of concurrent users?
numConnections ≡ numServerThreads
2007 CommunityOne 47
http://www.flickr.com/photos/rev_bri/112249215/
2007 CommunityOne 48
Asynchronous Servlets
• Sun – Grizzly HTTP Connector• BEA – AbstractAsyncServlet• Tomcat 6.x – CometServlet• Jetty – Continuations
• No standards here
2007 CommunityOne 49
Cometd / Bayeux
• Cometd reference implementation• Bayeux pub/sub on top of Comet
• Think JMS for JavaScript
2007 CommunityOne 50
DEMO
• http://localhost:8080/examples/magnets/
2007 CommunityOne 51
Piggyback
• The middle of the road• Send data to client with any other responses
2007 CommunityOne 52
Other Approaches to Push
• LiveCycle Data Services• LightStreamer
2007 CommunityOne 53
Issues
• Server load• Internet Explorer
2007 CommunityOne 54
Agenda
1. Who I Am
2. Offline Ajax
3. Pushing Ajax
4. Ajax Performance
5. Summary
2007 CommunityOne 55
Ajax Performance
http://www.flickr.com/photos/edfladung/451378444/
Ajax Performance
2007 CommunityOne 56
Ask questions first, optimize second
2007 CommunityOne 57
What is the Bottleneck?
• Situation dependent• How much data?• What type of data?• How many server hits?• What are the common workflows?• What browsers are you targeting?• What is the existing infrastructure?
2007 CommunityOne 58
Lifecycle
2007 CommunityOne 59
Data Formats
• XML• <xml>You know this</xml>
• JSON• {“json”:[“maybe”,”you”,don’t”],”know”:”this”}
• HTML• <html>Looks familiar</html>
• Text• Images
2007 CommunityOne 60
Server
• HTML• Whatever you use today
• XML• Standard support for object serialization• Similar to HTML
• JSON• Easy to add on
2007 CommunityOne 61
Bandwidth
• HTML• <div style=“color: red;”>Napoleon</div>
• XML• <customer><name>Napoleon</name></customer>• <c n=“Napoleon”/>
• JSON• {“customer”:[{“Name”:”Napoleon”}]}• {“c”:[{“n”:”Napoleon”}]}
• Polling, code size etc
2007 CommunityOne 62
JavaScript
• HTML• Nothing to do
• XML• Transform with XSLT (browser dependent)• Iterate over XML DOM nodes and string build• Iterate over XML DOM nodes and create HTML DOM nodes
• JSON• eval() the block of JSON to a JavaScript object• Transform with JSON Templates• Iterate through JavaScript arrays and string build• Iterate through JavaScript arrays and create HTML DOM nodes
2007 CommunityOne 63
Internet Explorer
2007 CommunityOne 64
Firefox
2007 CommunityOne 65
http://www.jamesward.org/census/
Firefox
Internet Explorer
2007 CommunityOne 66
XSLT Cross Browser Performance
0
5
10
15
20
25
30
35
40
45
0 100 200 300 400 500
Table Cells
Tim
e (m
s)
Firefox 1.5
IE 6
2007 CommunityOne 67
Don’t Lose your Keys
<xsl:key name=“sales-by-customer” match=“customer” use=“@name” />
<xsl:value-of select="key(‘sales-by-customer', $CustomerName)">
2007 CommunityOne 68
XSLT Performance
0
5
10
15
20
25
30
35
40
45
0 100 200 300 400 500
Table Cells
Tim
e (m
s)
IE 6 (with key)
IE 6 (w/o key)
2007 CommunityOne 69
JSON with Padding (JSONP)
• Most apps use XHR• Using XHR for JSON necessitates eval()• eval() is slow
• Instead dynamically insert a <script> tag and have the returned JavaScript call a function …
• http://localhost/javaone/
2007 CommunityOne 70
The Fast, Server Dependent Wayfunction getPostsSi() { var elem = document.createElement("script"); elem.src = "http://del.icio.us/feeds/json/davyjones?
callback=buildHtml"; document.body.appendChild(elem);}
function buildHtml(obj) { var s = []; for (var i=0; i<obj.length; i++) { s.push("<div><a href='"+obj[i].u+"'>" + obj[i].d +
"</a></div>"); } $("container").innerHTML = s.join("");}
2007 CommunityOne 71
The Slow, Server Agnostic Way
function getPostsXhr() { var xhr = new nitobi.ajax.HttpRequest(); xhr.handler =
"http://localhost/feeds/json/davyjones"; xhr.onComplete.subscribe(xhrDataReady); xhr.send();}
function xhrDataReady(evtArgs) { var obj = eval("("+evtArgs+")"); buildHtml(obj);}
2007 CommunityOne 72
HTML DOM
• HTML• $(“myNode”).innerHTML = “<div>Napoleon</div>”;
• XML• innerHTML• DOM manipulations
• createElement(“div”), appendChild(node), etc
• JSON• innerHTML• DOM manipulations
• Interactivity
2007 CommunityOne 73
General DOM Function
2007 CommunityOne 74
Most Wanted
• innerHTML – consider HTML tags, floats, events• offsetTop / Left• getBoundingClientRect / getBoxObjectFor• Stylesheets
2007 CommunityOne 75
Watch Out for Memory Leaks
• Circular loops between the DOM and JavaScript
• Use Drip to find them
<script type=“text/javascript”>var domNode = $(“myDomNode”);domNode.foo = domNode;</script>
expando
2007 CommunityOne 76
JavaScript Compression
• Removing comments• Removing whitespace• Removing new-line characters• Replacing variables with shorter names
2007 CommunityOne 77
Simple Example
var _a = function(a){var b=0;var c=a.length;for(var d=0;d<c;d++){b+=a[d];}return b/c;}var calcAverage=_a;
/** * @private */var _calcAverage = function(aNumber) { var nTotal = 0; var iLength = aNumber.length; for (var iIndex = 0; i<iLength; i++) { nTotal += aNumber[iIndex]; } return nTotal/iLength;}/** * Calculates the average of an array of numbers. * @param {Array} Array of numbers to average. */var calcAverage = _calcAverage;
2007 CommunityOne 78
Rhino Minification / Obfuscation
<target name="obfuscateJS" description="compress and obfuscate code">
<java classname="org.mozilla.javascript.tools.shell.Main“dir="${basedir}\build\rhino\bin\"fork="true"output="${basedir}\output\src_obfuscated.js">
<arg line="-c ${basedir}\input\src.js" /> <classpath> <pathelement path="${basedir}\build\rhino\bin\js.jar"/> </classpath> </java></target>
2007 CommunityOne 79
Compression
• Request• Accept-Encoding: gzip,deflate
• Response• Content-Encoding: gzip || deflate
• IIS and Apache dynamically GZip / Deflate content and cache it
• All modern browsers support compressed content• Internet Explorer 6 SP1 had some problems
• IE, Firefox, Opera accept compressed content _without_ Content-Encoding header
2007 CommunityOne 80
Apache Compression
# Compress everything unless excluded below.
SetOutputFilter DEFLATE
SetInputFilter DEFLATE
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|rar)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \.(?:pdf|avi|mov|mp3|rm)$ no-gzip dont-vary
# Explicity compress certain file types
AddOutputFilterByType DEFLATE text/html text/plain text/xml
2007 CommunityOne 81
Size (Kb)
Original 9.3
Minify 3.9
GZip / Deflate 2.8
Minify + GZip / Deflate 1.3
Size Reduction 86%
How Small is It?Expected Results for JavaScript Compression
2007 CommunityOne 82
Content Merging
• Reduce download overhead by merging resources• JavaScript• Cascading Stylesheets• Images
• Careful with caching though!
2007 CommunityOne 83
Image Merging
2007 CommunityOne 84
Image Merging
<html> <head> <style type="text/css" media="screen"> .colour {clip: rect(0px 135px 125px 0px);} .grayscale { left:-135px; clip: rect(0px 270px 125px 135px); } .grayscale, .colour { position:absolute; width: 270px;height: 125px; background: url(images/nitobi.jpg); } .container { height:125px;width:135px; position:relative; } </style> </head> <body> <div class="container"><div class="colour"></div></div> <div class="container"><div class="grayscale"></div></div> </body></html>
clip: rect(0px 135px 125px 0px);
clip: rect(0px 270px 125px 135px);
background: url(images/nitobi.jpg);
2007 CommunityOne 85
Agenda
1. Who I Am
2. Offline Ajax
3. Pushing Ajax
4. Ajax Performance
5. Summary
2007 CommunityOne 86
Summary
• “Work Offline” – know your users• Browser specific, Flash or Install• Usability
• Push – watch out for your thumb• Real-time collaboration• Usability
• Performance – ask questions first, optimize later• Be careful but not too careful• Usability