<?xml version="1.0" encoding="utf-8"?>
<html>
If you've ever looked at Firebug when loading a profile page, you're familiar with the flurry of activity involved in loading an OpenSocial app—but this doesn't mean your app can't be snappy to load. This field manual contains tactics for reducing latency in your app that will decrease the load on your servers and provide a better experience for your users.
Be sure to read the OpenSocial Latency Measurement guide to get an accurate measurement of your app's latency.
Many techniques that are used in normal web development will also benefit your OpenSocial app. Here are some of the most effective techniques.
Most containers offer support for the
Cache-Control |
HTTP header. You have server-side control over how your resources are cached, so be sure to set your headers appropriately for maximum benefit.
The
Cache-Control |
header is best described in the HTTP/1.1 specification but there are some simpler descriptions available as well. If you're not sure about the cache headers your server is currently sending, you can try some publicly available tools to examine the cache headers on your files and see if they need to be tweaked.
Be aware that the
Cache-Control |
header will be examined for all content coming from your server, including XML application specs, responses from
makeRequest |
(both prefetched and not), and proxied images. Be sure to set caching headers for all of this content!
Apache defaults to using
Last-Modified |
and
ETag |
headers to control caching for static files, rather than the recommended
Expires |
and
Cache-Control: max-age |
headers. If you are using Apache, change your cache headers to
Expires |
and
Cache-Control: max-age |
!
Need to disable caching on your Apache server? Use the following in your
.htaccess |
file to disable caching on
.css |
,
.js |
, and
.xml |
files (change the
FilesMatch |
line if you need to support more filetypes):
<source lang="javascript">
<FilesMatch "\.(css|js|xml)$">
Header unset ETag
FileETag None
Header set Cache-Control "no-cache"
</FilesMatch>
</source>
What are the benefits? Your server has much more control over how the container caches its content. You can set a low cache expiration for content that changes often, and a high cache timeout for content that does not change. Caching will become much more efficient once you set the appropriate headers.
The HTTP/1.1 specification states:
<blockquote>Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.</blockquote>
For this reason, some internet browsers (like IE7) will only download two files from a given server at a time, shared amongst all HTML, XML, image, CSS, and JavaScript files. To reduce the number of connections that a user has to make back to your server, consolidate and inline as much code as possible.
If your JavaScript includes look like:
<source lang="javascript">
<script src="http://www.example.com/javascript.core.js" type="text/javascript"></script> |
</source>
then you should combine each file into one master JavaScript file:
<source lang="javascript">
<script src="http://www.example.com/javascript.all.js" type="text/javascript"></script>
</source>
Better yet, inline your code if at all possible:
<source lang="javascript">
<script type="text/javascript">
function1() { |
</script>
</source>
This will save server connections for other assets. Remember that this approach can be used for CSS, as well.
To decrease the number of image files your application needs to load, you can use image spriting to combine all your image files into a single master "sprite" file. Check out A List Apart's CSS Spriting article for a good description of this technique.
Generally speaking, concatenating your files is a great performance improvement you can make. Because of the aggressive caching that containers perform, even using a relatively slow server-side script to automatically concatenate files will still wind up performing better than separate files (once the automatically concatenated file is cached). Aim for a single CSS and a single JS file in production.
What are the benefits? This approach keeps the number of server connections low, and reduces the total number of HTTP requests that each user of your application has to make.
App developers very often use frameworks such as Prototype, jQuery, Dojo, or Script.aculo.us. In OpenSocial container environment, where applications are embedded in multiple iframes, these libraries often get loaded multiple times, not properly gzipped or minified. Google's latest AJAX libraries API addresses these issues by serving properly gzipped and minified version of these libraries from fast edge servers that are close to clients, providing caching once and for all, and significantly improving performance.
For example, to load Prototype 1.6.0.2 you would do the following:
<source lang="javascript">
<script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js"></script>
</source>
For more info on how to take advantage of AJAX Libraries API to speed up your apps on Google's infrastructure, go to AJAX Libraries API.
*Turn on gzip for any content you deliver. Good things come in small packages. |
*Tip: *Try the YSlow Firefox plugin to analyze your app's performance.
The
gadgets.io.getProxyUrl |
function will return the location of the cached version of the URL you provide, including images, JavaScript, and CSS. So instead of using the URL of content hosted on your server, like this:
<source lang="javascript">
function showImage() {
imgUrl = 'http://www.example.com/i_heart_apis_sm.png'; |
};
showImage();
</source>
you can use the URL of the cached content, like this:
<source lang="javascript">
function showImage() {
imgUrl = 'http://www.example.com/i_heart_apis_sm.png'; |
};
showImage();
</source>
Take advantage of multiple content sections in your gadget spec to render more tailored views for canvas and profile pages. This will help ensure that the container only loads the necessary components for each view. In particular, focus on making your profile view as lean as possible.
It's much faster to request data from the container than it is to hit your own server. There are lots of ways you can cache your application data in the Persistence API and speed up page loads. The profile view is a great place to do this because it gets a lot of page views and there is less dynamic content.
Here's the slow way to load a profile page:
makeRequest |
Here's a much faster way:
DataRequest |
makeRequest |
First, let's look at using multiple content sections. Here's the bare minimum:
<source lang="javascript">
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="users <3 speed"> |
</Module>
</source>
Now let's use the technique where we populate the profile view with HTML cached in appData:
<source lang="javascript">
<Content type="html" view="profile"> |
<script type="text/javascript">
function request() { |
function response(data) {
console.log(data); |
};
gadgets.util.registerOnLoadHandler(request); |
</script>
]]> |
</source>
Finally, implement some functionality for the canvas view. When the user takes an action that will update the data shown in their profile, update the 'profile' field in appData. This app lets the user set a quote to be displayed on their profile. When the 'save' link is clicked, the quote and the HTML to display in the profile view are updated in appData. Here's the full application spec:
<source lang="javascript">
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="users <3 speed"> |
<script type="text/javascript">
function request() { |
function response(data) {
console.log(data); |
};
gadgets.util.registerOnLoadHandler(request); |
</script>
]]> |
<script type="text/javascript">
function request() { |
function response(data) {
var viewer = data.get("viewer") && data.get("viewer").getData(), function save() { |
function response2(data) { |
/*
|
function status(text) {
var dom = |
gadgets.util.registerOnLoadHandler(request); |
</script>
<div id="main"></div>
<div id="status"></div>
]]> |
</Module>
</source>
</html>