<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>igvita.com</title>
	
	<link>http://www.igvita.com</link>
	<description>A goal is a dream with a deadline.</description>
	<pubDate>Tue, 10 Nov 2009 17:22:39 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/igvita" type="application/rss+xml" /><feedburner:emailServiceId>igvita</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>Consuming XMPP PubSub in Ruby</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/DYDYvKzwjxM/</link>
		<comments>http://www.igvita.com/2009/11/10/consuming-xmpp-pubsub-in-ruby/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 17:22:39 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[pubsub]]></category>

		<category><![CDATA[realtime]]></category>

		<category><![CDATA[xmpp]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=819</guid>
		<description><![CDATA[XMPP is a very versatile protocol with well over several hundred proposed and working extensions, which has also proven itself in production (ex: Google Talk). Presence, roster management, federated and server to server (S2S) messaging are all examples of features that you get for free, which make it a very appealing platform for messaging applications. [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/posts/09/xmpp-logo.png" style="margin-right: 1em;"/>XMPP is a very versatile protocol with well over several hundred <a href="http://xmpp.org/extensions/">proposed and working extensions</a>, which has also proven itself in production (ex: Google Talk). Presence, roster management, federated and server to server (S2S) messaging are all examples of features that you get for free, which make it a very appealing platform for messaging applications. Combine it with extensions such as <a href="http://xmpp.org/extensions/xep-0060.html">XEP-0060 (PubSub)</a>, and we have all the relevant buzzwords: pubsub, real-time, federated, and presence. </p>
<p>The PubSub specification within XMPP, as defined in XEP-0060, is definitely <a href="http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/">not as flexible as that of AMQP</a>, but it is often times enough to cover the most popular use cases. However, technical merits aside, one of the key missing components, especially in Ruby, has been the historical lack of functioning libraries - xmpp4r claims to support it, but examples are lacking. Thankfully, after test driving the latest batch of gems, it looks like we're finally there. </p>
<h4><strong>Getting off the ground with XMPP</h4>
<p></strong></p>
<p>Without a good toolkit XMPP can be a gnarly protocol to get started with - <a href="http://www.pidgin.im/">Pidgin IM client</a> has some great tools for spying on the exchange, but monitoring pages of XML scroll by can only get you so far. Thankfully, Seth Fitzsimmons has built <a href="http://github.com/mojodna/switchboard">switchboard</a> ("curl for XMPP"), which offers a powerful command line tool to greatly simplify the process. Make sure to read the <a href="http://mojodna.net/2009/07/16/switchboard-curl-for-xmpp.html">full tutorial</a>, or jump right into it by testing it with the <a href="http://en.support.wordpress.com/jabber/">Wordpress XMPP stream</a>:</p>
<blockquote><p>
# list available options, subscribe to a blog, list subscriptions and then open the stream<br />
switchboard disco --target pubsub.im.wordpress.com info<br />
switchboard pubsub --server pubsub.im.wordpress.com --node /blog/icanhazcheesburger.com subscribe<br />
switchboard pubsub --server pubsub.im.wordpress.com subscriptions<br />
switchboard pubsub --server pubsub.im.wordpress.com listen
</p></blockquote>
<p>Based on <a href="http://home.gna.org/xmpp4r/">xmpp4r</a>, switchboard is also a toolkit for assembling your own XMPP clients, which means that it can be easily customized to power a PubSub consumer. From start to finish, and since examples are still hard to come by:</p>
<p><a href="javascript:showme('514_1');"> <b>> switchboard-pubsub.rb</b></a>
<div style=" background:white;" id=514_1>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'switchboard'</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> WordpressJack
  <span style="color:#9966CC; font-weight:bold;">def</span> <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9900CC;">connect</span><span style="color:#006600; font-weight:bold;">&#40;</span>switchboard, settings<span style="color:#006600; font-weight:bold;">&#41;</span>
    switchboard.<span style="color:#9900CC;">plug</span>!<span style="color:#006600; font-weight:bold;">&#40;</span>PubSubJack<span style="color:#006600; font-weight:bold;">&#41;</span>
    switchboard.<span style="color:#9900CC;">hook</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:post</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
    switchboard.<span style="color:#9900CC;">on_pubsub_event</span> <span style="color:#9966CC; font-weight:bold;">do</span> |event|
      event.<span style="color:#9900CC;">payload</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> |payload|
        payload.<span style="color:#9900CC;">elements</span>.<span style="color:#9900CC;">each</span> <span style="color:#9966CC; font-weight:bold;">do</span> |item|
          on<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:post</span>, item<span style="color:#006600; font-weight:bold;">&#41;</span>
        <span style="color:#9966CC; font-weight:bold;">end</span>
      <span style="color:#9966CC; font-weight:bold;">end</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
settings = <span style="color:#6666ff; font-weight:bold;">Switchboard::Settings</span>.<span style="color:#9900CC;">new</span>
settings<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'pubsub.server'</span><span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">'pubsub.im.wordpress.com'</span>
settings<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'jid'</span><span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">'user@im.wordpress.com'</span>
settings<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'password'</span><span style="color:#006600; font-weight:bold;">&#93;</span> = <span style="color:#996600;">'password'</span>
&nbsp;
switchboard = <span style="color:#6666ff; font-weight:bold;">Switchboard::Client</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>settings<span style="color:#006600; font-weight:bold;">&#41;</span>
switchboard.<span style="color:#9900CC;">plug</span>!<span style="color:#006600; font-weight:bold;">&#40;</span>WordpressJack<span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
switchboard.<span style="color:#9900CC;">on_post</span> <span style="color:#9966CC; font-weight:bold;">do</span> |post|
  <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;A new post was received:&quot;</span>
  <span style="color:#CC0066; font-weight:bold;">puts</span> post.<span style="color:#9900CC;">methods</span>.<span style="color:#9900CC;">sort</span>.<span style="color:#9900CC;">uniq</span>
  <span style="color:#CC0066; font-weight:bold;">exit</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
switchboard.<span style="color:#9900CC;">run</span>!
&nbsp;</pre>
</div>
<h4><strong>XMPP with EventMachine and Nokogiri</strong></h4>
<p>If you have an EventMachine stack, or looking for a high performance library, Jeff Smick's <a href="http://github.com/sprsquish/blather">blather</a> is definitely a gem to investigate. The combination of the asynchronous nature of EventMachine, a SAX parser within Nokogiri, and a great DSL make it very fast and a pleasure to work with:</p>
<p><a href="javascript:showme('514_2');"> <b>> blather-pubsub.rb</b></a>
<div style=" background:white;" id=514_2>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'blather/client/client'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'blather/client/dsl/pubsub'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'blather'</span>
&nbsp;
EventMachine.<span style="color:#9900CC;">run</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
  host = <span style="color:#996600;">'pubsub.im.wordpress.com'</span>
  node = <span style="color:#996600;">'blog/icanhazcheesburger.com'</span>
  user = <span style="color:#996600;">'user@im.wordpress.com'</span>
  pass = <span style="color:#996600;">'pass'</span>
&nbsp;
  jid = <span style="color:#6666ff; font-weight:bold;">Blather::JID</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>user<span style="color:#006600; font-weight:bold;">&#41;</span>
  client = <span style="color:#6666ff; font-weight:bold;">Blather::Client</span>.<span style="color:#9900CC;">setup</span><span style="color:#006600; font-weight:bold;">&#40;</span>jid, pass<span style="color:#006600; font-weight:bold;">&#41;</span>
  client.<span style="color:#9900CC;">register_handler</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:ready</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Connected. Send messages to #{client.jid.inspect}.&quot;</span>
    pub = <span style="color:#6666ff; font-weight:bold;">Blather::DSL::PubSub</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span>client, host<span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  client.<span style="color:#9900CC;">register_handler</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:pubsub_event</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#123;</span> |event|
    <span style="color:#CC0066; font-weight:bold;">puts</span> event
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  client.<span style="color:#9900CC;">connect</span>
<span style="color:#006600; font-weight:bold;">&#125;</span></pre>
</div>
<h4><strong>PubSub & Event-Driven Architecture</strong></h4>
<p>Having personally struggled in the past with XMPP PubSub and Ruby, it's been great to revisit the use case and find a new set of fully functional libraries. The <a href="http://www.igvita.com/2009/04/06/henry-ford-event-driven-architecture/">event driven architecture</a> which is enabled by technologies such as XMPP, <a href="http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/">AMQP</a>, <a href="http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/">Comet</a>, <a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">Webhooks and PubsubHubbub</a> are increasingly becoming the staple of many web applications, and for a good reason. If you haven't already, grab <a href="http://github.com/mojodna/switchboard">switchboard</a> or <a href="http://github.com/sprsquish/blather">blather</a> and take XMPP for a test drive.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=DYDYvKzwjxM:9L9LPcvu6SY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=DYDYvKzwjxM:9L9LPcvu6SY:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=DYDYvKzwjxM:9L9LPcvu6SY:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=DYDYvKzwjxM:9L9LPcvu6SY:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=DYDYvKzwjxM:9L9LPcvu6SY:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=DYDYvKzwjxM:9L9LPcvu6SY:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=DYDYvKzwjxM:9L9LPcvu6SY:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=DYDYvKzwjxM:9L9LPcvu6SY:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=DYDYvKzwjxM:9L9LPcvu6SY:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/DYDYvKzwjxM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/11/10/consuming-xmpp-pubsub-in-ruby/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/11/10/consuming-xmpp-pubsub-in-ruby/</feedburner:origLink></item>
		<item>
		<title>Nginx &amp; Comet: Low Latency Server Push</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/21hcnE4IGjM/</link>
		<comments>http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 16:06:45 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[comet]]></category>

		<category><![CDATA[nginx]]></category>

		<category><![CDATA[push]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=793</guid>
		<description><![CDATA[Server push is the most efficient and low latency way to exchange data. If both the publisher and the receiver are publicly visible then a protocol such as PubSubHubbub or a simpler Webhook will do the job. However, if the receiver is hidden behind a firewall, a NAT, or is a web-browser which is designed [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" style="margin: 0pt 1em 0pt 0pt;" src="http://www.igvita.com/blog/posts/09/comet.png"/>Server push is the most efficient and low latency way to exchange data. If both the publisher and the receiver are publicly visible then a protocol such as <a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">PubSubHubbub or a simpler Webhook</a> will do the job. However, if the receiver is hidden behind a firewall, a NAT, or is a web-browser which is designed to generated outbound requests, not handle incoming traffic, then the implementation gets harder. If you are adventurous, you could setup a <a href="http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/">ReverseHTTP server</a>. If you are patient, you could wait for the <a href="http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/">WebSocket's API</a> in HTML5. And if you need an immediate solution, you could compromise: instead of a fully asynchronous push model, you could use <a href="http://en.wikipedia.org/wiki/Comet_%28programming%29">Comet</a>, also known as Reverse Ajax, HTTP Server Push, or HTTP Streaming.</p>
<p>Coined by <a href="http://alex.dojotoolkit.org/2006/03/comet-low-latency-data-for-the-browser/">Alex Russell in early 2006</a>, the term Comet is an umbrella term for technologies which take advantage of persistent connections initiated by the client and kept open until data is available (<a href="http://en.wikipedia.org/wiki/Comet_%28programming%29#Ajax_with_long_polling">long polling</a>), or kept open indefinitely as the data is pushed to the client (<a href="http://en.wikipedia.org/wiki/Comet_%28programming%29#Streaming">streaming</a>) in chunks. The immediate advantage of both techniques is that the client and server can communicate with minimal latency. For this reason, Comet is widely deployed in chat applications (Facebook, Google, Meebo, etc), and is also commonly used as a firehose delivery mechanism. </p>
<h4><strong>Converting Nginx into a Long Polling Comet Server</strong></h4>
<p>A large entry barrier to Comet adoption is the implicit requirement for specialized, event driven web servers capable of efficiently handling large numbers of long polling connections. <a href="http://www.tornadoweb.org/">Friendfeed's Tornado</a> server is a good example of an app level server that meets the criteria. However, thanks to Leo Ponomarev's efforts, you can now also turn your Nginx server into a fully functional Comet server with the <a href="http://github.com/slact/nginx_http_push_module">nginx_http_push_module</a> plugin.  </p>
<p align="center"><img style="border: 1px solid rgb(204, 204, 204); " src="http://www.igvita.com/posts/09/nginx-push.png"/></p>
<p>Instead of using a custom framework, Leo's plugin exposes two endpoints on your Nginx server: one for the subscribers, and one for the publisher. The clients open long-polling connections to a channel on the Nginx server and start waiting for data. Meanwhile, the publisher simply POST's the data to Nginx and the plugin then does all the heavy lifting for you by distributing the data to the waiting clients. This means that the publisher never actually serves the data directly, it is simply an event generator! It is hard to make it any simpler then that.</p>
<p>Best of all, it only gets better from here. Both the client and the publisher can create arbitrary channels, and the plugin is also capable of message queuing, which means that the Nginx server will store intermediate messages if the client is offline. Queued messages can be expired based on time, size of the waiting stack, or through a memory limit. </p>
<h4><strong>Configuring Nginx & Ruby Demo</strong></h4>
<p>To get started you will have to build Nginx from source. Unpack the <a href="http://wiki.nginx.org/NginxInstall#Source_Releases">source tree</a>, grab the plugin repo from GitHub and then build the server with the push module (<strong>./configure --add-module=/path/to/plugin && make && make install</strong>). Next, consult the <a href="http://github.com/slact/nginx_http_push_module/blob/master/README">readme</a> and the <a href="http://github.com/slact/nginx_http_push_module/blob/master/protocol.txt">protocol</a> files to learn about all the available options. A simple multi client broadcast configuration looks like the following:</p>
<p><a href="javascript:showme('3178_1');"> <b>> nginx-push.conf</b></a>
<div style=" background:white;" id=3178_1>
<pre class="ruby"><span style="color:#008000; font-style:italic;"># internal publish endpoint (keep it private / protected)</span>
location /publish <span style="color:#006600; font-weight:bold;">&#123;</span>
  set <span style="color:#ff6633; font-weight:bold;">$push_channel_id</span> <span style="color:#ff6633; font-weight:bold;">$arg_id</span>;      <span style="color:#008000; font-style:italic;">#/?id=239aff3 or somesuch</span>
  push_publisher;
&nbsp;
  push_store_messages on;            <span style="color:#008000; font-style:italic;"># enable message queueing </span>
  push_message_timeout 2h;           <span style="color:#008000; font-style:italic;"># expire buffered messages after 2 hours</span>
  push_max_message_buffer_length <span style="color:#006666;">10</span>; <span style="color:#008000; font-style:italic;"># store 10 messages</span>
  push_min_message_recipients <span style="color:#006666;">0</span>;     <span style="color:#008000; font-style:italic;"># minimum recipients before purge</span>
<span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># public long-polling endpoint</span>
location /activity <span style="color:#006600; font-weight:bold;">&#123;</span>
  push_subscriber;
&nbsp;
  <span style="color:#008000; font-style:italic;"># how multiple listener requests to the same channel id are handled</span>
  <span style="color:#008000; font-style:italic;"># - last: only the most recent listener request is kept, 409 for others.</span>
  <span style="color:#008000; font-style:italic;"># - first: only the oldest listener request is kept, 409 for others.</span>
  <span style="color:#008000; font-style:italic;"># - broadcast: any number of listener requests may be long-polling.</span>
  push_subscriber_concurrency broadcast;
  set <span style="color:#ff6633; font-weight:bold;">$push_channel_id</span> <span style="color:#ff6633; font-weight:bold;">$arg_id</span>;
  default_type  text/plain;
<span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;</pre>
</div>
<p>Once you have the Nginx server up and running, we can setup a simple broadcast scenario with a single publisher and several subscribers to test-drive our new Comet server:</p>
<p><a href="javascript:showme('3178_2');"> <b>> comet-push-consume.rb</b></a>
<div style=" background:white;" id=3178_2>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'rubygems'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'em-http'</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">def</span> subscribe<span style="color:#006600; font-weight:bold;">&#40;</span>opts<span style="color:#006600; font-weight:bold;">&#41;</span>
  listener = <span style="color:#6666ff; font-weight:bold;">EventMachine::HttpRequest</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'http://127.0.0.1/activity?id='</span>+ opts<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:channel</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">get</span> <span style="color:#ff3333; font-weight:bold;">:head</span> =&gt; opts<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:head</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  listener.<span style="color:#9900CC;">callback</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#008000; font-style:italic;"># print recieved message, re-subscribe to channel with</span>
    <span style="color:#008000; font-style:italic;"># the last-modified header to avoid duplicate messages </span>
    <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Listener recieved: &quot;</span> + listener.<span style="color:#9900CC;">response</span> + <span style="color:#996600;">&quot;<span style="color:#000099;">\\n</span>&quot;</span>
&nbsp;
    modified = listener.<span style="color:#9900CC;">response_header</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">'LAST_MODIFIED'</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    subscribe<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">&#123;</span>:channel =&gt; opts<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:channel</span><span style="color:#006600; font-weight:bold;">&#93;</span>, <span style="color:#ff3333; font-weight:bold;">:head</span> =&gt; <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#996600;">'If-Modified-Since'</span> =&gt; modified<span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
EventMachine.<span style="color:#9900CC;">run</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
  channel = <span style="color:#996600;">&quot;pub&quot;</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Publish new message every 5 seconds</span>
  EM.<span style="color:#9900CC;">add_periodic_timer</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006666;">5</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
    time = <span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">now</span>
    publisher = <span style="color:#6666ff; font-weight:bold;">EventMachine::HttpRequest</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'http://127.0.0.1/publish?id='</span>+channel<span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">post</span> <span style="color:#ff3333; font-weight:bold;">:body</span> =&gt; <span style="color:#996600;">&quot;Hello @ #{time}&quot;</span>
    publisher.<span style="color:#9900CC;">callback</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Published message @ #{time}&quot;</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Response code: &quot;</span> + publisher.<span style="color:#9900CC;">response_header</span>.<span style="color:#9900CC;">status</span>.<span style="color:#9900CC;">to_s</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Headers: &quot;</span> + publisher.<span style="color:#9900CC;">response_header</span>.<span style="color:#9900CC;">inspect</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Body: <span style="color:#000099;">\\n</span>&quot;</span> + publisher.<span style="color:#9900CC;">response</span>
      <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;<span style="color:#000099;">\\n</span>&quot;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># open two listeners (aka broadcast/pubsub distribution)</span>
  subscribe<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:channel</span> =&gt; channel<span style="color:#006600; font-weight:bold;">&#41;</span>
  subscribe<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:channel</span> =&gt; channel<span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/nginx-push.zip'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/downloads.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/nginx-push.zip'>nginx-push.zip (Full Nginx Config + Ruby client)</a>
							</h4><p>Downloads: 133 File Size: 2.7 KB </p>
						</div> </p>
<p>In the script above, every five seconds a publisher emits a new event to our Nginx server, which in turn, pushes the data to two subscribers which have long-polling connections open and are waiting for data. Once the message is sent to each subscriber, Nginx closes their connections and the clients then immediately re-establish them to wait for the next available message. End result, a real-time message push between the publisher and the clients via Nginx!</p>
<h4><strong>Long Polling, Streaming, and Comet in Production</strong></h4>
<p>Leo's module is still very young and is under active development, but it is definitely one to keep an eye on. The upcoming release is focused on bug fixes, but looking ahead there are also plans to add a streaming protocol: instead of closing the connection every time (aka, long polling), Nginx would keep it open and stream the incoming events as chunks of data to the clients in real-time. Having such an option would make it ridiculously easy to deploy your own firehose API's (ex: <a href="http://apiwiki.twitter.com/Streaming-API-Documentation">Twitter streaming</a>).</p>
<p>Last but not least, don't forget about the growing number of <a href="http://wiki.nginx.org/NginxModules">other available</a> <a href="http://wiki.nginx.org/Nginx3rdPartyModules">modules for Nginx</a>, or if you are so inclined, get a head start on building your own by reading <a href="http://www.evanmiller.org/nginx-modules-guide.html">Evan Miller's  great guide</a> on the subject. </p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=21hcnE4IGjM:rUjIiurf9O8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=21hcnE4IGjM:rUjIiurf9O8:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=21hcnE4IGjM:rUjIiurf9O8:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=21hcnE4IGjM:rUjIiurf9O8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=21hcnE4IGjM:rUjIiurf9O8:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=21hcnE4IGjM:rUjIiurf9O8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=21hcnE4IGjM:rUjIiurf9O8:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=21hcnE4IGjM:rUjIiurf9O8:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=21hcnE4IGjM:rUjIiurf9O8:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/21hcnE4IGjM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/10/21/nginx-comet-low-latency-server-push/</feedburner:origLink></item>
		<item>
		<title>Advanced Messaging &amp; Routing with AMQP</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/79W3JS0O34w/</link>
		<comments>http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/#comments</comments>
		<pubDate>Thu, 08 Oct 2009 16:34:17 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[amqp]]></category>

		<category><![CDATA[pubsub]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=748</guid>
		<description><![CDATA[Not all message queues are made equal. In the simplest case, a message queue is synonymous with an asynchronous protocol in which the sender and the receiver do not operate on the message at the same time. However, while this pattern is most commonly used to decouple distinct services (an intermediate mailbox, of sorts), the [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/blog/posts/09/amqp-rabbit.png" style="margin: 0pt 1em 0pt 0pt;"/>Not all message queues are made equal. In the simplest case, a message queue is synonymous with an asynchronous protocol in which the sender and the receiver do not operate on the message at the same time. However, while this pattern is most commonly used to decouple distinct services (an intermediate mailbox, of sorts), the more advanced implementations also enable a host more advanced recipes: load balancing, queueing, failover, pubsub, etc. AMQP can do all of the above, and yesterday's announcement of <a href="http://lists.rabbitmq.com/pipermail/rabbitmq-announce/attachments/20091007/3aaed239/attachment.txt">RabbitMQ 1.7</a> (an open source AMQP broker) warrants a closer look.</p>
<p>Originally developed at JP Morgan as a vendor neutral wire and broker protocol, AMQP (<a href="http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol">Advanced Message Queuing Protocol</a>) is, in fact, a general purpose messaging bus. The protocol itself is still under active development, but there are a variety of open source client and server implementations for it, as well as some big commercial supporters (RedHat, Microsoft, etc). In other words, it works, it is production ready, and I can vouch for it from personal experience - we stream tens of millions of messages through <a href="http://data.postrank.com/api_rt_content.html">AMQP at PostRank</a> on a daily basis.</p>
<h4><strong>AMQP vs XMPP: Features & Architecture</strong></h4>
<p>The AMQP vs XMPP debate has been <a href="https://stpeter.im/index.php/2007/12/07/amqp-and-xmpp/">raging</a> <a href="http://saladwithsteve.com/2007/08/future-is-messaging.html">for</a> <a href="http://www.opensourcery.co.za/2009/04/19/to-amqp-or-to-xmpp-that-is-the-question/">years</a> now. On the surface they both look identical, but in reality there are a number of important distinctions. For example, presence is one of the central components of XMPP, but it is not part of the AMQP specification. XMPP uses XML, whereas AMQP has a binary protocol. AMQP has native support for a number of delivery use cases (at least once, exactly once, select subscribers, persistence, etc) and also a variety of <em>exchange implementations</em> which allow fine-grained control to where and how the messages are routed.</p>
<p align="center"><img src="http://www.igvita.com/posts/09/amqp-diagram.png" style="border: 1px solid rgb(204, 204, 204); padding: 12px;"/></p>
<p>The <a href="http://jira.amqp.org/confluence/display/AMQP/AMQP+Specification">AMQP spec</a> is a fast and recommended read, but by a way of quick introduction, the core architectural components are: publisher, exchange, queue, and consumer. As you may have guessed, the publisher is the data producer which pushes messages to an exchange. Why is it called an exchange? Because the exchange is a routing engine which is responsible for delivering the messages to the right queues (exchanges never store messages). For example, a message may need to be routed to just a single queue (<strong>direct exchange</strong>), maybe the message should be forwarded to every queue (pubsub) in the list (<strong>fanout exchange</strong>), or perhaps the message should be routed based on a key (<strong>topic exchange</strong>). </p>
<h4><strong>Publishing & Consuming AMQP in Ruby</strong></h4>
<p>The type of exchange, message parameters, and the name of the attached queue can all contribute to the delivery and routing behavior of the message. However, for the sake of example, let's create a simple pubsub fanout exchange in Ruby:</p>
<p><a href="javascript:showme('6174_1');"> <b>> amqp-publisher.rb</b></a>
<div style=" background:white;" id=6174_1>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'amqp'</span>
&nbsp;
AMQP.<span style="color:#9900CC;">start</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:host</span> =&gt; <span style="color:#996600;">'localhost'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#008000; font-style:italic;"># create a fanout exchange on the broker</span>
  exchange = MQ.<span style="color:#9900CC;">new</span>.<span style="color:#9900CC;">fanout</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'multicast'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># publish multiple messages to fanout</span>
  exchange.<span style="color:#9900CC;">publish</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'hello'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  exchange.<span style="color:#9900CC;">publish</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'world'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;</pre>
</div>
<p>In order to consume the messages from an exchange the consumer needs to create a queue and then bind it to an exchange. A queue can be durable (survive between server restarts), or auto-deletable for cases when the queue should disappear if the consumer goes down. Best of all, once the queue is bound to an exchange, the messages are streamed to the client in real-time via a persistent connection (no polling!):</p>
<p><a href="javascript:showme('6174_2');"> <b>> amqp-consumer.rb</b></a>
<div style=" background:white;" id=6174_2>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'amqp'</span>
&nbsp;
AMQP.<span style="color:#9900CC;">start</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:host</span> =&gt; <span style="color:#996600;">'localhost'</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  amq = MQ.<span style="color:#9900CC;">new</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># bind 'listener' queue to 'multicast' exchange</span>
  amq.<span style="color:#9900CC;">queue</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'listener'</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">bind</span><span style="color:#006600; font-weight:bold;">&#40;</span>amq.<span style="color:#9900CC;">fanout</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'multicast'</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">subscribe</span> <span style="color:#9966CC; font-weight:bold;">do</span> |msg|
    <span style="color:#CC0066; font-weight:bold;">puts</span> msg <span style="color:#008000; font-style:italic;"># process your message here</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.github.com/tmm1/amqp/tree/master/.git'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/github.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.github.com/tmm1/amqp/tree/master/.git'>amqp.git (AMQP client implementation in Ruby/EventMachine)</a>
							</h4><p>Downloads: 195 File Size: 0.0 KB </p>
						</div> </p>
<h4><strong>Advanced AMQP Recipes</strong></h4>
<p>The flexibility of the message and the exchange model is what makes AMQP such a powerful tool. Whenever a publisher generates a message, he can mark it as 'persistent' which will guarantee delivery through the broker - if there is an attached queue, it will accumulate messages until the consumer requests them. However, if you're streaming transient data (access logs, for example), you can also disable message persistence and not worry about overwhelming your broker. That's how you achieve 'exactly-once' vs 'at least once' semantics.</p>
<p>Trying to build a <strong>pubsub hub?</strong> Create a fanout exchange and attach as many queues as you want, each consumer will receive a copy of the message. <strong>Load balancing?</strong> Bind two workers to the same queue and the broker will automatically round-robin the messages (there is no upper limit on the number of workers). <strong>Failover?</strong> By default the AMQP broker does not require a message to be ACKed by a consumer, but with a simple configuration flag the messages will be kept on the server until the ACK is received. If the consumer goes down without ACKing a message, they will be automatically put back on the queue. Need to <strong>route a message based on a key?</strong> Topic exchange allows partial matching based on a message key that is set by the producer. Do you want to <strong>notify the producer if there are no subscribers</strong> attached to a queue? Set the immediate flag on the message and the broker will do all the work.
<p>Best of all, you can also compose these patterns to cover <a href="http://somic.org/2008/11/11/using-rabbitmq-beyond-queueing/">virtually any</a> <a href="http://www.rabbitmq.com/how.html">delivery use case</a>!</p>
<h4><strong>AMQP Brokers & Ruby / Rails</strong></h4>
<p><img align="left" src="http://www.igvita.com/blog/posts/09/amqp-brokers.png" style="margin: 0pt 1em 0pt 0pt;"/>There are a variety of available broker implementations: <a href="http://www.zeromq.org/">ZeroMQ</a>, <a href="http://activemq.apache.org/">ActiveMQ</a>, <a href="http://www.openamq.org/">OpenAMQ</a>, and <a href="http://www.rabbitmq.com/">RabbitMQ</a>. Because the underlying protocol is still in flux, there is definitely some variation between all the implementations - do your homework. If you're looking for a speed demon, ZeroMQ claims a 15.4 microsend routing overhead (4+ million msgs/s). However, RabbitMQ is arguably the most stable and feature complete broker implementation. If you are a CentOS or a Fedora user, you'll be happy to know that it is now <a href="https://admin.fedoraproject.org/updates/rabbitmq-server">part of the distro</a> (<em>yum install rabbitmq-server</em>), otherwise follow the <a href="http://www.rabbitmq.com/download.html">installation instructions</a>.</p>
<p>Once the server is installed, follow the <a href="http://www.rabbitmq.com/admin-guide.html">administration guide</a> to start the broker. If you're looking for a RESTful or a GUI tool to help you configure the broker, drop in <a href="http://willcodeforfoo.com/2009/07/13/announcing-alice/">Alice on the same server</a>.  Like the SQL prompt? Install the <a href="http://www.rabbitmq.com/rabbitmq-bql.html">BQL plugin</a> and familiarize yourself with the syntax.</p>
<p>The <a href="http://github.com/tmm1/amqp">AMQP gem</a> is probably the best choice when it comes to Ruby clients - it is asynchronous, it is fast, and it is in use by dozens of companies. If you're looking for a synchronous client, <a href="http://github.com/famoseagle/carrot">Carrot</a> gem is the answer. If you're using async-observer plugin in your Rails projects, you can drop in <a href="http://github.com/jamie/async-observer-amqp">async-observer-amqp</a> to migrate to AMQP. In other words, it is easy to get started, it is incredibly powerful, and it has great library support for virtually every language. Give it a try.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=79W3JS0O34w:m2IAfjXVxa0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=79W3JS0O34w:m2IAfjXVxa0:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=79W3JS0O34w:m2IAfjXVxa0:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=79W3JS0O34w:m2IAfjXVxa0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=79W3JS0O34w:m2IAfjXVxa0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=79W3JS0O34w:m2IAfjXVxa0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=79W3JS0O34w:m2IAfjXVxa0:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=79W3JS0O34w:m2IAfjXVxa0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=79W3JS0O34w:m2IAfjXVxa0:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/79W3JS0O34w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/</feedburner:origLink></item>
		<item>
		<title>Deploying JRuby on Google App Engine</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/veLCA_cjj04/</link>
		<comments>http://www.igvita.com/2009/09/23/deploying-jruby-on-google-app-engine/#comments</comments>
		<pubDate>Wed, 23 Sep 2009 15:44:24 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[gae]]></category>

		<category><![CDATA[google]]></category>

		<category><![CDATA[jruby]]></category>

		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=704</guid>
		<description><![CDATA[Following the Java support announcement for Google App Engine back in April there was a small flurry of excitement about the possibility of JRuby on GAE. However, while the notion was appealing, the execution was lacking - you had to do backflips to get JRuby, servlets, and XML configs all properly bundled. Compared to the [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" style="margin: 0pt 1em 0pt 0pt;" src="http://www.igvita.com/blog/posts/09/gae-ruby.png"/>Following the <a href="http://googleappengine.blogspot.com/2009/04/seriously-this-time-new-language-on-app.html">Java support announcement</a> for Google App Engine back in April there was a small flurry of excitement about the possibility of JRuby on GAE. However, while the notion was appealing, the execution was lacking - you had to do backflips to get JRuby, servlets, and XML configs all properly bundled. Compared to the aesthetically pleasing experience of running a Sinatra, Rack, or even a Rails app with Phusion, it is no wonder that most of us promptly forgot about GAE.</p>
<p>Having said that, the auto-scale, load-balancing, geographic redundancy, <a href="http://code.google.com/appengine/docs/java/datastore/">BigTable datastore</a>, <a href="http://code.google.com/appengine/docs/java/xmpp/overview.html">XMPP</a>, <a href="http://code.google.com/appengine/docs/java/memcache/overview.html">memcached</a> and the newer <a href="http://code.google.com/appengine/docs/java/config/cron.html">cron</a> and <a href="http://code.google.com/appengine/docs/java/taskqueue/overview.html">taskqueue</a> services, are definitely all reasons to revisit the platform. John Woodell and Ryan Brown, both Google employees, have been doing terrific work on helping to lower the entry barrier. In fact, while the documentation still needs a lot of work and polish, the deployment story and the API's are basically there - it now takes just a few keystrokes to get your Ruby app on GAE!</p>
<h4><strong>Migrating to Google App Engine</strong></h4>
<p>The first thing you'll notice with App Engine is that most of your Ruby/Rails applications won't run on the platform out of the box. GAE offers a lot of great benefits, such as free load-balancing and access to the BigTable datastore amongst many other things, but it does so by abstracting or removing some of the interfaces we're all used to: there is no filesystem, BigTable is not a relational database, cron jobs are HTTP calls, and so on. While an inconvenience at first, the functionality is still there but it does require some extra work on behalf of the developer. For example, while ActiveRecord does not work at the moment, there is a <a href="http://github.com/genki/dm-datastore-adapter">DataMapper adapter</a> that can meet all of your needs. Likewise, your code cannot make arbitrary outbound HTTP connections, but you can <a href="http://code.google.com/p/appengine-jruby/source/browse/appengine-apis/lib/appengine-apis/urlfetch.rb">patch net/http</a> to use the URLFetch API.</p>
<p align="center"><img style="border: 1px solid #ccc; padding:12px;" src="http://www.igvita.com/posts/09/gae-dashboard.png"/></p>
<p>The net positive of all of these changes is that your application can then be uniformly scaled across the GAE infrastructure. By default, you receive a free quota for up to (roughly) five million requests, and beyond that a credit card is required. Best of all, you no longer have to think about hardware, databases, or any other supporting services. BigTable guarantees uniform access time for your queries irrespective of the size of the dataset (at a cost of higher overall latency), and CPU, memcached, and XMPP load is automatically spread within the GAE cluster. And if you're feeling adventurous, you could try deploying a distributed file system (<a href="http://code.google.com/p/gaevfs/">GaeVFS</a>), or even build your own GAE hosting environment from scratch with <a href="http://code.google.com/p/appscale/">AppScale</a>.</p>
<h4><strong>Hello World with GAE</strong></h4>
<p>The tooling support for getting your application on GAE is now <a href="http://www.youtube.com/watch?v=pHMpf6hx8Ek&feature=youtube_gdata">almost fully automated</a>. Ryan Brown's <a href="http://code.google.com/p/appengine-jruby/source/browse/#hg/appengine-apis">appengine-api's gem</a> has full coverage of all the essential services, and the <a href="http://code.google.com/p/appengine-jruby/">google-appengine gem</a> provides a number of essential tools to help you with the generation, setup, and deployment of your applications. A simple "Hello World" Rack application is as simple as:</p>
<p><a href="javascript:showme('1278_1');"> <b>> rack-gae.rb</b></a>
<div style=" background:white;" id=1278_1>
<pre class="ruby"><span style="color:#008000; font-style:italic;"># install GAE tools (no JRuby required)</span>
<span style="color:#008000; font-style:italic;"># &gt; sudo gem install google-appengine</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># create a rackup file: config.ru</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'appengine-rack'</span>
&nbsp;
<span style="color:#6666ff; font-weight:bold;">AppEngine::Rack</span>.<span style="color:#9900CC;">configure_app</span><span style="color:#006600; font-weight:bold;">&#40;</span>
    <span style="color:#ff3333; font-weight:bold;">:application</span> =&gt; <span style="color:#996600;">&quot;your-app-id&quot;</span>,
    <span style="color:#ff3333; font-weight:bold;">:version</span> =&gt; <span style="color:#006666;">1</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;
run <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#6666ff; font-weight:bold;">Rack::Response</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">&quot;Hello World!&quot;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># start development server on local server</span>
<span style="color:#008000; font-style:italic;"># &gt; dev_appserver.rb .</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># deploy application to GAE</span>
<span style="color:#008000; font-style:italic;"># &gt; appcfg.rb update .</span>
&nbsp;</pre>
</div>
<p>All the configuration and deployment logic is done on your behalf by the <strong>dev_appserver</strong> and <strong>appcfg</strong> executables: packaging JRuby, creating the XML configs, authentication, etc. Deploying a Rails application, takes <a href="http://rails-primer.appspot.com/">just a few more steps</a>. Once the app is live, login into your <a href="http://www.youtube.com/watch?v=o32tWdyaIFA">GAE dashboard</a> to scan the logs, query BigTable, or track your quotas - no need for mucking around with any system utilities!</p>
<h4><strong>Developing for App Engine</strong></h4>
<p><a href="http://watercoolr.appspot.com"><img align="left" style="margin: 0pt 1em 0pt 0pt;" src="http://www.igvita.com/blog/posts/09/watercoolr.png"/></a>In theory, GAE has all the potential to become a popular deployment platform for Ruby applications (not to mention a nice market for <a href="http://www.youtube.com/watch?v=7mh9vUzOVng">custom Google Apps modules</a>). The lacking resources at this point are the blog posts, developers, and the overall Ruby community mindshare around App Engine. John Woodell and Ryan Brown have done a fantastic job of lowering the entry barriers, but we're still missing the real-life production deployments that would weed out the API bugs and inconsistencies, as well as, build the overall trust in the platform. </p>
<p>Curious to give it a try, I took an evening to try and port a small, but non-trivial app to App Engine. Having settled on watercoolr (<a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">webhooks pubsub</a>), I <a href="http://github.com/igrigorik/watercoolr/commit/cb5324076a9c81cc2b4c61bcd368132734b4d82d">swapped out SQLite for BigTable</a> and a few keystrokes later, I had a public watercoolr service (<a href="http://watercoolr.appspot.com/">http://watercoolr.appspot.com</a>) in production. No configuration, no need to worry about overloading the database. <a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">Feel free to use it</a> for your own HTTP pubsub needs! GAE is an exciting platform, check out the <a href="http://code.google.com/p/appengine-jruby/wiki/GettingStarted">examples</a>, <a href="http://code.google.com/appengine/articles/">articles</a>, and make sure to give it a try.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=veLCA_cjj04:oPMlhapvWRE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=veLCA_cjj04:oPMlhapvWRE:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=veLCA_cjj04:oPMlhapvWRE:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=veLCA_cjj04:oPMlhapvWRE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=veLCA_cjj04:oPMlhapvWRE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=veLCA_cjj04:oPMlhapvWRE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=veLCA_cjj04:oPMlhapvWRE:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=veLCA_cjj04:oPMlhapvWRE:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=veLCA_cjj04:oPMlhapvWRE:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/veLCA_cjj04" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/09/23/deploying-jruby-on-google-app-engine/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/09/23/deploying-jruby-on-google-app-engine/</feedburner:origLink></item>
		<item>
		<title>Collaborative Filtering with Ensembles</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/897t42Bxxy8/</link>
		<comments>http://www.igvita.com/2009/09/01/collaborative-filtering-with-ensembles/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 18:08:03 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Machine Learning]]></category>

		<category><![CDATA[ensemble]]></category>

		<category><![CDATA[github]]></category>

		<category><![CDATA[ml]]></category>

		<category><![CDATA[netflix]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=609</guid>
		<description><![CDATA[One of the most interesting insights from the results of the Netflix challenge is that while the algorithms, the psychology, and good knowledge of statistics goes a long way, it was ultimately the cross-team collaboration that ended the contest. "The Ensemble" team, appropriately named for the technique they used to merge their results consists of [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/blog/posts/08/algorithm.png" style="margin: 0pt 1em 0pt 0pt;"/>One of the most interesting insights from the results of the Netflix challenge is that while the algorithms, the <a href="http://www.wired.com/techbiz/media/magazine/16-03/mf_netflix">psychology</a>, and good knowledge of statistics goes a long way, it was ultimately the <a href="http://www.nytimes.com/2009/07/28/technology/internet/28netflix.html?_r=2&hpw">cross-team collaboration</a> that ended the contest. "<a href="http://www.the-ensemble.com/">The Ensemble</a>" team, appropriately named for the technique they used to merge their results consists of over 30 people. Likewise, the runner up team ("<a href="http://www.research.att.com/~volinsky/netflix/bpc.html">BellKor</a>") is a collaborative effort of several distinct groups that merged their results. It is easy to overlook this fact, except that it is not a one-time occurrence. The leaderboard for the recent <a href="http://contest.github.com/">GitHub contest</a> also clearly shows over half of the top ten entries as <a href="http://en.wikipedia.org/wiki/Machine_learning_ensemble">ensemble techniques</a>!</p>
<p>Few people would argue against the idea of consulting several experts on any complex subject - as the saying goes: "two heads are better than one". Having said that, the ensemble techniques which leverage this exact strategy are rarely a hot topic in the machine learning communities, or at least, up until now. Given their recent success this is likely to change, but perhaps even more importantly, their effectiveness may actually force the 'collaborative filtering' space to become, well, a lot more collaborative.</p>
<h4><strong>GitHub Contest: The Good, The Bad</strong></h4>
<p><img align="left" src="http://www.igvita.com/blog/posts/09/github-netflix.png" style="margin: 0pt 1em 0pt 0pt;"/>Religious arguments about the merits of each distributed version control system aside, one thing GitHub does really well is allow users to follow and discover new open source projects. In that light, a competition for a recommendations engine makes perfect sense. As part of the challenge you get access to records of over 56K users, 120K repositories, and 440K relationships between each. Great data set, immediate feedback via post-commit hooks, all results available in the open, and a few people <a href="http://github.com/xlvector/xlvector-github-contest/tree/master">even</a> <a href="http://github.com/hintly/hintly_ensemble/tree/master">shared</a> <a href="http://github.com/jheuer/github-contest-ensemble/tree/master">their</a> <a href="http://github.com/jdrowell/ghcontest-crowdsourced/tree/master">code</a>!</p>
<p>If there was one thing to improve about the contest, then it would have to be the ranking methodology. The goal was to "guess" the 4,788 removed repositories, which I would argue, is optimizing for the wrong thing. The goal should have been not to guess what users are already watching, but to produce a more general predictor for what users <strong>should be watching</strong> - the former is a subset of the latter. To see why, think of the case where a general predictor might actually have a set of better recommendations (based on other user's patterns) than the few repos that the GitHub team hid from the dataset for any user.</p>
<h4><strong>Ensemble Techniques & Machine Learning</strong></h4>
<p>Ranking methodology aside, perhaps the most interesting result of the GitHub contest is due to how it was setup: each submission is a push into a public Git repo, which means that each entry is automatically in the public domain. Unlike the Netflix contest, where all submissions were private and teams had to agree to merge their results, the GitHub contest became a free-for-all and a fertile crowd for testing ensembles!</p>
<p><img align="left" src="http://www.igvita.com/blog/posts/09/hspace.png" style="margin: 0pt 1em 0pt 0pt;"/>While the topic of Ensembles <a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.108.6096">is</a> <a href="http://en.wikipedia.org/wiki/Machine_learning_ensemble">a</a> <a href="http://users.rowan.edu/~polikar/RESEARCH/PUBLICATIONS/csm06.pdf">rich</a> <a href="http://www.eecs.wsu.edu/~holder/courses/CptS570/fall07/papers/Dietterich00.pdf">one</a>, the general idea is remarkably simple: instead of attempting to build one general model to capture all the subtleties of the data, use multiple predictors and combine their results. For an illustrative example, assume that we were trying to build a model that would separate the data in the diagram above. After a brief examination, we form a simple hypothesis (call this hypothesis <strong>family H</strong>): we could separate the data using simple geometric shapes such as a circle (<strong>H1</strong>), or a square (<strong>H2</strong>). Visually we can clearly see that neither one by itself will do a good job, but applying them both gives us a pretty good approximation while the model remains very simple (we want our model to be simple, for computational reasons). So what do we do? We train two independent classifiers and then merge their results, aka, use an ensemble of classifiers.</p>
<p>The description above sweeps a lot of details under the rug, but core of the idea is there. <a href="http://en.wikipedia.org/wiki/Boosting">Boosting</a>, <a href="http://en.wikipedia.org/wiki/Bootstrap_aggregating">bagging</a>, consensus aggregation, dynamic classifier selection, and dozens of other techniques have been developed on top of this process and have proven to be very successful. The overarching principle of ensemble techniques is to make each predictor as unique as possible: use a different learning algorithm (<a href="http://www.igvita.com/2007/04/16/decision-tree-learning-in-ruby/">decision trees</a>, <a href="http://www.igvita.com/2008/01/07/support-vector-machines-svm-in-ruby/">svm</a>, <a href="http://www.igvita.com/2007/01/15/svd-recommendation-system-in-ruby/">svd</a>), or use a different feature (random subspace method). Then, once you have many individual classifiers, determine a mechanism to join the results (simple method: each predictor votes on each point, then tally up the votes) and make a final prediction using the new classifier.</p>
<h4><strong>GitHub Ensembles</strong></h4>
<p>Perhaps the most illustrative example of this simple technique is <a href="http://github.com/jdrowell/ghcontest-crowdsourced/tree/master">John Rowell’s entry</a> into the GitHub contest, which is best described by a snippet right from the source:</p>
<p><a href="javascript:showme('9914_1');"> <b>> ensemble.rb</b></a>
<div style=" background:white;" id=9914_1>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'nokogiri'</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">'open-uri'</span>
&nbsp;
<span style="color:#9966CC; font-weight:bold;">class</span> Crowdsource
  <span style="color:#9966CC; font-weight:bold;">def</span> initialize
    load_leaderboard  <span style="color:#008000; font-style:italic;"># scrape github contest leaders</span>
    parse_leaders     <span style="color:#008000; font-style:italic;"># find their top performing results</span>
    fetch_results     <span style="color:#008000; font-style:italic;"># download best results</span>
    cleanup_leaders   <span style="color:#008000; font-style:italic;"># cleanup missing or incorrect data</span>
    crunchit          <span style="color:#008000; font-style:italic;"># build an ensemble</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#008000; font-style:italic;">#...</span>
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
Crowdsource.<span style="color:#9900CC;">new</span>
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.github.com/jdrowell/ghcontest-crowdsourced/tree/master/.git'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/github.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.github.com/jdrowell/ghcontest-crowdsourced/tree/master/.git'>ghcontest-crowdsourced.git (John Rowell's ensemble predictor)</a>
							</h4><p>Downloads: 395 File Size: 0.0 KB </p>
						</div> </p>
<p>Because all the entries in the GitHub contest are in the public domain (actually, a few people caught on and started deleting their repos immediately after their submissions because of this technique), we can download the best predictions of other users, construct an ensemble, and then submit our own set of results.</p>
<h4><strong>Collaborative, Collaborative Filtering: CCF!</strong></h4>
<p>While on first examination the ensemble technique may have a flavor of cheating, I think it can actually have a significant effect on how future challenges of this sort could be structured. Don't fight it, embrace it. Instead of focusing on individual effort or insight, it is not hard to imagine much more collaborative, collaborative-filtering competitions (CCF's). Some teams can focus on building great independent predictors, while others can focus on developing and improving the methods for constructing ensembles (a mashup, of sorts). In other words, collaborative filtering has the potential to become a lot more collaborative! And in the meantime, I wish the GitHub guys luck in trying to determine the winner.</p>
<p class="download">Update: GitHub <a href="http://github.com/blog/481-about-the-github-contest">removed most ensemble submissions</a> /  released the <a href="http://github.com/schacon/github-contest/tree/master">code to power the challenge</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=897t42Bxxy8:Thnky-5EsO8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=897t42Bxxy8:Thnky-5EsO8:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=897t42Bxxy8:Thnky-5EsO8:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=897t42Bxxy8:Thnky-5EsO8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=897t42Bxxy8:Thnky-5EsO8:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=897t42Bxxy8:Thnky-5EsO8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=897t42Bxxy8:Thnky-5EsO8:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=897t42Bxxy8:Thnky-5EsO8:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=897t42Bxxy8:Thnky-5EsO8:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/897t42Bxxy8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/09/01/collaborative-filtering-with-ensembles/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/09/01/collaborative-filtering-with-ensembles/</feedburner:origLink></item>
		<item>
		<title>Post-Javascript DOM with Aptana Jaxer</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/La-Hbi-FCJc/</link>
		<comments>http://www.igvita.com/2009/08/25/post-javascript-dom-with-aptana-jaxer/#comments</comments>
		<pubDate>Tue, 25 Aug 2009 17:42:17 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[ajax]]></category>

		<category><![CDATA[dom]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[jaxer]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=656</guid>
		<description><![CDATA[It is hard to imagine the web as we know it today without AJAX. Within just a couple of years, it has transformed how we think about web applications, how we build them, and the technology stack underneath. Prototype, JQuery, YUI and dozens of others frameworks let us build rich and interactive applications, while Javascript [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/posts/09/jaxer-server.png" style="margin-right: 1em;"/>It is hard to imagine the web as we know it today without <a href="http://en.wikipedia.org/wiki/Ajax_%28programming%29">AJAX</a>. Within just a couple of years, it has transformed how we think about web applications, how we build them, and the technology stack underneath. Prototype, JQuery, YUI and dozens of others frameworks let us build rich and interactive applications, while Javascript and rendering engines are progressing in <a href="http://ejohn.org/blog/javascript-performance-rundown/">leaps and bounds</a> when it comes to performance. Exciting stuff. However, this client-side functionality comes at a price. The AJAX testing experience is still painful (even with solutions like <a href="http://seleniumhq.org/">Selenium</a>, <a href="http://wiki.github.com/brynary/webrat">Webrat</a>, etc), and even more generally, working with, or even getting access to the post-Javascript DOM remains a challenge. Want to build a crawler which sees the page as the author intented? That's a tough problem.</p>
<h4><strong>Javascript is (Almost) Everywhere</strong></h4>
<p><img align="left" src="http://www.igvita.com/posts/09/google-spider.png" style="margin-right: 1em;"/>Like it or not, disabling JavaScript in your browser today is likely to render a large section of the web as completely unusable, and that is unlikely to change anytime soon. For that reason, over the years there have been many recurring rumors of the Google bot rendering Javascript when it came to your page. Google <a href="http://googlewebmastercentral.blogspot.com/2007/11/spiders-view-of-web-20.html">denies this</a>, but there are also theories that the V8 engine is a direct result of such initiatives - which, I think, is not completely unfounded.</p>
<p>In either case, if you ever attempted this yourself, you quickly realized just how non-trivial this problem actually is. A typical spider just downloads the HTML of the page, it has not knowledge of the DOM, or Javascript. So, at the end of the day, what you need is a browser: the content must be downloaded, the HTML needs to be interpreted into a DOM model, and then finally handed off to the Javascript runtime for further processing. That's a lot of work.</p>
<h4><strong>Jaxer, the AJAX Server</strong></h4>
<p>Having attempted at this problem many times with <a href="http://en.wikipedia.org/wiki/Rhino_%28JavaScript_engine%29">Rhino</a>, <a href="http://en.wikipedia.org/wiki/SpiderMonkey_%28Javascript_engine%29">SpiderMonkey</a> and even raw Webkit bindings, I finally stumbled on <a href="http://jaxer.org/">Jaxer</a>. Developed at <a href="http://en.wikipedia.org/wiki/Aptana">Aptana</a>, it is based directly on <a href="https://developer.mozilla.org/en/Gecko">Gecko</a> (Firefox rendering engine), minus the graphical rendering, which means that the DOM is already there and Javascript is free of charge. I never bought into the server-side Javascript movement, but seeing Jaxer in this light definitely opened my eyes to a lot of opportunities. The installation is as easy as <a href="http://jaxer.org/guide/setup.php">unpacking the Apache runtime</a> and then proxying your requests through the server. For example, to get the full post-Javascript DOM of any web-page, just drop this template into your Jaxer directory:</p>
<p><a href="javascript:showme('1556_1');"> <b>> post-js.html</b></a>
<div style=" background:white;" id=1556_1>
<pre class="html4strict"><span style="color: #00bbdd;">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;</span>
<span style="color: #00bbdd;">  &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&gt;</span>
<span style="color: #009900;"><a href="http://december.com/html/4/element/html.html"><span style="color: #000000; font-weight: bold;">&lt;html</span></a> xmlns=<span style="color: #ff0000;">&quot;http://www.w3.org/1999/xhtml&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
  <span style="color: #009900;"><a href="http://december.com/html/4/element/head.html"><span style="color: #000000; font-weight: bold;">&lt;head&gt;</span></a></span>
    <span style="color: #009900;"><a href="http://december.com/html/4/element/meta.html"><span style="color: #000000; font-weight: bold;">&lt;meta</span></a> <span style="color: #000066;">http-equiv</span>=<span style="color: #ff0000;">&quot;Content-Type&quot;</span> <span style="color: #000066;">content</span>=<span style="color: #ff0000;">&quot;text/html; charset=utf-8&quot;</span> /<span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><a href="http://december.com/html/4/element/title.html"><span style="color: #000000; font-weight: bold;">&lt;title&gt;</span></a></span>Post JavaScript DOM<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/title&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/head&gt;</span></span>
  <span style="color: #009900;"><a href="http://december.com/html/4/element/body.html"><span style="color: #000000; font-weight: bold;">&lt;body&gt;</span></a></span>
&nbsp;
    <span style="color: #009900;"><span style="color: #808080; font-style: italic;">&lt;!-- Render post-JS DOM: http://jaxer/post-js.html?www.google.com --&gt;</span></span>
    <span style="color: #009900;"><a href="http://december.com/html/4/element/script.html"><span style="color: #000000; font-weight: bold;">&lt;script</span></a> runat=<span style="color: #ff0000;">'server'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
       var url_query = Jaxer.request.queryString;
       var sb = new Jaxer.Sandbox(url_query);
       Jaxer.response.setContents(sb.toHTML());
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/script&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/body&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/html&gt;</span></span>
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/postjs.txt'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/txt.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/postjs.txt'>post-js.html (Proxy through Jaxer for post-JS DOM)</a>
							</h4><p>Downloads: 320 File Size: 0.6 KB </p>
						</div> </p>
<h4><strong>Accessing the Server-Side DOM </strong></h4>
<p>The first thing you'll notice is just how slow most of the pages will come back when proxied through Jaxer. However, this has nothing to do with Jaxer and everything to do with the dozens of Javascript files and slow remote calls our browsers have to do on our behalf. Their asynchronous nature just happens to mask the abysmal performance of most pages. Which, incidentally, is also likely the reason why our search engines today do not look at the post-JS DOM.</p>
<p>If you haven't already, take a look at the <a href="http://jaxer.org/tutorials/">Jaxer tutorials</a>. You have full access to the DOM on the server, which means that you can use JQuery to alter the response, connect to a database, test your Javascript on the fly, etc. Having said that, I'm still secretly hoping that one day our curl command client will just have a flag to return the final post-Javascript DOM.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=La-Hbi-FCJc:7AvQyTxbybA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=La-Hbi-FCJc:7AvQyTxbybA:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=La-Hbi-FCJc:7AvQyTxbybA:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=La-Hbi-FCJc:7AvQyTxbybA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=La-Hbi-FCJc:7AvQyTxbybA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=La-Hbi-FCJc:7AvQyTxbybA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=La-Hbi-FCJc:7AvQyTxbybA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=La-Hbi-FCJc:7AvQyTxbybA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=La-Hbi-FCJc:7AvQyTxbybA:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/La-Hbi-FCJc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/08/25/post-javascript-dom-with-aptana-jaxer/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/08/25/post-javascript-dom-with-aptana-jaxer/</feedburner:origLink></item>
		<item>
		<title>Smart Clients: ReverseHTTP &amp; WebSockets</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/IPlAq3g91qc/</link>
		<comments>http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/#comments</comments>
		<pubDate>Tue, 18 Aug 2009 15:30:01 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[reversehttp]]></category>

		<category><![CDATA[websocket]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=604</guid>
		<description><![CDATA[Polling architectures, as pervasive as they are today, did not come about due to their efficiency. Whether you are maintaining a popular endpoint (Twitter), or trying to get near real-time news (RSS), neither side benefits from this architecture. Over the years we've built a number of crutches in the form of Cache headers, ETags, accelerators, [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" style="margin-right: 1em;" src="http://www.igvita.com/posts/09/polling.png"/>Polling architectures, as pervasive as they are today, did not come about due to their efficiency. Whether you are maintaining a popular endpoint (Twitter), or trying to get near real-time news (RSS), neither side benefits from this architecture. Over the years we've built a number of crutches in the form of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">Cache headers</a>, <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19">ETags</a>, <a href="http://www.igvita.com/2009/08/05/masking-latency-failures-with-squid/">accelerators</a>, but none have fundamentally solved the problem - because the client remains 'dumb' the burden is still always on the server. For that reason, it's worth paying attention to some of the technologies which are seeking to reverse this trend: <a href="http://www.reversehttp.net/">ReverseHTTP</a> & <a href="http://dev.w3.org/html5/websockets/">WebSockets</a>.</p>
<h4><strong>Smart(er) Clients: Pushing Complexity to the Edges</strong></h4>
<p>Our web-browsers today are, for the most part, passive consumers. This model has worked wonderfully up until now, but as thought experiment, think about the implications on your architecture if the browsers were smarter. For example, what if the browser also contained a web server? For one, this would mean that the browser and the web-server would be peers: they could talk via HTTP, use <a href="http://oauth.net/">OAuth</a>, <a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/">Webhooks</a>, etc!</p>
<p><img align="left" style="margin-right: 1em;" src="http://www.igvita.com/posts/09/ff-chrome-opera.png"/> Brushing <a href="http://en.wikipedia.org/wiki/Network_address_translation">NAT</a> and firewall complications aside for a few seconds, this would mean that any browser could register itself via a webhook, and the server would simply propagate the updates to each client whenever new data is available (aka, <a href="http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub">PubSubHubbub</a> with the client). Fast and efficient.</p>
<p> So how do you go about running a web-server in your browser? It is possible in theory, but the support is lacking. Firefox has a <a href="https://developer.mozilla.org/en/HTTP_server_for_unit_tests">built in Javascript webserver for testing</a>, but it requires XPCOM privileges; there is some talk on the <a href="http://groups.google.com/group/mozilla-labs-jetpack/browse_thread/thread/23bec93cda843510?fwc=1">Jetpack mailing lists</a> about supporting this feature, but nothing conclusive; <a href="http://tinyclouds.org/node/">node.js</a> is an evented V8 powered web-server, but it can't run within Chrome. The closest you will get without modify the core of your browser is <a href="https://addons.mozilla.org/en-US/firefox/addon/3002">Firefox POW</a> (Plain Old Webserver) extension, but we need something much more flexible.</p>
<h4><strong>Hybrid Clients: ReverseHTTP & Supernodes</strong></h4>
<p>The first time you read the proposed <a href="http://www.reversehttp.net/">ReverseHTTP</a>, or equivalent spec, it will undoubtedly feel like a crazy idea. Having said that, it works. Because most clients are hidden behind personal / corporate firewalls or NAT's, it renders them unreachable for a raw client-side HTTP push model. ReverseHTTP proposes a hybrid solution: another web-server in middle acts a proxy, and the client maintains a persistent connection to it. Whenever a request comes to the proxy, it is relayed to the client via the persistent connection, where the response is determined by the browser and finally relayed back to the originating source. In effect, you are running a webserver in your browser. <a href="http://www.reversehttp.net/demos/tinydemo.html">Give it a try</a>, and if you’re curious, check out the <a href="http://github.com/tonyg/reversehttp/tree/master">server code</a> as well.</p>
<p align="center"><img src="http://www.igvita.com/posts/09/reversehttp.png"/></p>
<p>Unfortunately, this is still a half-hearted solution because it introduces yet another layer of infrastructure into the equation, but it also has its benefits. First, the scalability complexity is propagated down to the edges – a client, a hosting provider, or even an ISP can maintain such proxy nodes for their users. In fact, that is exactly what <a href="http://unite.opera.com/">Opera Unite</a> is all about. Blur your eyes to cut through the marketing, and you'll see that, in fact, the "revolution" is the ability for a browser to act as a server, via the Opera Unite proxy service. Sounds crazy? Microsoft's <a href="http://technet.microsoft.com/en-us/library/bb457011.aspx">Teredo service</a> (tunneling IPv6 traffic over IPv4) has been providing this very service for free for several years now to all Windows users. Last but not least, this is also the same principle that powers your favorite P2P client (Skype, Kazaa, etc). It is not as crazy as it seems.</p>
<h4><strong>Bi-directional Communication With WebSockets API</strong></h4>
<p>Of course, we also can't forget Comet, BOSH, or many other attempts at the same problem. Personally, I've been holding off for a simpler solution. All I want is a socket – Flash has it, why can't the web browser too? Thankfully, HTML 5 has an answer: <a href="http://dev.w3.org/html5/websockets/">WebSocket API</a>.</p>
<p><a href="javascript:showme('4762_1');"> <b>> websocket.js</b></a>
<div style=" background:white;" id=4762_1>
<pre class="javascript"><span style="color: #009900; font-style: italic;">// open a websocket</span>
<span style="color: #003366; font-weight: bold;">var</span> conn = <span style="color: #003366; font-weight: bold;">new</span> WebSocket<span style="color: #66cc66;">&#40;</span><span style="color: #3366CC;">&quot;ws://yourwebservice.com/service&quot;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;
<span style="color: #009900; font-style: italic;">// act on incoming data</span>
conn.<span style="color: #006600;">onopen</span>  = <span style="color: #003366; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span>e<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> ... <span style="color: #66cc66;">&#125;</span>
conn.<span style="color: #006600;">onread</span>  = <span style="color: #003366; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span>e<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> ... <span style="color: #66cc66;">&#125;</span>
conn.<span style="color: #006600;">onclose</span> = <span style="color: #003366; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span>e<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span> ... <span style="color: #66cc66;">&#125;</span>
&nbsp;
<span style="color: #009900; font-style: italic;">// push messages back to server</span>
conn.<span style="color: #006600;">send</span><span style="color: #66cc66;">&#40;</span><span style="color: #3366CC;">&quot;Bi-directional!&quot;</span><span style="color: #66cc66;">&#41;</span>;
&nbsp;</pre>
</div>
<p><img align="left" style="margin-right: 1em;" src="http://www.igvita.com/posts/09/w3c.png"/>As more and more browsers start adopting <a href="http://www.w3.org/TR/html5/">HTML 5</a> features I'm hoping to see WebSockets become a reality within the next couple of years. Full bi-directional data exchange between the client and server, without a need for a third party in between – that is what Opera Unite announcement should have been. It still means maintaining persistent connections between client and server, which is arguably not as elegant as registering your browser session through a webhook, but there is a need for both solutions. One is great for intermittent updates, the other (WebSockets) are the right answer for high-velocity streams.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=IPlAq3g91qc:BqRSqWk54Y0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=IPlAq3g91qc:BqRSqWk54Y0:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=IPlAq3g91qc:BqRSqWk54Y0:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=IPlAq3g91qc:BqRSqWk54Y0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=IPlAq3g91qc:BqRSqWk54Y0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=IPlAq3g91qc:BqRSqWk54Y0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=IPlAq3g91qc:BqRSqWk54Y0:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=IPlAq3g91qc:BqRSqWk54Y0:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=IPlAq3g91qc:BqRSqWk54Y0:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/IPlAq3g91qc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/08/18/smart-clients-reversehttp-websockets/</feedburner:origLink></item>
		<item>
		<title>Masking Latency &amp; Failures with Squid</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/tLPbzSKFQxw/</link>
		<comments>http://www.igvita.com/2009/08/05/masking-latency-failures-with-squid/#comments</comments>
		<pubDate>Wed, 05 Aug 2009 16:27:22 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[cache]]></category>

		<category><![CDATA[squid]]></category>

		<category><![CDATA[varnish]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=611</guid>
		<description><![CDATA[Latency matters. Watching the interviews with the Yahoo developers on the launch of their new homepage, I was once again reminded about the great amount of effort they have put in to make it all work. In fact, I've written about Mark Nottingham's proposals for stale-while-revalidate and stale-if-error in the past (with a homebaked implentation), [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/posts/09/yahoo-squid.png" style="margin-right: 1em;"/>Latency matters. Watching the interviews with the <a href="http://developer.yahoo.net/blog/archives/2009/07/yahoo_homepage.html">Yahoo developers</a> on the launch of their new homepage, I was once again reminded about the great amount of effort they have put in to make it all work. In fact, I've <a href="http://www.igvita.com/2008/10/07/asynchronous-http-cache-validations/">written</a> about <a href="http://www.mnot.net/blog/">Mark Nottingham's</a> proposals for <a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-while-revalidate-00.txt">stale-while-revalidate</a> and <a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-if-error-00.txt">stale-if-error</a> in the past (with a <a href="http://www.igvita.com/2008/10/07/asynchronous-http-cache-validations/">homebaked implentation</a>), but a more realistic deployment scenario is to use a caching server like Squid or Varnish. Let's take a look at how these extensions can help your specific deployment.</p>
<h4><strong>HTTP Caching Extensions: stale-*</strong></h4>
<p>Both <a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-while-revalidate-00.txt">stale-while-revalidate</a> and <a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-if-error-00.txt">stale-if-error</a> are the direct results of Mark Nottingham's work at Yahoo. It is clear that <a href="http://www.squid-cache.org/">Squid</a> plays a big role in the Y! infrastructure and these extensions are undoubtedly deployed in their data centers. <em>Stale-while-revalidate</em> addresses a simple problem: when a record becomes stale, instead of allowing the request to hit the application server, serve the stale data to the client and create an asynchronous request to update the cache (<a href="http://www.igvita.com/2008/10/07/asynchronous-http-cache-validations/">read more</a> & <a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-while-revalidate-00.txt">spec</a>). The benefit? All of your customers see consistent performance because the data is always served out of the cache (think RSS feeds, search results, etc).</p>
<p align="center"><img src="http://www.igvita.com/posts/09/stale-while-revalidate-small.png"/></p>
<p>The second extension (<a href="http://www.mnot.net/drafts/draft-nottingham-http-stale-if-error-00.txt">stale-if-error</a>) can help you mask downtime by returning stale data while your ops team resolves the problem. How does it work? You specify a cache-control header (<em>Cache-Control: max-age=600, stale-if-error=1200</em>) which indicates for how long, after the record is expired the cache server can serve this data if the application server is down. Of course, nothing stops you from setting this to a high value like a day or longer! This way, if your server goes down, at least the clients won't timeout on their requests while you're resolving the problem!</p>
<h4><strong>Setting up Squid as a Reverse Proxy</strong></h4>
<p><img align="left" src="http://www.igvita.com/posts/09/squid-chain.png" style="margin-right: 1em;"/><a href="http://varnish.projects.linpro.no/">Varnish</a> and <a href="http://www.squid-cache.org/">Squid</a> are the two top choices when it comes to caching reverse proxies. Varnish has been getting a lot momentum but unfortunately the features we're looking for are still in the works. There is an <a href="http://varnish.projects.linpro.no/ticket/369">open ticket for stale-if-error</a> support, and stale-if-revalidate implementation unfortunately does not provide the full asynchronous refresh model. For that reason, we're going to use <a href="http://www.squid-cache.org/Versions/v2/2.7/">Squid 2.7</a> (note: Squid 3.0 is a full rewrite of the 2.x branch, and while stale-* <a href="http://wiki.squid-cache.org/Features/StaleIfError">patches</a> <a href="http://wiki.squid-cache.org/Features/StaleWhileRevalidate">exist</a>, they haven't officially made it into the tree). A minimal Squid config (if there is such a thing) to get us up and running:</p>
<p><a href="javascript:showme('5896_1');"> <b>> squid.conf</b></a>
<div style=" background:white;" id=5896_1>
<pre class="ruby"><span style="color:#008000; font-style:italic;"># http_port public_ip:port accel defaultsite= default hostname, if not provided</span>
http_port <span style="color:#006666;">0.0</span><span style="color:#006666;">.0</span><span style="color:#006666;">.0</span>:<span style="color:#006666;">80</span> accel defaultsite=yourdomain.<span style="color:#9900CC;">com</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># IP and port of your main application server (or multiple)</span>
cache_peer <span style="color:#006666;">192.168</span><span style="color:#006666;">.0</span><span style="color:#006666;">.1</span> parent <span style="color:#006666;">80</span> <span style="color:#006666;">0</span> no-query originserver name=main
cache_peer_domain main yourdomain.<span style="color:#9900CC;">com</span>
&nbsp;
<span style="color:#008000; font-style:italic;"># Do not tell the world that which squid version we're running</span>
httpd_suppress_version_string on
&nbsp;
<span style="color:#008000; font-style:italic;"># Remove the Caching Control header for upstream servers</span>
header_access Cache-Control deny all
&nbsp;
<span style="color:#008000; font-style:italic;"># log all incoming traffic in Apache format</span>
logformat combined %&gt;a %ui %un <span style="color:#006600; font-weight:bold;">&#91;</span>%tl<span style="color:#006600; font-weight:bold;">&#93;</span> <span style="color:#996600;">&quot;%rm %ru HTTP/%rv&quot;</span> %Hs %&lt;st <span style="color:#996600;">&quot;%{Referer}&gt;h&quot;</span> <span style="color:#996600;">&quot;%{User-Agent}&gt;h&quot;</span> %Ss:%Sh
access_log /usr/local/squid/var/logs/squid.<span style="color:#9900CC;">log</span> combined all
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/squid.conf'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/downloads.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.igvita.com/downloads/squid.conf'>squid.conf (Full Squid 2.7 Reverse Accelerator config)</a>
							</h4><p>Downloads: 274 File Size: 167.9 KB </p>
						</div> </p>
<h4><strong>Connecting Squid and your Application</strong></h4>
<p>Once Squid is deployed as part of your request chain, taking advantage of its caching mechanism is a trivial matter, just add the "Cache-Control" header! A simple Rack application to make it all work:</p>
<p><a href="javascript:showme('5896_2');"> <b>> rack-stale.rb</b></a>
<div style=" background:white;" id=5896_2>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;rack&quot;</span>
&nbsp;
app = <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
  <span style="color:#CC0066; font-weight:bold;">p</span> <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#ff3333; font-weight:bold;">:new_request</span>, <span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">now</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
  headers = <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#008000; font-style:italic;"># cache for 10 seconds, serve stale for up to 20s, and for up to 30s on errors</span>
    <span style="color:#996600;">&quot;Cache-Control&quot;</span> =&gt; <span style="color:#996600;">&quot;max-age=10, stale-while-revalidate=10, stale-if-error=20&quot;</span>,
    <span style="color:#996600;">&quot;Last-Modified&quot;</span> =&gt; <span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">now</span>.<span style="color:#9900CC;">to_s</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">200</span>, headers, <span style="color:#996600;">&quot;Hello World @ #{Time.now}&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
<span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#6666ff; font-weight:bold;">Rack::Handler::Mongrel</span>.<span style="color:#9900CC;">run</span><span style="color:#006600; font-weight:bold;">&#40;</span>app, <span style="color:#006600; font-weight:bold;">&#123;</span>:Host =&gt; <span style="color:#996600;">&quot;127.0.0.1&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:Port</span> =&gt; <span style="color:#006666;">80</span><span style="color:#006600; font-weight:bold;">&#125;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
&nbsp;</pre>
</div>
<p>The only requirements are that we set a <em>Last-Modified</em> time, and then provide our preferred caching intervals. <em>Max-age</em> specifies the general lifetime of the object (TTL), <em>stale-while-revalidate</em> indicates for how long after max-age has expired can the server provide the stale data (total lifetime is max-age + stale-while-revalidate). Finally, <em>stale-if-error</em> indicates for how long the server can provide the stale data if the application server is down - it is not unusual to set this higher than stale-if-revalidate. That's it!</p>
<h4><strong>Masking Latency and Failures</strong></h4>
<p>Of course, these extensions do not excuse any of us from building fast and reliable web-services. In theory, we would have no need for these extensions, but as the saying goes: "in theory, theory and practice are the same, in practice, they are not." If you have data that can be served slightly stale (frankly, the majority of the data falls into this category), then Squid can make all the difference next time your server decides to take a break at 4AM.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=tLPbzSKFQxw:Urxe0jJcsko:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=tLPbzSKFQxw:Urxe0jJcsko:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=tLPbzSKFQxw:Urxe0jJcsko:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=tLPbzSKFQxw:Urxe0jJcsko:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=tLPbzSKFQxw:Urxe0jJcsko:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=tLPbzSKFQxw:Urxe0jJcsko:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=tLPbzSKFQxw:Urxe0jJcsko:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=tLPbzSKFQxw:Urxe0jJcsko:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=tLPbzSKFQxw:Urxe0jJcsko:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/tLPbzSKFQxw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/08/05/masking-latency-failures-with-squid/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/08/05/masking-latency-failures-with-squid/</feedburner:origLink></item>
		<item>
		<title>Extending Tokyo Cabinet DB with Lua</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/5iq57HwtKVM/</link>
		<comments>http://www.igvita.com/2009/07/13/extending-tokyo-cabinet-db-with-lua/#comments</comments>
		<pubDate>Mon, 13 Jul 2009 16:37:12 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Databases]]></category>

		<category><![CDATA[db]]></category>

		<category><![CDATA[lua]]></category>

		<category><![CDATA[tokyocabinet]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=583</guid>
		<description><![CDATA[Tokyo Cabinet is a trove of hidden of gems, the more you learn about it, the more you will appreciate the design and technical decisions behind it. By database standards it is a young project (started in 2007), but since it is a successor to the QDBM project developed by Hirabayashi-san (2000-2007), we could make [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" src="http://www.igvita.com/posts/09/tc.png" style="margin-right: 1em;"/>Tokyo Cabinet is a <a href="http://www.igvita.com/2009/02/13/tokyo-cabinet-beyond-key-value-store/">trove of hidden of gems</a>, the more you learn about it, the more you will appreciate the design and technical decisions behind it. By database standards it is a young project (started in 2007), but since it is a successor to the <a href="http://qdbm.sourceforge.net/">QDBM</a> project developed by Hirabayashi-san (2000-2007), we could make the argument that it has been, in fact, nine years in the making.</p>
<p>Best of all, the rewrite allowed the project to shed its past baggage and build on a more modern stack with a better perspective of the required features for a modern deployment (<a href="https://launchpad.net/drizzle">Drizzle</a> is another recent example from the MySQL camp). Case in point, <a href="http://tokyocabinet.sourceforge.net/tyrantdoc/#luaext">Tokyo Tyrant supports Lua scripting</a> on the server, allowing us to add arbitrary user defined functions (<a href="http://dev.mysql.com/doc/refman/5.1/en/plugin-api.html">UDF's in MySQL</a> parlance) and to extend the database itself!</p>
<h4><strong>Lowering the Barrier With Lua Scripting</strong></h4>
<p><img align="left" src="http://www.igvita.com/posts/09/lua.png" style="margin-right: 1em;"/>If you have played with Lego Mindstorms, or ever tried to script your phone or a gaming environment (<a href="http://www.wowwiki.com/Lua">WoW</a>), chances are you've worked with <a href="http://www.lua.org/">Lua</a>. Due to its extremely small footprint (~212kb for source, documentation, and examples), fast interpreter, portability and easy to understand syntax it has become the de-facto embedded scripting language for many applications. Explore the <a href="http://lua-users.org/wiki/TutorialDirectory">available tutorials</a>, or pick up "<a href="http://www.amazon.com/Programming-Lua-Second-Roberto-Ierusalimschy/dp/8590379825/ref=sr_1_1?ie=UTF8&s=books&qid=1247449280&sr=8-1">Programming Lua</a>" by the creator of the language for a more in depth look, it's a fun language!</p>
<p>So why is Lua scripting such an exciting feature for Tokyo Tyrant? Because it lowers the barrier for programming and extending the database by an order of magnitude! If you have ever worked with MySQL UDF functions, you'll definitely appreciate the ease and the speed of development. No need for mucking with internal API's, nothing to compile or link against, and execution is done within a stable and sandboxed environment. </p>
<h4><strong>Extending Tokyo Tyrant with Lua</strong></h4>
<p>Anytime you bootup a Tokyo Tyrant server you can tell it to load arbitrary Lua code alongside which will then be evaluated at runtime if the client requests it. From there, the developer of the extension has full access to the incoming request and the underlying Tokyo Cabinet database, allowing us to inject arbitrary functionality. To get a flavor for the workflow, take a look at some of the examples in the <a href="http://www.slideshare.net/igrigorik/lean-mean-tokyo-cabinet-recipes-with-lua">following slides</a>:</p>
<div style="width:600px;text-align:left" id="__ss_1708861"><object style="margin:0px" width="600" height="490"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=tokyo-recipes-090711100331-phpapp02&rel=0&stripped_title=lean-mean-tokyo-cabinet-recipes-with-lua" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=tokyo-recipes-090711100331-phpapp02&rel=0&stripped_title=lean-mean-tokyo-cabinet-recipes-with-lua" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="600" height="490"></embed></object></div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.github.com/igrigorik/tokyo-recipes/tree/master/.git'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/github.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.github.com/igrigorik/tokyo-recipes/tree/master/.git'>tokyo-recipes.git (Lua Extensions for Tokyo Tyrant)</a>
							</h4><p>Downloads: 503 File Size: 0.0 KB </p>
						</div> </p>
<p>Because the Lua scripting interface is relatively new, the number of available extensions and documentation is not very large. For that reason, I've started the <a href="http://github.com/igrigorik/tokyo-recipes/tree/master">tokyo-recipes repo</a> on GitHub, in which I've aggregated some of the available extensions, and added extra documentation and examples to help grease the wheels: <a href="http://github.com/igrigorik/tokyo-recipes/tree/1fdaf47a1fcf7a77e721379b1efa9cc0a510f72f/expire">TTL functionality</a> (ala memcached), <a href="http://github.com/igrigorik/tokyo-recipes/tree/1fdaf47a1fcf7a77e721379b1efa9cc0a510f72f/sets">working with Sets</a> (ala Redis), <a href="http://github.com/igrigorik/tokyo-recipes/tree/1fdaf47a1fcf7a77e721379b1efa9cc0a510f72f/session-trail">session timestamping</a> and a <a href="http://github.com/igrigorik/tokyo-recipes/tree/1fdaf47a1fcf7a77e721379b1efa9cc0a510f72f/map-reduce">wordcount map-reduce</a> example just to name a few. Give it a try, it is an extremely powerful feature!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=5iq57HwtKVM:Wu0wjjQMQrI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=5iq57HwtKVM:Wu0wjjQMQrI:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=5iq57HwtKVM:Wu0wjjQMQrI:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=5iq57HwtKVM:Wu0wjjQMQrI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=5iq57HwtKVM:Wu0wjjQMQrI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=5iq57HwtKVM:Wu0wjjQMQrI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=5iq57HwtKVM:Wu0wjjQMQrI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=5iq57HwtKVM:Wu0wjjQMQrI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=5iq57HwtKVM:Wu0wjjQMQrI:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/5iq57HwtKVM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/07/13/extending-tokyo-cabinet-db-with-lua/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/07/13/extending-tokyo-cabinet-db-with-lua/</feedburner:origLink></item>
		<item>
		<title>HTTP PubSub: Webhooks &amp; PubSubHubbub</title>
		<link>http://feedproxy.google.com/~r/igvita/~3/-zbWrPTcnHE/</link>
		<comments>http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 16:14:07 +0000</pubDate>
		<dc:creator>Ilya Grigorik</dc:creator>
		
		<category><![CDATA[Architecture]]></category>

		<category><![CDATA[Ruby]]></category>

		<category><![CDATA[pubsub]]></category>

		<category><![CDATA[pubsubhubbub]]></category>

		<guid isPermaLink="false">http://www.igvita.com/?p=514</guid>
		<description><![CDATA[With all the recent buzz about real-time web, surely this is the year XMPP/AMQP Publish-Subscribe (PubSub) makes it to the big leagues! Or maybe not. Ejabberd (XMPP), RabbitMQ (AMQP) and other pubsub server implementations have come a long way but they remain cumbersome to setup and maintain, and perhaps more importantly, the clients require special [...]]]></description>
			<content:encoded><![CDATA[<p><img align="left" style="margin-right: 1em;" src="http://www.igvita.com/posts/09/webhooks.png"/>With all the recent buzz about real-time web, surely this is the year <a href="http://xmpp.org/extensions/xep-0060.html">XMPP</a>/<a href="http://jira.amqp.org/confluence/display/AMQP/Advanced+Message+Queuing+Protocol">AMQP</a> Publish-Subscribe (PubSub) makes it to the big leagues! Or maybe not. <a href="http://www.ejabberd.im/">Ejabberd</a> (XMPP), <a href="http://www.rabbitmq.com/">RabbitMQ</a> (AMQP) and other pubsub server implementations have come a long way but they remain cumbersome to setup and maintain, and perhaps more importantly, the clients require special libraries and a steep learning curve. That is not to say that either XMPP or AMQP are doomed for failure, in fact, they will continue to thrive, but there is a great case for a simplified PubSub implementation to cover the ad-hoc cases where a dedicated TCP channel might be an overkill: enter Webhooks.</p>
<p>The best part about <a href="http://www.webhooks.org/">Webhooks</a> is that most of us are already familiar with them: callbacks over HTTP. Pioneered by PayPal and Subversion as a way to send real-time notifications to the client, they have found their way into many dozens of products we all use every day. Need pre or post commit hooks for your SVN or Git repository? Both GitHub and SVN support HTTP callbacks. Need a payment alert from PayPal, or an alert when a wiki page is modified? There are webhooks for that too. This simple mechanism allows us to build web services that work together via a simple and ubiquitous protocol we can all understand: HTTP!</p>
<h4><strong>Working with Webhooks</strong></h4>
<p><img align="left" style="margin-right: 1em;" src="http://www.igvita.com/posts/09/webhook-callback.png"/>A good way to think about webhooks is as Unix pipes for the web. While PubSub is a great application of the technology, Webhooks allow bidirectional communication, which opens up our possibilities to: publishing notifications, chaining web services to perform complex actions, or allowing external plugins to enter the workflow. All of this functionality is accomplished via simple HTTP queries (both POST and GET), which means no special libraries or servers to make it all happen. In fact, building your own Webhooks powered PubSub server takes <a href="http://github.com/jcapote/watercoolr/blob/125707e211b0a3cb6462ea405bb77aefb1618f8b/watercoolr.rb">less than a hundred lines</a> with the help of Sinatra, as demonstrated by the Julio Capote and his <a href="http://github.com/jcapote/watercoolr/tree/master">watercoolr</a> project on GitHub. To see it in action, we can use <a href="http://www.postbin.org/">PostBin</a> as our mock recipient:</p>
<blockquote><p>
[ilya@igvita ~]# ruby postbin.rb <a href="http://www.postbin.org/1987c4m">http://www.postbin.org/1987c4m</a><br />
creating channel...<br />
adding subscriber to channel sej7u7<br />
<em>{"status":"OK"}</em><br />
Post message: Hello Webhooks PubSub!<br />
<em>{"status":"OK"}</em>
</p></blockquote>
<p>In this scenario, our local <a href="http://watercoolr.nuklei.com/">watercoolr service</a> acts as a PubSub server, accepting requests to create custom channels, maintaining a subscriber list, and pushing out notifications when an update arrives at the channel. Best of all, interacting with the server is as simple as writing a RESTful client:</p>
<p><a href="javascript:showme('8200_1');"> <b>> postbin.rb</b></a>
<div style=" background:white;" id=8200_1>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;rubygems&quot;</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;rest_client&quot;</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;json&quot;</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;creating channel...&quot;</span>
resp = RestClient.<span style="color:#9900CC;">post</span> <span style="color:#996600;">&quot;http://localhost:4567/channels&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:data</span> =&gt; <span style="color:#996600;">&quot;&quot;</span>
id = JSON.<span style="color:#9900CC;">parse</span><span style="color:#006600; font-weight:bold;">&#40;</span>resp<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;id&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;adding subscriber to channel #{id}&quot;</span>
resp = RestClient.<span style="color:#9900CC;">post</span> <span style="color:#996600;">&quot;http://localhost:4567/subscribers&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:data</span> =&gt; <span style="color:#006600; font-weight:bold;">&#123;</span>:channel =&gt; id, <span style="color:#ff3333; font-weight:bold;">:url</span> =&gt; ARGV<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#006666;">0</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#125;</span>.<span style="color:#9900CC;">to_json</span>
&nbsp;
<span style="color:#CC0066; font-weight:bold;">loop</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#CC0066; font-weight:bold;">print</span> <span style="color:#996600;">&quot;Post message: &quot;</span>
  msg = STDIN.<span style="color:#CC0066; font-weight:bold;">gets</span>.<span style="color:#CC0066; font-weight:bold;">chomp</span>
&nbsp;
  resp = RestClient.<span style="color:#9900CC;">post</span> <span style="color:#996600;">&quot;http://localhost:4567/messages&quot;</span>, <span style="color:#ff3333; font-weight:bold;">:data</span> =&gt; <span style="color:#006600; font-weight:bold;">&#123;</span>:channel =&gt; id, <span style="color:#ff3333; font-weight:bold;">:message</span> =&gt; msg<span style="color:#006600; font-weight:bold;">&#125;</span>.<span style="color:#9900CC;">to_json</span>
  <span style="color:#CC0066; font-weight:bold;">puts</span> resp
<span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;</pre>
</div>
<h4><strong>PubSubHubbub Spec: Auth, Discovery & Protocol Implementation</strong></h4>
<p>Brad Fitzpatrick of Livejournal fame and Brett Slatkin, both of whom are currently at Google <a href="http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.1.html">drafted a PubSubHubbub spec</a> for a "simple web-scale pubsub protocol" which preserves the spirit of HTTP webhooks but adds a much needed layer of basic security, discovery, and coherent protocol definition:</p>
<blockquote><p>
We offer this spec in hopes that it fills a need or at least advances the state of the discussion in the pubsub space. Polling sucks. We think a decentralized pubsub layer is a fundamental, missing layer in the Internet architecture today and its existence, more than just enabling the obvious lower latency feed readers, would enable many cool applications, most of which we can't even imagine. But we're looking forward to decentralized social networking.
</p></blockquote>
<p><iframe src='http://docs.google.com/EmbedSlideshow?docid=ajd8t6gk4mh2_34dvbpchfs&amp;size=s' frameborder='0' width='555' height='340'></iframe></p>
<p>The <a href="http://code.google.com/p/pubsubhubbub/">google code project</a> also provides an AppEngine implementation with a <a href="http://pubsubhubbub.appspot.com/publish">public hub</a>, which we can test via an asynchronous (EventMachine based) PubSubHubbub Ruby library:</p>
<p><a href="javascript:showme('8200_2');"> <b>> pubsub-gae.rb</b></a>
<div style=" background:white;" id=8200_2>
<pre class="ruby"><span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;rubygems&quot;</span>
<span style="color:#CC0066; font-weight:bold;">require</span> <span style="color:#996600;">&quot;pubsubhubbub&quot;</span>
&nbsp;
EventMachine.<span style="color:#9900CC;">run</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
  <span style="color:#008000; font-style:italic;"># publish single URL</span>
  pub = <span style="color:#6666ff; font-weight:bold;">EventMachine::PubSubHubbub</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'http://pubsubhubbub.appspot.com/publish'</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">publish</span> <span style="color:#996600;">&quot;http://www.test.com/&quot;</span>
  pub.<span style="color:#9900CC;">callback</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Successfully notified hub.&quot;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
  pub.<span style="color:#9900CC;">errback</span>  <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Uh oh, something broke: #{pub.response}&quot;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># publish multiple URL's to hub</span>
  feeds = <span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#996600;">&quot;http://www.test.com&quot;</span>, <span style="color:#996600;">&quot;http://www.test.com/2&quot;</span><span style="color:#006600; font-weight:bold;">&#93;</span>
  pub = <span style="color:#6666ff; font-weight:bold;">EventMachine::PubSubHubbub</span>.<span style="color:#9900CC;">new</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'http://pubsubhubbub.appspot.com/publish'</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">publish</span> feeds
&nbsp;
  pub.<span style="color:#9900CC;">callback</span> <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Successfully notified hub.&quot;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
  pub.<span style="color:#9900CC;">errback</span>  <span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#CC0066; font-weight:bold;">puts</span> <span style="color:#996600;">&quot;Uh oh, something broke: #{pub.response}&quot;</span> <span style="color:#006600; font-weight:bold;">&#125;</span>
<span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;</pre>
</div>
<p><div class='download-link'>
							<a href='http://www.igvita.com/download.php?file=http://www.github.com/igrigorik/PubSubHubbub/tree/master/.git'><img alt='Download' class='leftalign' src='http://www.igvita.com/wp-content/plugins/dBeautifier/icons/github.png' /></a>
							<h4>
								<a href='http://www.igvita.com/download.php?file=http://www.github.com/igrigorik/PubSubHubbub/tree/master/.git'>PubSubHubbub.git (Asynchronous PubSubHubbub Ruby Client)</a>
							</h4><p>Downloads: 503 File Size: 0.0 KB </p>
						</div> </p>
<h4><strong>Webhooks & PubSubHubbub</strong></h4>
<p>Of course, neither Webhooks nor PubSubHubbub are the answer to every problem. Both XMPP and AMQP will continue to exist alongside, but chances are, will take on the brunt of high-velocity feeds while Webhooks can happily power the remaining 90% of the simpler use-cases. In fact, there is already a <a href="http://github.com/tonyg/rabbithub/tree/master">RabbitMQ PubSubHubbub hub server implemenation</a> in the works! For further reading, flip through Jeff Lindsay's <a href="http://www.slideshare.net/progrium/web-hooks-and-the-programmable-world-of-tomorrow-presentation">presentations</a> on <a href="http://blog.webhooks.org/2009/04/23/slides-from-pivotal-labs-talk/">webhooks</a>, and if you are interested in PubSubHubbub check out <a href="http://www.veodia.com/player.php?vid=fCNU1qQ1oSs">this presentation</a> by Brad Fitzpatrick and Brett Slatkin.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/igvita?a=-zbWrPTcnHE:W_-CvtdNiIM:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/igvita?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=-zbWrPTcnHE:W_-CvtdNiIM:D7DqB2pKExk"><img src="http://feeds.feedburner.com/~ff/igvita?i=-zbWrPTcnHE:W_-CvtdNiIM:D7DqB2pKExk" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=-zbWrPTcnHE:W_-CvtdNiIM:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/igvita?i=-zbWrPTcnHE:W_-CvtdNiIM:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=-zbWrPTcnHE:W_-CvtdNiIM:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/igvita?i=-zbWrPTcnHE:W_-CvtdNiIM:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/igvita?a=-zbWrPTcnHE:W_-CvtdNiIM:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/igvita?i=-zbWrPTcnHE:W_-CvtdNiIM:gIN9vFwOqvQ" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/igvita/~4/-zbWrPTcnHE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.igvita.com/2009/06/29/http-pubsub-webhooks-pubsubhubbub/</feedburner:origLink></item>
	</channel>
</rss>
