SPDY, Google’s web protocol, is gaining momentum. Intending to improve the user’s web experience it aims at severely reducing page load times.
We’ve blogged about the protocol and jetty’s straight forward SPDY support already: Jetty-SPDY is joining the revolution! and SPDY support in Jetty.
No we’re taking this a step further and we push!
SPDY push is one of the coolest features in the SPDY protocol portfolio.
In the traditional http approach the browser will have to request a html resource (the main resource) and do subsequent requests for each sub resource. Every request/response roundtrip will add latency.
E.g.:
GET /index.html – wait for response before before browser can request sub resources
GET /img.jpg
GET /style.css – wait for response before we can request sub resources of the css
GET /style_image.css (referenced in style.css)
This means a single request – response roundtrip for each resource (main and sub resources). Worse some of them have to be done sequentially. For a page with lots of sub resources, the amount of connections to the server (traditionally browsers tend to open 6 connections) will also limit the amount of sub resources that can be fetched in parallel.
Now SPDY will reduce the need to open multiple connections by multiplexing requests over a single connection and does more improvements to reduce latency as described in previous blog posts and the SPDY spec.
SPDY push will enable the server to push resources to the browser/client without having a request for that resource. For example if the server knows that index.html contains a reference to img.jpg, style.css and that style.css contains a reference to style_image.css, the server can push those resources to the client.
To take the previous example:
GET /index.html
PUSH /img.jpg
PUSH /style.css
PUSH /style_image.css
That means only a single request/response roundtrip for the main resource. And the server immediately sends out the responses for all sub resources. This heavily reduces overall latency, especially for pages with high roundtrip delays (bad/busy network connections, etc.).
We’ve written a unit test to benchmark the differences between plain http, SPDY and SPDY + push. Note that this is not a real benchmark and the roundtrip delay is emulated! Proper benchmarks are already in our task queue, so stay tuned. However, here’s the results:
HTTP: roundtrip delay 100 ms, average = 414
SPDY(None): roundtrip delay 100 ms, average = 213
SPDY(ReferrerPushStrategy): roundtrip delay 100 ms, average = 160
Sounds cool? Yes, I guess that sounds cool! 🙂
Even better in jetty this means only exchanging a Connector with another, provide our implementation of the push strategy – done. Yes, that’s it. Only by changing some lines of jetty config you’ll get SPDY and SPDY + push without touching your application.
Have a look at the Jetty Docs to enable SPDY. (will be updated soon on how to add a push strategy to a SPDY connector.)
Here’s the only thing you need to configure in jetty to get your application served with SPDY + push transparently:
<New id=”pushStrategy”>
<Arg type=”List”>
<Array type=”String”>
<Item>.*.css</Item>
<Item>.*.js</Item>
<Item>.*.png</Item>
<Item>.*.jpg</Item>
<Item>.*.gif</Item>
</Array>
</Arg>
<Set name=”referrerPushPeriod”>15000</Set>
</New>
<Call name=”addConnector”>
<Arg>
<New>
<Arg>
<Ref id=”sslContextFactory” />
</Arg>
<Arg>
<Ref id=”pushStrategy” />
</Arg>
<Set name=”Port”>11081</Set>
<Set name=”maxIdleTime”>30000</Set>
<Set name=”Acceptors”>2</Set>
<Set name=”AcceptQueueSize”>100</Set>
<Set name=”initialWindowSize”>131072</Set>
</New>
</Arg>
</Call>
So how do we push?
We’ve implemented a pluggable mechanism to add a push strategy to a SPDY connector. Our default strategy, called ReferrerPushStrategy is using the “referer” header to identify push resources on the first time a page is requested.
The browser will request the main resource and quickly afterwards it usually requests all sub resources needed for that page. ReferrerPushStrategy will use the referer header used in the sub requests to identify sub resources for the main resource defined in the referer header. It will remember those sub resources and on the next request of the main resource, it’ll push all sub resources it knows about to the client.
Now if the user will click on a link on the main resource, it’ll also contain a referer header for the main resource. However linked resources should not be pushed to the client in advance! To avoid that ReferrerPushStrategy has a configurable push period. The push strategy will only remember sub resources if they’ve been requested within that period from the very first request of the main resource since application start.
So this is some kind of best effort strategy. It does not know which resources to push at startup, but it’ll learn on a best effort basis.
What does best effort mean? It means that if the browser doesn’t request the sub resources fast enough (within the push period timeframe) after the initial request of the main resource it’ll never learn those sub resources. Or if the user is fast enough clicking links, it might push resources which should not be pushed.
Now you might be wondering what happens if the browser has the resources already cached? Aren’t we sending data over the wire which the browser actually already has? Well, usually we don’t. First we use the if-modified-since header to identify if we should push sub resources or not and second the browser can refuse push streams. If the browser gets a syn for a sub resource it already has, then it can simply reset the push stream. Then the only thing that has been send is the syn frame for the push stream. Not a big drawback considering the advantages this has.
There has to be more drawbacks?!
Yes, there are. SPDY implementation in jetty is still experimental. The whole protocol is bleeding edge and implementations in browsers as well as the server still have some rough edges. There is already broad support amoung browsers for the SPDY protocol. Stable releases of firefox and chromium/chrome support SPDY draft2 out of the box and it already works really well. SPDY draft 3 however is only supported with more recent builds of the current browsers. SPDY push seems to work properly only with SPDY draft 3 and the latest chrome/chromium browsers. However we’re all working hard on getting the rough edges smooth and I presume SPDY draft 3 and push will be working in all stable browsers soon.
We also had to disable push for draft 2 as this seemed to have negative effects on chromium up to regular browser crashes.
Try it!
As we keep eating our own dog-food, https://www.webtide.com is already updated with the latest code and has push enabled. If you want to test the push functionality get a chrome canary or a chromium nightly build and access our company’s website.
This is how it’ll look in the developer tools and on chrome://net-internals page.
developer-tools (note that the request has been done with an empty cache and the pushed resources are being marked as read from cache):

net-internals (note the pushed and claimed resource count):

Pretty exciting! We keep “pushing” for more and better SPDY support. Improve our push strategy and support getting SPDY a better protocol. Stay tuned for more stuff to come.
Note that SPDY stuff is not in any official jetty release, yet. But most probably will be in the next release. Documentation for jetty will be updated soon as well.


2 Comments

John Esposito · 25/06/2012 at 19:52

Hi Tom,
We just published a six-page cheat-sheet/reference card covering Jetty. Want to trade links?
Thanks, and take care,
John Esposito
Content Curator, DZone

Ming Lin · 28/02/2013 at 09:46

Hi Thomas,
Greate job! In the section “Try it!”, you give us a test scenario and result, it is impressive. But I don’t know what the developer-toos is and how to do the test, would you please give me the detailed information? Thanks.

Comments are closed.