<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kenneth&#039;s Picnic &#187; Work</title>
	<atom:link href="http://kenneth.kufluk.com/blog/blog/work/feed/" rel="self" type="application/rss+xml" />
	<link>http://kenneth.kufluk.com/blog</link>
	<description>Welcome to Kenneth&#039;s Picnic.  Put your feet up and admire the stars over London.</description>
	<lastBuildDate>Sun, 29 Aug 2010 20:08:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Building McLaren.com – Part 6:  Monitoring</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-6-monitoring/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-6-monitoring/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 16:17:52 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[mclaren]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=646</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. Part six, the last part, covers the monitoring tools. Of course, we&#8217;re using the standard Google Analytics monitoring, which is great at delivering summary stats for past events (or even earlier in the day if [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>Part six, the last part, covers the monitoring tools.<span id="more-646"></span></p>
<p>Of course, we&#8217;re using the standard Google Analytics monitoring, which is great at delivering summary stats for past events (or even earlier in the day if you go and move the dates to include today).  I&#8217;ve implemented the newer asynchronous calls of course.</p>
<p>For <strong>real-time</strong> monitoring of stats, we&#8217;re using two tools.  One, we merge the subscriber counts returned from nginx to show the number of viewers during the race.</p>
<p>Second, we use <a href="http://chartbeat.com">ChartBeat</a>.  It is <em>awesome</em>.<br />
<img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/04/Screen-shot-2010-04-01-at-10.40.00-300x234.png" alt="" title="chartbeat - great realtime stats" width="300" height="234" class="alignnone size-medium wp-image-647" /></p>
<p>There&#8217;s a limited amount I can show here, since the dashboard screen is quite interactive.  The stats show above are for a random weekday morning just before Easter, and so are quite low.</p>
<p>Click here to view <a href="http://chartbeat.com/dashboard/?url=kenneth.kufluk.com&#038;k=29a30ae6190da31dfe34743eb1c5a651">live stats for this site</a> (though it&#8217;s probably only you watching right now).</p>
<p>While our visitors are screen-switching between our website and the TV footage, we&#8217;re also toggling to ChartBeat.  It&#8217;s been a great help to be able to watch the subscriber counts, mix in the twitter feedback, and see exactly where everyone is.</p>
<p>However, we found that while we can see visitors watching for up to two hours on ChartBeat, our &#8220;average time on site&#8221; recorded for Google Analytics was tiny &#8211; around two minutes!</p>
<h2>Accounting for the Google Analytics &#8220;Time on Site&#8221; discrepancy</h2>
<p>Checking through GA&#8217;s documentation reveals the reason for the difference.</p>
<blockquote><p>In order to capture the length of a visit, Google Analytics tracks the elapsed time between pageviews. The last page of a visit will not be recorded (as there is no subsequent pageview).</p></blockquote>
<p><a href="http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&#038;answer=57044">http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&#038;answer=57044</a></p>
<p>So if you have a page which is viewed for a significant period of time, and is then closed without further interaction, your visit durations can be seriously compromised.</p>
<p>Our solution is to use Event Tracking to log an event every minute.</p>
<pre class="brush: jscript">
	// Ping google analytics every minute to show accurate figures for &quot;time on site&quot; and &quot;length of visit&quot;.
	// this can also decrease &quot;bounce rate&quot; figures.
	// one minute is the recommended interval
	var gaPing = setInterval(function() {
		//using the async tracking calls
		_gaq.push([&#039;_trackEvent&#039;, &#039;SomeSortOfCategory&#039;, &#039;SomeSortOfAction&#039;]);
	}, 60 * 1000);
</pre>
<p>Since implementing this code, we&#8217;ve had a lot less disparity between ChartBeat and GA figures, though there is still some (<10%).</p>
<p>The recommended view for Google Analytics for viewing something like a race, which lasts for just two hours, is to go to:<br />
Visitors &gt; Visitor Trending &gt; Time on Site<br />
Choose the <em>day</em> that you&#8217;re interested in.<br />
Then click the little &#8216;hour&#8217; icon in the top right (hadn&#8217;t noticed that had you?)<br />
<img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/04/Screen-shot-2010-04-01-at-11.05.06.png" alt="" title="clock icon" width="162" height="46" class="alignnone size-full wp-image-649" /></p>
<p>And we finally get some real stats.  Our average visit length is now well over an hour during the race, which is <em>huge</em>.<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/04/time-on-site.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/04/time-on-site-293x300.png" alt="" title="time-on-site" width="293" height="300" class="alignnone size-medium wp-image-650" /></a></p>
<h2>A warning on tag expiry</h2>
<p>Both ChartBeat and Google have an upper limit on views.  Google will record a maximum of <a href="http://code.google.com/apis/analytics/docs/tracking/eventTrackerGuide.html#implementationConsiderations">500 tags per user session</a>, which is fine is we keep our pings to a minute.  ChartBeat has a limit of two hours, which causes a noticeable false dropoff towards the end of our races.</p>
<h2>Wrapping up</h2>
<p>Ok, I think that&#8217;s all that needs to be shared in this series.  If you have any extra questions, let me know.  If you&#8217;re working on a large-scale realtime data server, and need me for some consulting/development <em>definitely</em> give me a call.  And if you think there&#8217;s a better way to do all of this, let me know as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-6-monitoring/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building McLaren.com – Part 5: Serving from the Cloud</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-5-serving-from-the-cloud/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-5-serving-from-the-cloud/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 16:14:43 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[mclaren]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=633</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. I&#8217;ll be writing up what we&#8217;ve done here in several parts. Sign up for my RSS feed to keep updated. Part five covers the setting up of broadcast servers using Amazon EC2. If you&#8217;ve never [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>I&#8217;ll be writing up what we&#8217;ve done here in several parts.  Sign up for my <a href="http://feeds.feedburner.com/KennethKufluk">RSS feed</a> to keep updated.</p>
<p>Part five covers the setting up of broadcast servers using Amazon EC2.<span id="more-633"></span></p>
<p><strong>If you&#8217;ve never worked with Amazon EC2 before, please follow my &#8216;<a href="http://kenneth.kufluk.com/blog/2010/03/getting-started-with-amazon-ec2/">Getting Started</a>&#8216; guide first.</strong></p>
<p>In a previous part, we covered nginx setup set-by-step, so this time we&#8217;re going to look at creating a set of cloud servers with a single line of code.</p>
<h2><a name="setup_using_a_script" id="setup_using_a_script">Setup using a script</a></h2>
<p>Required:  ec2 command line tools</p>
<p>Amazon AMIs allow you to pass &#8216;user-data&#8217; into the image.  The alestic AMIs are configured to allow this data to be executed as soon as the machine starts.  For more on this, have a look at their article &#8220;<a href="http://alestic.com/2009/06/ec2-user-data-scripts">Automate EC2 Instance Setup with user-data Scripts</a>&#8220;.</p>
<p>I&#8217;m going to use a script called &#8220;<a href="http://kenneth.kufluk.com/pirata/install-nginx-mclaren">install-nginx-mclaren</a>&#8220;.  Save it to your working folder now, and execute the lines below from the same folder.<br />
This script was quite hard to write, due to various complications with the way the script gets passed in.  Line breaks are particularly difficult, and it&#8217;s always hard to set values deep in a configuration file using a script.
</p>
<pre class="code">ec2-run-instances -n 1 --key mclarenkey --user-data-file install-nginx-mclaren ami-bdc0ebc9 -t c1.xlarge</pre>
<p>where <code>&#039;n</code>&#039; is the number of instances to start.</p>
<p>
This starts a 64-bit 8-core 7GB “High-CPU Extra Large” Server running an Ubuntu 9.10 Karmic AMI, from Alestic.com
</p>
<p>
Your server is now visible in the Amazon AWS Management Console.
</p>
<p>
After around a minute, your servers should be running.  You can view them by typing:
</p>
<pre class="code">ec2-describe-instances</pre>
<p>Response:
</p>
<pre class="code">[Deprecated] Xalan: org.apache.xml.res.XMLErrorResources_en_US
RESERVATION	r-66ca1711	893696797735	default
INSTANCE	i-ecde7c9b	ami-b3c0ebc7	ec2-12-345-67-89.eu-west-1.compute.amazonaws.com	ip-10-226-42-19.eu-west-1.compute.internal	running	mclarenkey	0		m1.small	2010-03-16T10:50:47+0000	eu-west-1a	aki-b02a01c4	ari-39c2e94d		monitoring-disabled	12.345.67.89	10.226.42.19			ebs
BLOCKDEVICE	/dev/sda1	vol-62967b0b	2010-03-16T10:50:57.000Z	</pre>
<p>You can now see the Public <acronym title="Domain Name System">DNS</acronym> to connect.<br/></p>
<p>When using ssh, connect as user “ubuntu” (this is related to the choice of AMI)<br/></p>
<pre class="code">ssh -i ~/.ssh/mclarenkey.pem ubuntu@ec2-12-345-67-89.eu-west-1.compute.amazonaws.com</pre>
<p>It will take a few minutes for the setup script to run.  You can watch it in action like so:
</p>
<pre class="code">tail -f /var/log/syslog</pre>
<p>Our server is now running!
</p>
<p>
View it here:
</p>
<pre class="code">http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/</pre>
<p>If all is well, you&#8217;ll be sent straight to mclaren.com!<br />
More useful are the feed urls:</p>
<pre class="code">http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/feed/publish

http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/feed/subscribe</pre>
<p>Remember to pop into your nginx conf file and configure your POST IPs!</p>
<p>In the final part in the series, I&#8217;ll cover monitoring.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-5-serving-from-the-cloud/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building McLaren.com – Part 4: Load testing</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-4-load-testing/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-4-load-testing/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 10:14:31 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[mclaren]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=626</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. I&#8217;ll be writing up what we&#8217;ve done here in several parts. Sign up for my RSS feed to keep updated. Part four covers the load testing of telemetry data, broadcast by the nginx http push [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>I&#8217;ll be writing up what we&#8217;ve done here in several parts.  Sign up for my <a href="http://feeds.feedburner.com/KennethKufluk">RSS feed</a> to keep updated.</p>
<p>Part four covers the load testing of telemetry data, broadcast by the nginx http push module.<span id="more-626"></span></p>
<h2>Using Apache Bench (ab)</h2>
<p><a href="http://httpd.apache.org/docs/2.0/programs/ab.html" class="urlextern" title="http://httpd.apache.org/docs/2.0/programs/ab.html"  rel="nofollow">Apache Bench</a> (<code>ab</code>) is our preferred load-testing tool.
</p>
<p>
<strong>Load Testing is a stressful exercise</strong> for any server.  I strongly recommend creating a completely new Amazon EC2 server to run load tests from.
</p>
<p><strong>Do NOT run <code>ab</code> from your local machine.</strong>  I know you&#039;re trying to save effort, but it&#039;s not about power.  Both PCs and Macs are <em>desktop</em> machines, and will just throw errors at pathetically small load, even when testing against themselves!
</p>
<p>
<code>ab</code> may not be installed by default.  It can be installed with either of the following commands (linux):</p>
<pre class="code">yum install httpd-devel</pre>
<p>
or
</p>
<pre class="code">apt-get install apache2-utils</pre>
<p>Now type <code>ab</code> for help.
</p>
<p>
You&#039;ll need to increase your open files limit, as we did on the feed server.
</p>
<pre class="code">ulimit -n 999999</pre>
<p>A good test (in my experience) is:
</p>
<pre class="code">ab -n 100000 -c 5000 -k -r http://server/feed/subscribe</pre>
<p><strong>Some important notes before you do this</strong>:
</p>
<ol>
<li class="level1">
<div class="li"> If you&#039;re not posting to the <code>publish</code> <acronym title="Uniform Resource Locator">URL</acronym> on that box, all the <code>ab</code> threads will just hang and wait.  Make sure you&#039;re publishing.</div>
</li>
<li class="level1">
<div class="li"> <code>ab</code> is requesting a lot of documents, and it&#039;ll get them.  This will take a lot of bandwidth.  Check your charges.</div>
</li>
<li class="level1">
<div class="li"> The ”<code>-r</code>” switch is not supported by the RedHat version of <code>ab</code>.  Just take it away if it complains.</div>
</li>
<li class="level1">
<div class="li"> Don&#039;t run this against other people&#039;s servers!  It&#039;s mean and stupid.  And they could conceivably take you to court for a denial of service attack.</div>
</li>
<li class="level1">
<div class="li"> Don&#039;t run this against a live site!  Not only is that stupid, but you could also be prosecuted under terrorism legislation for denial-of-service attacks.  Your IP will get blacklisted, and you will be shunned by the wider Internet community, forced to eke out some sort of existence in lower IRC chatrooms for the rest of time.  Don&#8217;t do it kids.</div>
</li>
</ol>
<p><code>ab</code> uses the following parameters here:</p>
<ul>
<li class="level1"> n &#8211; number of requests</li>
<li class="level1"> c &#8211; number of concurrent requests</li>
<li class="level1"> k &#8211; enable &#039;keep-alive&#039; (which lowers the overhead of each connection having it&#039;s own <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> connection established each time)</li>
<li class="level1"> url &#8211; the page we&#039;re requesting.  You can always run <code>curl &lt;url&gt;</code> to see what this is.</li>
<li class="level1"> r &#8211; ignore errors &#8211; can help when using very high numbers</li>
</ul>
<p>
I&#8217;ve found the keep-alive is necessary, but I&#8217;m not sure if it&#8217;s fair to have it in there.</p>
<p><code>ab</code> will present a summary report.<br/></p>
<p>
<strong>Failures and Errors</strong>
</p>
<p>
The &#039;failures&#039; due to length shown below are common, and not really failures.  They are merely saying that the length of responses vary, and since our POSTs are different lengths then this is ok.</p>
<p>
Should you receive an <code>apr_socket_connect(): Cannot assign requested address (99)</code> or similar, wait a bit and start again.  There&#039;s probably a router in the way trying to prevent DOS attacks, and I half suspect that it take a lot longer than you think before the ports get properly closed.
</p>
<p>
<strong>What happens when it gets hit too hard?</strong>
</p>
<p>
Two important factors seem to come into play.</p>
<p>
One, the POST action starts taking longer, and can start to take longer than a second.
</p>
<p>
Two, requests seem to be served at slower intervals.  It may be that users miss packets (telemetry not so important, but comments missing are bad).  If a user is not activly waiting when the new POST comes through, they will not get the update.
</p>
<p>Example output:
</p>
<pre class="code">This is ApacheBench, Version 2.3 &lt;$Revision: 655654 $&gt;

Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking feed.mclaren.com (be patient).....done

Server Software:        nginx/0.8.33
Server Hostname:        feed.servername.com
Server Port:            80

Document Path:          /feed/subscribe
Document Length:        776 bytes

Concurrency Level:      5
Time taken for tests:   29.421 seconds
Complete requests:      100
Failed requests:        70
   (Connect: 0, Receive: 0, Length: 70, Exceptions: 0)
Write errors:           0
Keep-Alive requests:    95
Total transferred:      100395 bytes
HTML transferred:       73605 bytes
Requests per second:    3.40 [#/sec] (mean)
Time per request:       1471.046 [ms] (mean)
Time per request:       294.209 [ms] (mean, across all concurrent requests)
Transfer rate:          3.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   5.5      0      21
Processing:    11 1469 2126.8   1041   10642
Waiting:        0 1469 2127.2   1041   10642
Total:         11 1471 2130.3   1041   10659

Percentage of the requests served within a certain time (ms)
  50%   1041
  66%   1043
  75%   1046
  80%   1047
  90%   1049
  95%  10657
  98%  10659
  99%  10659
 100%  10659 (longest request)
 </pre>
</div>
<h2><a name="data_from_nginx" id="data_from_nginx">Data from NGINX</a></h2>
<div class="level2">
<p>NGINX also provides figures, and these are likely to be more accurate than via other means.  When a POST is made the reponse shows the number of active subscribers.<br />
Our publishing system records these figures and displays them in the admin console.</p>
<pre class="code">queued messages: 0
last requested: 1 sec. ago (-1=never)
active subscribers: 60001</pre>
<h2>Test Results</h2>
<p><code>ab</code> has a maximum limit on concurrent requests, which is a mere 20,000.</p>
<p>Since cloud servers are so easy to set up, and you can rely on their network connectivity, they make good machines to load-test with.  When testing against our fairly seriously specced rackspace feed server, I could run 3 machines requesting 20,000 concurrents per second before I started losing packets at a serious rate.  It could serve about 45,000 requests per second, each being around a kilobyte each.</p>
<p>Turning that around and testing against a cloud server (using the c1.xlarge instance type), we could reach only about a third of that before we saw similar delays/losses.</p>
<p>So, our final decision on load was the following:<br />
Our main server can handle the first 20,000 concurrent viewers.<br />
A new c1.xlarge server to be brought up for every subsequent 10,000 visitors.</p>
<p>The JavaScript in the client-side is supplied with URLs for every feed server, and makes a choice at random.  We&#8217;ve seen an even distribution by this means across several servers.</p>
<p>The c1.xlarge servers cost 96cents/hour, and the kind of bandwidth we&#8217;re serving, together with the relatively short race duration, makes this a very economical way to server the load.</p>
<p>In the next post, I&#8217;ll show you the scripts I use to bring these cloud servers online.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-4-load-testing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building McLaren.com – Part 3: Reading Telemetry</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-%e2%80%93-part-3-reading-telemetry/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-%e2%80%93-part-3-reading-telemetry/#comments</comments>
		<pubDate>Mon, 05 Apr 2010 15:00:44 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[crossdomain]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[mclaren]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=609</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. I&#8217;ll be writing up what we&#8217;ve done here in several parts. Sign up for my RSS feed to keep updated. Part three covers the JavaScript data for the telemetry panel, known as &#8220;The Race 1.0b&#8221;. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>I&#8217;ll be writing up what we&#8217;ve done here in several parts.  Sign up for my <a href="http://feeds.feedburner.com/KennethKufluk">RSS feed</a> to keep updated.</p>
<p>Part three covers the JavaScript data for the telemetry panel, known as &#8220;The Race 1.0b&#8221;.<span id="more-609"></span></p>
<h2>Using JSON-P</h2>
<p>There&#8217;s very little processing of the feed between the web server and McLaren.  The telemetry data is simply reformatted for convenience and merged with the Commentary (which is actually added through a local source by a guy in the pit garage).  We made sure we sent through as much data as we could get &#8211; though you may need a large screen to be able to see it!</p>
<p>Data is sent using <a href="http://en.wikipedia.org/wiki/JSON#JSONP">JSON-P</a>.  This is so that the data can be acquired from the feed domain rather than the site domain; crucial since the data feed server is in a different network for performance.</p>
<p>JSON is a great way to mark up data for consumption by JavaScript.  It&#8217;s quick to assimilate, and very easy to read and debug.  It&#8217;s also far shorter in length than the equivalent XML structure.</p>
<p>One of the principles of the Nginx Http Push Module is request idempotency.  If this doesn&#8217;t mean anything to you, you&#8217;re not alone.  All this means is that the client should always request the same URL, but with different caching times set.  This lets the caching mechanisms within nginx serve up all the messages posted since your last request.  However, this is no use to us &#8211; there is simply no way to load json-p cross-domain without using script tags, and webkit-based browsers simply <em>will not</em> reload the scripts if the filenames are the same.  (I suspect this is true of other browsers too actually, but I&#8217;ve got caching turned off for development in Firefox).</p>
<p>I append a randomised string to the end of my json-p requests (using cache:false in my jQuery ajax request) to avoid caching.  The broadcast mechanism still works, but I&#8217;m unable to use the queue backlog in the nginx http push module.  I have therefore set the maximum queue length to zero.</p>
<p>The disadvantage of this is that I will only receive packets of data that are sent when I am on the line as a subscriber.  If my connection is patchy, then I might miss packets of data &#8211; not critical for telemetry, but a shame for any commentary that is sent through.</p>
<p>However, I am able to broadcast the JSON-P packets to vast numbers of subscribers.</p>
<h2>JSON Format</h2>
<p>The &#8216;standard&#8217; telemetry packet looks like this:</p>
<pre class="brush: jscript">
Dashboard.jsonCallback(&#039;{&quot;drivers&quot;:{&quot;HAM&quot;:{&quot;code&quot;:&quot;HAM&quot;,&quot;telemetry&quot;:{&quot;timestamp&quot;:&quot;18:43:38.078&quot;,&quot;nEngine&quot;:758,&quot;NGear&quot;:0,&quot;rThrottlePedal&quot;:0,&quot;pBrakeF&quot;:2,&quot;gLat&quot;:0,&quot;gLong&quot;:0,&quot;sLap&quot;:0,&quot;vCar&quot;:0,&quot;NGPSLatitude&quot;:-37.85131,&quot;NGPSLongitude&quot;:144.97086},&quot;additional&quot;:{&quot;lap&quot;:&quot;0&quot;,&quot;position&quot;:&quot;6&quot;,&quot;is_racing&quot;:&quot;0&quot;}},&quot;BUT&quot;:{&quot;code&quot;:&quot;BUT&quot;,&quot;telemetry&quot;:{&quot;timestamp&quot;:&quot;18:43:38.317&quot;,&quot;nEngine&quot;:1695,&quot;NGear&quot;:0,&quot;rThrottlePedal&quot;:0,&quot;pBrakeF&quot;:1,&quot;gLat&quot;:0,&quot;gLong&quot;:0,&quot;sLap&quot;:0,&quot;vCar&quot;:0,&quot;NGPSLatitude&quot;:-37.84984,&quot;NGPSLongitude&quot;:144.96927},&quot;additional&quot;:{&quot;lap&quot;:&quot;0&quot;,&quot;position&quot;:&quot;1&quot;,&quot;is_racing&quot;:&quot;0&quot;}}}}&#039;);
</pre>
<p>It&#8217;s pretty clear what each term above means.  The packet above is from a lap of the Australian Grand Prix in Melbourne.</p>
<p>The commentary, and all data in &#8216;additional&#8217;, is provided by the commentry team in the pits.  This is why the lap and place can sometimes seem out of sequence.</p>
<p>At this point it&#8217;s a good time to add a legal note:</p>
<ul>
<li>The data must not be stored or distributed.  This data is <a href="http://mclaren.com/article/2010/copyright">copyright</a>ed like other information published on the Internet, and all F1 data is covered by strict rules regarding usage.</li>
<li>I am not employed by McLaren and do not speak for McLaren.  I give no authority nor advice on how to use this feed, and am only writing this post to help others who may wish to build a similar system.</li>
</ul>
<p>Having said that, here are my thoughts on netiquette:</p>
<ul>
<li>The speed, throttle and brake are sponsored by Vodafone.  If quoting these values, it would be a good karma to give a link to their site.</li>
<li>McLaren are providing this data for you to view, as a fan.  If referring to the data, you should provide a link to their site, and tell them how cool it is.</li>
<li>Don&#8217;t abuse the servers.  We can see the discrepancy between viewers on our site and from others, and measures might be taken to prevent feed highjacking.</li>
</ul>
<p>Ok, to continue.  Every lap end, we get some extra information in the packet:</p>
<pre class="brush: jscript">
Dashboard.jsonCallback(&#039;{&quot;drivers&quot;:{&quot;HAM&quot;:{&quot;lapTimeS&quot;:&quot;91.959&quot;,&quot;maxSpeedKph&quot;:305,&quot;meanSpeedKph&quot;:204,&quot;maxLateralG&quot;:6,&quot;code&quot;:&quot;HAM&quot;,&quot;telemetry&quot;:{&quot;timestamp&quot;:&quot;17:47:32.693&quot;,&quot;nEngine&quot;:15877,&quot;NGear&quot;:5,&quot;rThrottlePedal&quot;:0,&quot;pBrakeF&quot;:51,&quot;gLat&quot;:1,&quot;gLong&quot;:-2,&quot;sLap&quot;:316,&quot;vCar&quot;:202.6,&quot;NGPSLatitude&quot;:-37.84817,&quot;NGPSLongitude&quot;:144.96664},&quot;additional&quot;:{&quot;lap&quot;:&quot;0&quot;,&quot;position&quot;:&quot;7&quot;,&quot;is_racing&quot;:&quot;0&quot;}},&quot;BUT&quot;:{&quot;code&quot;:&quot;BUT&quot;,&quot;telemetry&quot;:{&quot;timestamp&quot;:&quot;17:47:32.938&quot;,&quot;nEngine&quot;:17276,&quot;NGear&quot;:5,&quot;rThrottlePedal&quot;:100,&quot;pBrakeF&quot;:1,&quot;gLat&quot;:0,&quot;gLong&quot;:0,&quot;sLap&quot;:593,&quot;vCar&quot;:232.1,&quot;NGPSLatitude&quot;:-37.846,&quot;NGPSLongitude&quot;:144.96573},&quot;additional&quot;:{&quot;lap&quot;:&quot;0&quot;,&quot;position&quot;:&quot;2&quot;,&quot;is_racing&quot;:&quot;0&quot;}}}}&#039;);
</pre>
<p>Extra fields in this packet are:</p>
<ul>
<li>lapTimeS</li>
<li>maxSpeedKph</li>
<li>meanSpeedKph</li>
<li>maxLateralG</li>
</ul>
<h2>How does it request the data?</h2>
<p>I use jQuery rather than Mootools.  So, I rewrote Leo&#8217;s Mootools plugin (provided for the chatroom on the pushmodule site) in jQuery.  I&#8217;m not convinced it offers me anything much anymore, since I&#8217;ve taken out most of what it did, but it&#8217;s easy to use.  It&#8217;s located at <code>http://mclaren.com/_assets/js/site/jquery.nginx_subscribe.js</code>.</p>
<p>I also use the jQuery JSON parsing plugin.</p>
<p>I then use those plugins with this code:</p>
<pre class="brush: javascript">
var Dashboard = function () {
	// URL of data feed
	var dashboardURL = &#039;http://feed.mclaren.com/feed/sub&#039;; // Live service
	// on document load
	$(document).ready(function() {
		// create subscriber
		var dashboard_subscribe = $.nginx_subscribe(
			dashboardURL,
			function success(textResp) {
				// the script calls success function direct
			},
			function failure(resp) {
				if (window.console) {
					console.log(&#039;subscribe failure&#039;);
				}
			}
		);
	});
	var addCommentary = function(jsonStr) {
		alert(jsonObj);
	}
	var addTelemetry = function(jsonObj) {
		alert(jsonObj);
	}
	var jsonCallback = function(jsonStr) {
		try {
			var respJson = jQuery.evalJSON(jsonStr);
			if (respJson.commentary &amp;amp;amp;amp;&amp;amp;amp;amp; respJson.commentary.length) addCommentary(respJson.commentary);
			if (respJson.drivers) addTelemetry(respJson.drivers);
		} catch (e) {
			if (window.console) {
				console.log(e);
			}
		}
	}
	return {jsonCallback:jsonCallback};
}
</pre>
<p>Obviously this script isn&#8217;t great because it will either alert you several times a second if a race is on, or not respond at all.  You&#8217;ll need to set up an nginx feed server yourself (as described in Part 2), and start POSTing to it, before you see any responses out of race time, and you&#8217;ll want to replace the alerts with something more useful.</p>
<p>I like to use the Net view of Firebug to watch the network traffic.</p>
<h2>Cool uses of the data</h2>
<p>From the very first race, our telemetry feed has been analysed by the clever mashup coders out there.  One of my favourites is <a href="http://ouseful.wordpress.com/tag/f1data/">OUseful.info</a>.</p>
<p>This map shows the points in Bahrain where Hamilton hit 100% throttle!<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/100pc-throttle.jpg"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/100pc-throttle-300x226.jpg" alt="" title="100pc-throttle" width="300" height="226" class="alignnone size-medium wp-image-617" /></a></p>
<p>And this beautiful layout shows which gear he was in as he did it.<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/gears.jpg"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/gears-238x300.jpg" alt="" title="gears" width="238" height="300" class="alignnone size-medium wp-image-618" /></a></p>
<p>It&#8217;s a great little site, and I&#8217;m looking forward to see more insights into the data coming out.  But it would be nice to see some copyright mentions shown for the data, as well as for the map itself.</p>
<p>In the next part in the series, I&#8217;ll have a look at Load Testing.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-%e2%80%93-part-3-reading-telemetry/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Building McLaren.com &#8211; Part 2:  Serving Telemetry</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-2-telemetry/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-2-telemetry/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 17:37:46 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[comet]]></category>
		<category><![CDATA[mclaren]]></category>
		<category><![CDATA[nginx]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=602</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. I&#8217;ll be writing up what we&#8217;ve done here in several parts. Sign up for my RSS feed to keep updated. Part two covers the telemetry panel, known as &#8220;The Race 1.0b&#8221;. Technically, I think this [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>I&#8217;ll be writing up what we&#8217;ve done here in several parts.  Sign up for my <a href="http://feeds.feedburner.com/KennethKufluk">RSS feed</a> to keep updated.</p>
<p>Part two covers the telemetry panel, known as &#8220;The Race 1.0b&#8221;.  Technically, I think this is the most interesting section of the site.<span id="more-602"></span></p>
<h2>Choosing a solution</h2>
<p>Before I started in this job, I spent a week scouting around for technologies and platforms to support what we need.</p>
<p>We need to distribute our data feed, on a second-by-second basis to <em>thousands</em> of viewers.  Although we weren&#8217;t sure exactly how many visitors we&#8217;d need to support, there&#8217;s a chance we&#8217;d get a mention on television, so we should be expecting visitors in the tens of thousands.  Obviously we&#8217;d need to keep the latency down too &#8211; there&#8217;s no point broadcasting stats that appear with a significant delay over the television signal.  This would very much be a companion to the television programme.</p>
<p>The most sensible way to receive a live feed is using Comet.  This is similar to the familiar Ajax, with one big difference.  When you request a data packet with Ajax, it will return as soon as it can.  It might say &#8220;here&#8217;s data&#8221;, or it might say &#8220;I have no data yet&#8221;.  The second of these cases is useless &#8211; you&#8217;d have to go and ask again.  The process of continually requesting data on a timed basis is known as <strong>polling</strong>.  It puts a big load on the server, which needs to waste execution time dealing with &#8220;I have no data&#8221; requests.</p>
<p>Comet is different, because it will not receive a &#8220;I have no data&#8221; response.  It doesn&#8217;t get a response at all in this case.  It just sits and waits, and only gets a response when data comes through.  This action is called a &#8220;push&#8221;, because the server triggers an action at the client, which is an unusual operation.  This is Comet&#8217;s key difference.</p>
<p>Comet has two flavours.  If the response terminates the connection when data comes through, then it will need to request the next packet straight away.  This, then, is known as &#8220;long-polling&#8221; Comet.  It has the familiar polling action that we had with Ajax, but far lower latency, because the packet comes through as soon as it&#8217;s available.  There is no chance of a 1 second delay.  The other flavour of Comet is &#8220;streaming&#8221;, because it drip-feeds the data into the connection.  The new HTML5 web sockets would probably fall under this heading, though I believe they use TCP direct, instead of needing to deal with HTTP.  I would love to have streamed the data, but reading about it online showed that I would have numerous problems, with proxies and hubs that would wait for packets to complete before passing on results.  Essentially, streaming is not thought to be ready for production yet.</p>
<p>I chose long-polling Comet for McLaren, and that simply left the question of implementation.</p>
<h2>Long-polling Comet:  software</h2>
<p>If you&#8217;re dealing with a server under high load, you&#8217;re bombarded with thousands of requests a second.  It was clear that I&#8217;d need the lightest possible solution to the problem.  A web server which can handle the <a href="http://www.kegel.com/c10k.html">C10k problem</a>, and hopefully do significantly better than that.  Apache uses one thread per request, so in anyone&#8217;s book, won&#8217;t be able to handle this kind of load.</p>
<p>The <a href="http://wiki.nginx.org/Main">nginx</a> (engine-x) webserver is an ideal choice for performance.  Webfaction have some <a href="http://blog.webfaction.com/a-little-holiday-present">oft-quoted stats</a> on it&#8217;s performance, and it can even show improvement when proxying to Apache.  Last year I&#8217;d read on <a href="http://simonwillison.net/2009/Oct/17/push/">Simon Willison&#8217;s blog</a> that nginx has a push module.</p>
<p>The <a href="http://pushmodule.slact.net/">nginx http push module</a> (nhpm) is a publisher/subscriber module for nginx.  It serves as a hub which can receive POST requests, and then distribute them to a large number of subscribers waiting on GET requests.  Naturally, the POSTs can be filtered by IP.  The logical demo for any Comet demo is a chatroom, which is duly presented on the site.  What I needed was something far more basic, because everyone receives the same data, but it would do.</p>
<p>Another option was to use <a href="http://nodejs.org/">node.js</a>, which has apparently now been used to support a million comet user for <a href="http://plurk.com">Plurk</a>.  Despite performing reasonably well in tests, I decided against node because I didn&#8217;t have the time to build and test a webserver myself before launch.  I tend to think there&#8217;s more at work in a robust webserver than first appears.  Nginx is mature, and the push module is several iterations in.</p>
<p>Let&#8217;s just go ahead and install nginx on our linux variant.  This will install on a Mac natively, but if you want to do do the later performance testing chapters, I recommend getting another linux box going.  You can <a href="http://kenneth.kufluk.com/blog/2010/03/getting-started-with-amazon-ec2/">set one up with Amazon</a> cheaply, or with <a href="http://www.rackspacecloud.com/">Rackspacecloud</a> if you find all that certificate stuff confusing.</p>
<p><strong>Installing nginx with the push module:</strong><br />
If you&#8217;re not connecting as root, switch to a root shell</p>
<pre class="brush: bash">
  sudo bash
</pre>
<p>Download and untar nginx and the nginx push module</p>
<pre class="brush: bash">
  curl -O http://www.nginx.org/download/nginx-0.7.65.tar.gz
  curl -O http://pushmodule.slact.net/downloads/nginx_http_push_module-0.692.tar.gz
  tar -xzvf nginx-0.7.65.tar.gz
  tar -xvzf nginx_http_push_module-0.692.tar.gz
</pre>
<p>nginx requires PCRE (regular expressions library) and open-ssl </p>
<pre class="brush: bash">
  curl -L -O http://downloads.sourceforge.net/pcre/pcre-8.01.tar.gz
  tar -xzf pcre-8.01.tar.gz
  apt-get install libssl-dev  #ubuntu
  yum install openssl-devel  #redhat/fedora
</pre>
<p>Now we compile and install nginx</p>
<pre class="brush: bash">
  cd nginx-0.7.65
  ./configure --add-module=../nginx_http_push_module-0.692 --with-http_flv_module --user=apache --group=apache --with-http_gzip_static_module --with-pcre=../pcre-8.01
  make &amp;&amp; make install
</pre>
<p>Now nginx is installed, we can pop off and edit the nginx.conf file.</p>
<pre class="brush: bash">
  vi /usr/local/nginx/conf/nginx.conf
</pre>
<p>The nginx config file is a thing of beauty.  If you&#8217;re used to Apache configs, this will be like upgrading from PC to Mac.  Difficult to switch but well worth the effort.</p>
<p>For now, just grab <a href="http://kenneth.kufluk.com/pirata/nginx.conf">my simple version of the nginx conf</a>.</p>
<p>If you haven&#8217;t got a user already, let&#8217;s create one, somewhat confusingly named &#8220;apache&#8221;, though you can choose your own name if you want.</p>
<pre class="brush: bash">
  groupadd apache
  useradd -c &quot;Apache Server&quot; -d /dev/null -g apache -s /bin/false apache
</pre>
<p>And start the server:</p>
<pre class="brush: bash">
  /usr/local/nginx/sbin/nginx
</pre>
<p>(you can stop with):</p>
<pre class="brush: bash">
  /usr/local/nginx/sbin/nginx -s stop
</pre>
<p>Remember it&#8217;s listening on Port80, so make sure it&#8217;s not conflicting with Skype or Apache.</p>
<p>Nginx is now listening on port 80.<br />
If you send a POST to /feed/publish it will get broadcast out to anyone hanging on /feed/subscribe<br />
Let&#8217;s test it in a browser.<br />
Open http://localhost/feed/subscribe<br />
It will hang on and wait.</p>
<p>Now create a small HTML page that POSTs to /feed/publish</p>
<pre class="brush: html">
&lt;form action=&quot;http://127.0.0.1/feed/publish&quot; method=&quot;POST&quot;&gt;
  &lt;textarea name=&quot;body&quot; value=&quot;content&quot;/&gt;
  &lt;input type=&quot;submit&quot;/&gt;
&lt;/form&gt;
</pre>
<p>Hit Submit, and &#8230; nothing will happen.</p>
<p>Closer inspection with FireBug shows that you&#8217;ve made an OPTIONS request instead of a POST.<br />
This is a limit on cross-domain posting in browsers.</p>
<p>Fortunately, I allowed for this in the conf file.  Move your HTML file to /usr/local/nginx/html/test.html<br />
You can now view it on your local server as http://localhost/test.html<br />
Hit Submit, and your GET thread that was hanging on, will now instantly respond with a packet of data for you to download.</p>
<p>We have a pub/sub hub running!</p>
<p>Another option is to POST using PHP or similar scripting language, which doesn&#8217;t have the browser cross-domain scripting security block.</p>
<p>You will want to lock down your POST url, because you don&#8217;t want everyone being able to broadcast to your users!<br />
Simply change this bit of the conf file:</p>
<pre class="brush: php">
	location /feed/publish {
		allow 127.0.0.1;  # deny public posting - only allow from this IP
		deny all;
		...
	}
</pre>
<p>All nginx http push module needs is a little memory to work.  It barely scratches the CPU at all.  Let me show you the CPU usage graph from the first race using the telemetry.<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-14-at-14.18.54.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-14-at-14.18.54-300x144.png" alt="" title="Screen shot 2010-03-14 at 14.18.54" width="300" height="144" class="alignnone size-medium wp-image-669" /></a></p>
<h2>Long-polling Comet:  system</h2>
<p>When configuring a Comet server for performance, everyone turns first to Richard Jones&#8217;s awesome posts, <a href="http://www.metabrew.com/article/a-million-user-comet-application-with-mochiweb-part-1">A Million-user Comet Application with Mochiweb</a>.  I followed his advice on server configuration.</p>
<p>Specifically, I do the following:</p>
<pre class="brush: bash">
ulimit -n 999999 # increase the number of available file handles
echo &quot;1024 65535&quot; &gt; /proc/sys/net/ipv4/ip_local_port_range # add more ephemeral ports
</pre>
<p>Of course, your ulimit change won&#8217;t persist, so you need to edit the limits conf:</p>
<pre class="brush: bash">
vi /etc/security/limits.conf
</pre>
<p>Insert these lines in the document somewhere:</p>
<pre class="brush: bash">
*      hard nofile 999999
*      hard nproc 999999
*      soft nofile 999999
*      soft nproc 999999
</pre>
<p>(ensure there&#8217;s no spaces before the asterisks).</p>
<h2>Long-polling Comet:  hardware</h2>
<p>We had three heavy boxes set up to run the site.  However, we were very keen to keep the site running regardless of visitors accessing our data feed.  If anything, we&#8217;d rather lose the telemetry than the site itself, so it didn&#8217;t make sense to mix hardware between the two functions.</p>
<p>Two boxes were kept to run the site then, under a load balancer.  The third box was moved out, and runs the feed server alone.</p>
<p>Next problem was the firewall.  Our firewall is hardware limited to 10,000 concurrent users, great for a standard site running simple subsecond requests, but utterly useless for any kind of Comet connection.  For another 300UKP the firewall could go to 5,000 more concurrents, but was still a huge limiting factor.  So we juggled it to get the best connection possible for the feed server.</p>
<p>Since we&#8217;re running one box, we could also move out from behind the load balancer.  We now can&#8217;t run on the same domain, so created a new subdomain for the server.</p>
<p>Heading back to original calculations, we then looked at the next perceived limit: bandwidth.</p>
<p>If our packet size is, for example, 1 kilobyte, including any http header.<br />
Assuming 1 packet is delivered per second.<br />
And our network card is 100Mbps (mega bit per second), which works out as 10MBps (mega byte per second).<br />
How many users can we support?</p>
<p>Simple maths, 10,000MBps / 1k = 10,000 requests per second.</p>
<p>So, we can&#8217;t move beyond 10,000 concurrent users anyway, simply because the bandwidth is a limiting factor, even with our small packets.</p>
<p>So we now have a Gigabit card, and Gigabit switch.  I&#8217;ve tested that up to 333Mbps, which is enough for at least 30,000 concurrents.  Any more and we&#8217;ll need cloud servers to support the load (hopefully detailed in a later part).</p>
<h2>Possible Alternatives</h2>
<p>Having thought about all this, the system could be replaced with a simple memory lookup.  The telemetry details could be stored in an array that is constantly overwritten, and the commentary stored in a queue in a way that you could ensure each requestor reliably received them all.  I had a quick peek at using the nginx memcached plugin, which would let me serve values straight from memcached.  It definitely had an appeal, though obviously performance wouldn&#8217;t get any better &#8211; since our limiting factor is the hardware supplying the bandwidth.</p>
<p>Next Part, I&#8217;ll look at the Feed itself, and how it&#8217;s processed on the front-end.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part-2-telemetry/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building McLaren.com &#8211; Part 1:  HTML</title>
		<link>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part1-html/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part1-html/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 14:40:50 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mclaren]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=579</guid>
		<description><![CDATA[I&#8217;ve just finished working on McLaren&#8217;s new F1 site, http://mclaren.com/home, for the 2010 season, at Pirata London, for Work Club. I&#8217;ll be writing up what we&#8217;ve done here in several parts. Sign up for my RSS feed to keep updated. First up, the HTML. The site design is brilliantly engineered for layout on the web. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve just finished working on McLaren&#8217;s new F1 site, <a href="http://mclaren.com/home">http://mclaren.com/home</a>, for the 2010 season, at <a href="http://www.piratalondon.com">Pirata London</a>, for <a href="http://www.work-club.com/">Work Club</a>.</p>
<p><a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/mclaren-site-2010.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-13.02.12-300x213.png" alt="" title="The new McLaren F1 site for 2010" width="300" height="213" class="alignnone size-medium wp-image-595" /></a></p>
<p>I&#8217;ll be writing up what we&#8217;ve done here in several parts.  Sign up for my <a href="http://feeds.feedburner.com/KennethKufluk">RSS feed</a> to keep updated.</p>
<p>First up, the HTML.  The site design is brilliantly engineered for layout on the web.  It&#8217;s clean, fresh and bold.  No rounded corners or &#8220;web 2.0&#8243; gradients.  <span id="more-579"></span>All credit to our designer, creative director and our client.</p>
<p>You might think this would be a breeze to implement, but actually it threw up several significant issues, which have been tricky.  Here&#8217;s a few interesting ones:</p>
<h2>Dymo labels</h2>
<p>The design for the majority of text areas has a strip of colour behind each row of text. I call this the &#8220;Dymo label&#8221; effect, because it looks like strips of dymo tape.<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/dymo_strips.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-11.57.28-300x97.png" alt="" title="McLaren&#039;s &quot;dymo&quot; style strips of colour behind lines of text." width="300" height="97" class="alignnone size-medium wp-image-582" /></a><br />
To get this effect, one cannot assign a background colour to the block-level container, as this will just appear as a solid rectangular block.  One needs an inline display element, so let&#8217;s use a span.  We add a background color to this span, and add padding above and below the text for a bit of coloured space.  The gaps between the lines are generated with the &#8216;line-height&#8217; style.  So our text size plus padding is 30px high, and our line-height is 36px, giving us a 6px gap.</p>
<p>Neat &#8211; our dymo-style strips are now placed.  Only problem is that the text lines butt up hard against the white surrounds:<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/line-ends-missing.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/line-ends-gone-300x97.png" alt="" title="line ends gone" width="300" height="97" class="alignnone size-medium wp-image-585" /></a></p>
<p>We want to add some padding to the start and ends of the lines, as shown here:<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/green-ends.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/green-ends-300x97.png" alt="" title="green-ends" width="300" height="97" class="alignnone size-medium wp-image-586" /></a></p>
<p>I tried a variety of options.  Margins are useless, because they don&#8217;t apply to inline elements.  Left and right padding works only on the beginning and end of the span, and so the sentence. Word spacing is only supported in a few browsers, and also increases gaps between words.</p>
<p>In fact, <strong>there is no way to control padding at wrapped line ends</strong>.</p>
<p>Asking on the CSS discussion groups pointed me to this item in the spec:  &#8216;<a href="http://www.w3.org/TR/css3-background/#box-decoration-break">box-decoration-break</a>&#8216;.  This obscurely named property would enable me to treat each line as a box, enabling me to add padding to the start and end of each line.  I find the explanation completely confusing, since it mostly concerns itself with page breaks.  It&#8217;s also unclear whether this fix will solve my inline blocks problem.</p>
<p>Of course, the biggest problem with this idea is that it&#8217;s so complex and unasked-for, that it hasn&#8217;t been implemented by any browser vendors, and as such, may drop out of the specification.</p>
<blockquote><p>The following features are at-risk and may be dropped at the end of the CR period if there has not been enough interest from implementers: ‘box-decoration-break’.</p></blockquote>
<p>And yet, since this is specified, there is resistance to the idea of implementing this (clearly useful) feature in any other way.  Personally, I would like to see a &#8216;wrapped-line-padding&#8217; property.</p>
<p>Another suggestion was to use a striped background-image property, with scaling.  I imagine that would work, but present the same problems.  If applied at the block-level, the last line would run to the end of the box.  If applied inline, there would be no padding.</p>
<p>So, what have I done?  I have implemented a fix in JS of course, and look forward to your inevitable criticism.  <img src='http://kenneth.kufluk.com/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<pre class="brush: jscript">
$(document).ready(function() {
	if(!$.browser.msie || $.browser.version&gt;7) $(&#039;span&#039;).not(&#039;.no-jswrap&#039;).each(function() {
		var text = $(this).html();
		var outstr = text.replace(/([^ ]+)/g, &#039;&lt;b&gt;$1&lt;/b&gt;&#039;);
		outstr = outstr.replace(/ /g, &#039;&#039;);
		$(this).html(outstr);
		$(this).addClass(&#039;jswrap&#039;);
	});
});
</pre>
<p>After document ready, each word is wrapped with deprecated, but still valid, &lt;b&gt; tags.  I then remove the spaces between the words.<br />
The &lt;b&gt; tags have padding of their own.<br />
<a href="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/b-tags.png"><img src="http://kenneth.kufluk.com/blog/wp-content/uploads/2010/03/Screen-shot-2010-03-31-at-12.39.08-300x165.png" alt="" title="Padding words using b tags to get line padding" width="300" height="165" class="alignnone size-medium wp-image-589" /></a></p>
<p>The downsides to this approach are of course, extra JS (though it&#8217;s only a cosmetic enhancement, so I&#8217;m not fussed if you&#8217;re running without JS.   And the bigger issue &#8211; the lack of spaces.  If you cut and paste text from the site (notice the pretty <em>selection</em> styles when you do), then your text pastes without spaces.  I can only apologise, and hope that it doesn&#8217;t ruin your screenreader.</p>
<blockquote style="word-wrap:break-word;text-align:left;"><p>Jenson-Champion’smoment 2009BrazilianGrandPrix- AdelugeonSaturdayafternoonleftJensonpowerlesstoovercomehiscar’sgripproblems,andhequalifiedadisappointing14th.OnSunday,however,hedrovewithflairandmercilessaggressiontosweepthroughtofifth–itwasnocoincidencethatoneofhisgreatestdrivesfinallyelevatedhimtothestatusofworldchampion.</p></blockquote>
<p>But it does give us our line padding. </p>
<h2>Hiding Navigation</h2>
<p>The navigation at the top of the page hides itself away after an amount of time.  It won&#8217;t win any usability awards of course, but might garner ourselves the odd design award (who never like sites to be <em>too</em> easy).  Regardless, it&#8217;s got to be hidden away.</p>
<p>This was a simple effect to add. I simply used jQuery to hide the navigation element after 3 seconds of the page loading, revealing if the user rolls their mouse over it.  After implementing it locally, I found an immediate issue.  When clicking to a new page, it was easy to leave the mouse over the navigation, which wouldn&#8217;t trigger a mouse enter event, especially if focus was on another window or tab.  Basically, it produced a lot of circumstances where the navigation was hiding, regardless of the mouse being over the nav.  Not good.</p>
<p>I have now moved the event, so the trigger for the timer would only start after the user rolls over the content on the page.  This works a lot better, though it&#8217;s a little confusing because the navigation appears or disappears seemingly at random.  But it&#8217;s effective, and it works.</p>
<p>The next problem was based on scrolling.  If you scroll to the bottom of a page, then use jQuery to slowly hide an element higher up the page, the document gets shorter.  Browsers do not handle this smoothly (even Chrome).  The screen flickers and jumps all over the place, and is likely to induce seizure even in the most hardened fans of strobe lighting.</p>
<p>I solved the scroll problem by hiding the navigation immediately after a page scroll.  So, on any scroll event, the nav disappears, fast.  No more flicker, no more problems.</p>
<pre class="brush: jscript">
	/*
	 * Expand and contract the navigation
	 */
	var navHider = function() {
		// show the nav when you roll over the top
		var showNav = function() {
			clearTimeout(navHideTimer);
			$(&#039;#navigation-wrapper&#039;).slideDown(&quot;slow&quot;);
		};
		// hide the header 1 sec after you roll over the content
		var hideNav = function() {
			clearTimeout(navHideTimer);
			navHideTimer = setTimeout(navHiderAction, 1000);
		};
		$(&#039;#header&#039;).mouseenter(showNav);
		$(&#039;#header a&#039;).focus(showNav);
		$(&#039;#header a&#039;).blur(hideNav);
		$(&#039;#content&#039;).mouseover(hideNav);

		// the hiding function
		var navHiderAction = function() {
			if (hiderEnabled) $(&#039;#navigation-wrapper&#039;).slideUp(&quot;slow&quot;);
		}
		// if the window is scrolled, hide it quickly to avoid jerky/flickering effects
		$(window).scroll(function() {
			clearTimeout(navHideTimer);
			if (hiderEnabled) $(&#039;#navigation-wrapper&#039;).slideUp(100);
		});
		var navHideTimer = 0;
	};
</pre>
<p>Of course, you&#8217;re probably still thinking of the usability issues.  Certainly, we initially saw a large number of visitors to our footer links (like the copyright page) when we launched, but this has flattened off.  Our visitors have found the navigation, they know how to bring it back, and we&#8217;ve had several compliments from the online design community.  I think we&#8217;ve brought our visitors with us on this one, and it&#8217;s probably acceptable now.</p>
<h2>Shadowbox URLs</h2>
<p>We use the cool ShadowBox plugin for our lightbox effect.  But of course, the articles shouldn&#8217;t show navigation in the lightbox, so they have to know when they&#8217;re in the lightbox and when they&#8217;re not.  I want to keep the link the same, so non-JS users can use the article links as they stand.</p>
<p>Simple fix to the init for ShadowBox gives the solution:</p>
<pre class="brush: jscript">
	Shadowbox.init({
		players:[&#039;iframe&#039;,&#039;img&#039;,&#039;swf&#039;,&#039;html&#039;],
		onOpen:function(e) {
			// let the page know that it shouldn&#039;t show headers
			if (Shadowbox.gallery[0].player==&#039;iframe&#039; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp; !Shadowbox.gallery[0].content.match(&#039;iframe=true&#039;)) Shadowbox.gallery[0].content = e.content = e.content+&#039;?iframe=true&#039;;
		},
		onClose:function(e) {
			// reload the CSS in IE8 to avoid sporadic font-face corruption
			$(&#039;#main-css&#039;).each(function() {
				this.href = this.href.split(&#039;#&#039;)[0];
			});
		}
	});
</pre>
<p>This tells ShadowBox to append the &#8216;?iframe=true&#8217; querystring to links as they are opened.  Note that I&#8217;ve also added a font-face fix for IE8 to the onClose function &#8211; for this to work, you&#8217;ll need to put an ID on your main stylesheet of &#8216;main-css&#8217;.</p>
<p>That&#8217;s it for the HTML.  In the next part, I&#8217;ll look at the telemetry feed.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/04/building-mclaren-com-part1-html/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Getting started with Amazon EC2</title>
		<link>http://kenneth.kufluk.com/blog/2010/03/getting-started-with-amazon-ec2/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/03/getting-started-with-amazon-ec2/#comments</comments>
		<pubDate>Sun, 28 Mar 2010 13:21:01 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Projects]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=556</guid>
		<description><![CDATA[Amazon provides a variety of hosting capabilities under the name “Amazon Web Services” (AWS). These are often described as &#8216;cloud&#8217; servers. Products offered are: EC2 &#8211; Elastic Compute Cloud &#8211; Application servers S3 &#8211; Storage &#8211; also known as EBS (elastic block store) &#8211; Disc blocks that can either be served directly, or attached to [...]]]></description>
			<content:encoded><![CDATA[<p>Amazon provides a variety of hosting capabilities under the name “Amazon  Web Services” (AWS).<br />
These are often described as &#8216;cloud&#8217; servers.  Products offered are:</p>
<ul>
<li>EC2 &#8211; Elastic Compute Cloud &#8211;  Application servers</li>
<li>S3 &#8211; Storage &#8211; also known as EBS  (elastic block store) &#8211; Disc blocks that can either be served directly,  or attached to an EC2 server.</li>
<li>CloudWatch &#8211; monitoring for EC2  servers (CPU, network, disc IO)</li>
<li>CloudFront &#8211; a CDN (notice how  confusing the name is)</li>
<li>Elastic Load Balancing &#8211; yeah yeah  yeah</li>
<li>Elastic MapReduce &#8211; for proper  techies</li>
</ul>
<p>All of these services are charged per use, so by hour, by gigabyte  (memory), by gigabyte (bandwidth), by gigabyte (disc), etc.<span id="more-556"></span><br />
It is well worth keeping an eye on costs, as it&#8217;s easy to run up a big  bill without realizing.<br />
One of the great features is that there are no setup fees, so it&#8217;s very  easy to create and destroy servers without running up large bills.<br />
If something isn&#8217;t working, simply start again.</p>
<h3>EC2</h3>
<p>Application servers can be created using one command or a few clicks.  A  server is called an “<strong>instance</strong>”, and is created from an  image called an “<strong>AMI</strong>”.<br />
Amazon provide a set of AMIs to build from.  So does &#8216;<a href="http://alestic.com">alestic.com</a>&#8216;, who provide good ubuntu images.<br />
They can be created in a variety of sizes, varying in memory, CPU and  disc space.<br />
These servers can also be quickly “terminated”.  This means that they&#8217;re  deleted from the ether and can never be retrieved.<br />
A server is charged from creation to termination.</p>
<h3>S3/EBS</h3>
<p>Since application servers can be so easily created and destroyed, one  needs a location where data is stored persistently.<br />
EBS blocks can be created and mounted onto EC2 instances.  An EBS store  can only attach to one EC2 at a time, but an EC2 instance can have many  EBS stores.<br />
If running a persistent (long-running) application server, you should  mount an EBS store and link it symbolically to the EC2 instance.  You  then put all your files directly into EBS.  Should you need to upgrade  your application server, you simply attach the EBS to a new instance and  destroy the old one.<br />
Larger servers are already based on EBS stores.</p>
<p>I&#8217;m not going to go into EBS stores in this post.  I&#8217;ll focus on EC2.</p>
<p>If you haven&#8217;t used Amazon AWS before, I strongly recommend following this guide with your own Amazon account.<br />
It will cost around a dollar.</p>
<h2>Working  with EC2 &#8211; Setup</h2>
<p>Getting started with Amazon Web Services is the trickiest bit, and can  often be off-putting.<br />
Once you&#8217;re set up then it&#8217;s much easier.  So hang in there.</p>
<p>Simple steps:</p>
<ol>
<li> Get an Amazon account</li>
<li> Get your keys</li>
<li> Create an instance</li>
<li> Get connected</li>
<li> Get serving</li>
</ol>
<h3>Get an  Amazon Account</h3>
<p>Head here and hit the signup button.</p>
<p><a title="http://aws.amazon.com/ec2/" rel="nofollow" href="http://aws.amazon.com/ec2/">http://aws.amazon.com/ec2/</a></p>
<p>You can use your existing Amazon account.<br />
You are agreeing to be billed monthly for any costs you incur, so you  won&#8217;t get any more warnings!</p>
<h3>Get your keys</h3>
<p>You should be directed to security where you&#8217;ll be asked to download an <code>X.509  key</code>.  Do that, and put it somewhere (very) safe.<br />
This is needed when you use command-line-tools (later steps).</p>
<p>On that same screen, you can create a “<strong>Key Pair</strong>”.  Do  that, and download the two files it gives you.  This should be a <code>PEM</code> file and a <code>CERT</code> file.<br />
Again, keep them safe.  The <code>PEM</code> file is the one we&#8217;ll use.</p>
<p>Rather than using passwords for <acronym title="Secure Shell">SSH</acronym> (SO last century), Amazon require you to use these files to  authenticate yourself when connecting.</p>
<p>I&#8217;m on a Mac, so I copy the <code>PEM</code> file to a ”<code>.ssh</code>”  folder in my user folder.<br />
You need to <code>chmod 400</code> the file to prevent a connection  error.<br />
Let&#8217;s say I created a keypair called ”<code>kennethkey</code>”, I can  now do this.</p>
<pre>cd ~/.ssh
chmod 400 kennethkey.pem
ssh-add kennethkey.pem</pre>
<p>This will add the key for the duration of my Terminal session.</p>
<h3>Create an  instance</h3>
<p>Now you&#8217;re set up, head back to the browser and find your way to the AWS Management Console.</p>
<p><a title="https://console.aws.amazon.com/ec2/home?region=eu-west-1" rel="nofollow" href="https://console.aws.amazon.com/ec2/home?region=eu-west-1">https://console.aws.amazon.com/ec2/home?region=eu-west-1</a></p>
<p>This is where you can do most of your work with Amazon.<br />
This console is new, so many documents you find online don&#8217;t describe  working with it.<br />
Don&#8217;t worry about that.</p>
<p><strong>What can we see?</strong><br />
Tabs on the top &#8211; we only need “Amazon EC2”<br />
Top nav &#8211; to Products for details of pricing (can be useful), and to  Account for details of costs to date (can be scary).<br />
Stick with the console for now.<br />
On the left is the nav.  Choose “<strong>EU West</strong>” as your  region unless you&#8217;re doing something US-centric.  Do that now.<br />
Have a quick look through the other links on the left.</p>
<ul>
<li> Instances &#8211; we can create servers in  here</li>
<li> Volumes &#8211; create EBS blocks here</li>
<li> Elastic IPs &#8211; here you can assign  fixed IPs</li>
<li> Security Groups &#8211; here you manage  the “firewall”, opening ports as required.</li>
<li> Key Pairs &#8211; manage those keys here  (unlikely to be needed)</li>
</ul>
<p>The others you can investigate yourself.  Beware of Snapshots, which are  a way to store incremental backups, but tend to be more complicated  than expected.</p>
<p>Let&#8217;s start with an Instance.<br />
Go to the Instances view, and click “Launch Instance”.<br />
You now choose an image (AMI).<br />
Just pick the “Basic 64-bit Fedora Core 8”, which is a 64-bit machine  (meaning we can get larger varieties), and is a linux variant.</p>
<p>In the next page you can choose the “Type”.  This is the size for the  instance, and this affects the pricing.  The pricing is annoyingly not  displayed here though.<br />
For this case, we chose a beefy <code>c1.xlarge</code>, which is 96cents  an hour.</p>
<p>Choose defaults for the rest of that page.<br />
On the next screen you can enable cloudwatch, which is about 15cents an  hour extra.  Leave the rest as defaults.<br />
On the next screen choose the keypair you created earlier.</p>
<p>Finally, Choose a security group.  Let&#8217;s just choose the default (don&#8217;t  create a new one).<br />
If you&#8217;re working on real projects, you can create and name security groups, which helps you see which servers are doing what in the console later on.  I find this easier than trying to memorize the code names for each machine.</p>
<p>Continue and Launch.<br />
You&#8217;ll see your instance appear in the list as “pending”.<br />
After about 30 seconds, it will be “running”.<br />
You are now being charged.</p>
<h3>Get connected</h3>
<p>We connected our new instance to the Security Group called “default”.<br />
Before we access that instance, we need to <strong>open the ports</strong>.<br />
In the Console, head to “Security Groups”.</p>
<p>Click “default” and in the bottom panel create lines for <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> (just choose <acronym title="Hyper Text Transfer Protocol">HTTP</acronym> in the dropdown and  click save)<br />
And <acronym title="Secure Shell">SSH</acronym> (again, choose <acronym title="Secure Shell">SSH</acronym> in the dropdown and save)<br />
We can now connect via <acronym title="Secure Shell">SSH</acronym>.</p>
<p>Head back to the Instances window.<br />
Click our instance, which is now “running”.<br />
In the bottom window you can see various details.<br />
“Public <acronym title="Domain Name System">DNS</acronym>” is the name  of the machine for the outside world.  It&#8217;ll be something like this:  <code>ec2-12-345-67-89.eu-west-1.compute.amazonaws.com</code><br />
Right click the instance and choose “Connect”.<br />
A window appears giving you instructions for connection.</p>
<p>On my Mac I just open a Terminal window.<br />
Because I used ”<code>ssh-add</code>” earlier, I don&#8217;t need the <code>-i  &lt;keypair&gt;</code> option.<br />
But if I didn&#8217;t use <code>ssh-add</code>, I would <code>cd</code> to the  directory where I stored the <code>PEM</code> key first.<br />
Once you&#8217;ve entered the command, you should get straight into the  machine as root!<br />
We&#8217;re connected!</p>
<h3>Get Serving</h3>
<p>Some AMIs have LAMP already installed.  We chose a vanilla AMI, so we  have nothing but the <acronym title="Operating System">OS</acronym>.<br />
For the purposes of this demo, let&#8217;s install Apache.</p>
<pre>yum install httpd</pre>
<p>And start it:</p>
<pre>/etc/init.d/httpd start</pre>
<p>Now we can go to the website:<br />
<a title="http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/" rel="nofollow" href="http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/">http://ec2-12-345-67-89.eu-west-1.compute.amazonaws.com/</a></p>
<p>We should see the Fedora Test Page!</p>
<h3>Install the command line tools</h3>
<p>Amazon provide a whole set of command line tools to help you work with instances.  These are always added first for any new feature, so they offer more options than are available through the console, for example to use the RDS features.</p>
<p>Get the Developer Tools here</p>
<p><a title="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&amp;categoryID=88" rel="nofollow" href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&amp;categoryID=88">http://developer.amazonwebservices.com/connect/entry.jspa?externalID=351&amp;categoryID=88</a></p>
<p>Put them somewhere permanent on your system</p>
<p>Follow the instructions for setup here:</p>
<p><a title="http://docs.amazonwebservices.com/AWSEC2/2009-11-30/UserGuide/index.html?setting-up-your-tools.html" rel="nofollow" href="http://docs.amazonwebservices.com/AWSEC2/2009-11-30/UserGuide/index.html?setting-up-your-tools.html">http://docs.amazonwebservices.com/AWSEC2/2009-11-30/UserGuide/index.html?setting-up-your-tools.html</a></p>
<p>It&#8217;s long and complicated, but IMHO that&#8217;s because it&#8217;s built in (ptui) Java.</p>
<h4>Specifics for Mac</h4>
<pre>mkdir ~/Documents/ec2-tools
mv /Users/kenneth/Downloads/ec2-api-tools-1.3-46266 ~/Documents/ec2-tools
mkdir ~/.ec2
mv /Users/kenneth/Downloads/X.509keys/* ~/.ec2</pre>
<p>Add the following to ~/.profile</p>
<pre>#Amazon EC2 Command line tools
export EC2_HOME=~/Documents/ec2-tools
export PATH=$PATH:$EC2_HOME/bin
export EC2_CERT=~/.ec2/cert-#######.pem   (match your key names)
export EC2_PRIVATE_KEY=~/.ec2/pk-########.pem
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
export EC2_URL=https://ec2.eu-west-1.amazonaws.com</pre>
<p>Then run this command:</p>
<pre>source ~/.profile</pre>
<p>(This just runs the stuff in the .profile)</p>
<p>Check that it&#8217;s all peachy with this command:</p>
<pre>ec2-describe-regions</pre>
<p>You can now create instances on the command line.<br />
A word of warning:  Internal bandwidth costs are not charged (eg, between two instances, or between your instance and your RDS), but if your machines are in different physical regions, the bandwidth is treated as external.  I ran up bandwidth charges of a terabyte in my first week, which proved hugely costly.  Be sure to check your regions.</p>
<p>We can view our servers like so:</p>
<pre>ec2-describe-instances</pre>
<p>Or even create a new one like this:</p>
<pre>ec2-run-instances -n 1 --key kennethkey ami-bdc0ebc9 -t c1.xlarge</pre>
<h3>Rip it down</h3>
<p>Ok, we&#8217;ve had some fun, and incurred about a dollar in fees.<br />
Let&#8217;s ditch our server.<br />
Head back to the console, and right-click the instance(s).<br />
Choose “terminate”.<br />
Our server will be destroyed and we&#8217;ll stop being charged.<br />
Our <acronym title="Secure Shell">SSH</acronym> session will be kicked off.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/03/getting-started-with-amazon-ec2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Long polling Comet</title>
		<link>http://kenneth.kufluk.com/blog/2010/02/long-polling-comet/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/02/long-polling-comet/#comments</comments>
		<pubDate>Fri, 05 Feb 2010 16:14:53 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[comet]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=346</guid>
		<description><![CDATA[I&#8217;ve been working on a long-polling comet client from Amix at Plurk, who uses it to monitor messages on the server. While he simply provides it as a &#8216;solution&#8217;, not detailing the hurdles he overcame to get there, it&#8217;s clear that there&#8217;s some thought gone into it. http://amix.dk/blog/post/19489#Comet-long-polling-for-all-browsers-using-ScriptCommunicator The script requires you to set a [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on a long-polling comet client from Amix at Plurk, who uses it to monitor messages on the server.  While he simply provides it as a &#8216;solution&#8217;, not detailing the hurdles he overcame to get there, it&#8217;s clear that there&#8217;s some thought gone into it.<br />
<a href="http://amix.dk/blog/post/19489#Comet-long-polling-for-all-browsers-using-ScriptCommunicator">http://amix.dk/blog/post/19489#Comet-long-polling-for-all-browsers-using-ScriptCommunicator</a></p>
<p>The script requires you to set a variable in your jsonp response.  The script checks whether that variable is true after the jsonp loads.  If true, your json has loaded.  If not, something has gone wrong.</p>
<p>So, if your jsonp looks like this:<br />
callback({blah:&#8221;blah&#8221;});<br />
You&#8217;ll need to revise it to this:<br />
callback({blah:&#8221;blah&#8221;});ScriptCommunicator.callback_called = true;</p>
<p>On the jQuery forums, John Resig said he isn&#8217;t enamoured with this approach since it means adapting the jsonp response, and if you&#8217;re using jsonp then you&#8217;re probably not in control of the feed.  It&#8217;s a fair point, but you could simply add the callback_called variable into the callback function instead.</p>
<p>Plus, if you&#8217;re running Comet long-polling, it&#8217;s better to use a different domain for your comet feed, to prevent tying up threads in the browser.  So jsonp is needed.</p>
<p>The &#8220;difficult&#8221; part of the process is knowing when to run the second script, that checks the callback_called value.  Amix uses a variety of methods for different browsers to achieve this.</p>
<p>In Firefox (default case) -> create two script tags using document.createElement, and attach them to the body.</p>
<p>In IE -> use the onreadystatechange event to call onSuccess, and then detect errors there, based on the readyState (somewhat illogical).</p>
<p>In Safari/Chrome -> write two script tags out with document.writeln.  Presumably, this is intended for use with an iframe, though that&#8217;s not clear.  OOTB, no webkit browsers receive data.</p>
<p>Having tried to hack the script to make the Safari browsers use the same method as Firefox, I&#8217;ve found that Safari&#8217;s caching kicked in and failed to load new scripts from the server (though this could be my fault for not changing the script name each time).</p>
<p>Looking back at Plurk, the comet scripts are indeed in an iframe, so I think this script probably needs some more input before it&#8217;s going to work for anyone else.  Hopefully, Amix (or someone) will put together a demo page and make any necessary fixes to the script when making that work.</p>
<p>It&#8217;s a very useful function/tool/script.  Thanks Amix.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/02/long-polling-comet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Typekit</title>
		<link>http://kenneth.kufluk.com/blog/2010/01/typekit/</link>
		<comments>http://kenneth.kufluk.com/blog/2010/01/typekit/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 15:37:51 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=327</guid>
		<description><![CDATA[It&#8217;s not often I write a blog post just about a new tech.  For good reason:  there&#8217;s always someone out there prepared to dedicate a bit of time to a full tutorial on implementation, pros and cons. I&#8217;d just like to mention Typekit because it&#8217;s not what I thought it was, and I&#8217;m quite impressed [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s not often I write a blog post just about a new tech.  For good reason:  there&#8217;s always someone out there prepared to dedicate a bit of time to a full tutorial on implementation, pros and cons.</p>
<p>I&#8217;d just like to mention Typekit because it&#8217;s not what I thought it was, and I&#8217;m quite impressed with what it is.</p>
<p>I&#8217;d assumed <a href="http://typekit.com" target="_blank">Typekit</a> was a solution to the technological problem of including new fonts on webpages.  Along similar lines to Cufon and sIFR.</p>
<p>For those who don&#8217;t know, including fonts is a problem because modern browsers haven&#8217;t agreed on a way to distribute fonts with web pages.  IE, oddly, had this solved ages ago, but since MS is so unfashionable at present, it has been largely ignored.</p>
<p>Cufon lets you use a new font by replacing text with little &lt;canvas&gt; image elements.  Ingenious, but quite silly as you have to create loads of these little elements on the fly.  SIFR does the same thing, but using Flash.  Indeed, we once used sIFR for the Cannes Film Festival website.  I remember a very long page-render time&#8230;</p>
<p>I&#8217;d assumed Typekit was going to be similar.  Basically, I&#8217;m wrong.</p>
<p><strong>Typekit is a solution for font licensing</strong>.  It&#8217;s always been a pain to get the right licenses for fonts.  Often the licenses you buy are both extortionately priced and not licensed to web distribution.  (This is because the way you provide a font to a web page is easily hijacked, and the font can be stolen).</p>
<p>Typekit provides access to their sensibly-sized library of fonts on a variety of subscription schemes.  Once signed up, including the fonts is a simple matter of a few lines of JavaScript.</p>
<p>Technically, Typekit uses the @font-face download rules.  This means it supports a limited number of browsers.  But it also supports IE, using the EOT font format (IE&#8217;s system attempts to prevent hijacking of fonts too).  So it&#8217;s not bad.</p>
<p>Downsides are that the iPhone is not supported, as mobile Safari doesn&#8217;t seem to support @font-face.  Of course, the non-JavaScript browsers out there don&#8217;t see the new fonts (but they&#8217;ve asked for a lesser experience, so who cares).  Also, there can be a FOUC (Flash of Unstyled Content) as the page loads the first time.</p>
<p>Once request I&#8217;d make to the Typekit team is the ability to support two sites on the Tester pack.  One for my simple blog, one for active development.  I know you want me to pay, but my commitment to fonts for personal use is not so great, and since you add your logo, it&#8217;s only an advert for yourselves anyway isn&#8217;t it.</p>
<p>In summary, I think it&#8217;s a great idea.  I see Typekit as the Spotify of fonts.</p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2010/01/typekit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Leaving Digitas</title>
		<link>http://kenneth.kufluk.com/blog/2009/12/leaving-digitas/</link>
		<comments>http://kenneth.kufluk.com/blog/2009/12/leaving-digitas/#comments</comments>
		<pubDate>Sun, 20 Dec 2009 15:19:24 +0000</pubDate>
		<dc:creator>Kenneth</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Work]]></category>

		<guid isPermaLink="false">http://kenneth.kufluk.com/blog/?p=313</guid>
		<description><![CDATA[I&#8217;ve been at Digitas for five years. Five years. As I&#8217;ve recently learned, that&#8217;s about thirty-five dog years. Which is about four years older than I am now. Here, I&#8217;ve worked on websites for Hewlett Packard, the Cannes Film Festival, Persil, Nicquitin, General Motors, Opel, Vauxhall, Sega&#8216;s TotalWar, Nakheel and the Palm Jumeirah, Bayer&#8216;s Xarelto, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been at Digitas for <span style="font-size: x-large;">five</span> years.  <em><span style="font-size: xx-large;">Five years.</span></em>  As I&#8217;ve recently learned, that&#8217;s about <span style="font-size: x-large;">thirty-five</span> <span style="text-decoration: underline;"><span style="font-size: x-large;">dog years</span></span>.  Which is about <span style="font-size: x-large;">four</span> years older than I am now.</p>
<p>Here, I&#8217;ve worked on websites for <span style="font-size: x-large;">Hewlett Packard</span>, the <span style="font-size: x-large;">Cannes Film Festival</span>, <span style="font-size: large;">Persil</span>, Nicquitin, <span style="font-size: x-large;">General Motors</span>, Opel, Vauxhall, <span style="font-size: x-large;">Sega</span>&#8216;s <span style="font-size: large;">TotalWar</span>, <span style="font-size: x-large;"> Nakheel</span> and the <span style="font-size: x-large;">Palm Jumeirah</span>, <span style="font-size: x-large;">Bayer</span>&#8216;s <span style="font-size: large;">Xarelto</span>, MSD&#8217;s Propecia, <span style="font-size: x-large;">Procter &amp; Gamble</span>, <span style="font-size: x-large;">Eukanuba</span>, <span style="font-size: x-large;">Digitas of course</span>, and a few sites so cool, I still <em>can&#8217;t tell you about them</em>.<br />
That&#8217;s <span style="text-decoration: underline;"><span style="font-size: xx-large;">not a bad record</span></span>.<br />
Estimates suggest that <em>on average*</em>, I have been involved with more than <span style="font-size: x-large;">seventeen percent</span> of <em><span style="text-decoration: underline;"> your household purchases</span></em> , and although I have a three-percent chance of <em>killing you with a car</em>, there&#8217;s an extra <span style="font-size: xx-large;"> four percent chance </span> of <em><span style="font-size: xx-large;">saving your life</span></em> after an operation.</p>
<p>Somehow I&#8217;ve been sent to Brussels, Warsaw, Geneva, <strong>Minsk</strong>, Basle, Kiev, Zurich, Paris and <strong>Bangalore</strong>.</p>
<p><span style="font-size: xx-large;">Ninety-two</span> of my <em><span style="font-size: large;"> facebook friends</span></em>, I met through Digitas.  That&#8217;s <span style="font-size: x-large;"> sixty-one percent</span>.</p>
<p>Since I joined Digitas, I&#8217;ve started and finished a<em> </em><em><span style="font-size: large;"> three-year degree</span></em> in Physical Science, with a <span style="font-size: xx-large;"> two:one</span>. I&#8217;ve got married, and<em> </em><em><span style="font-size: xx-large;"> twenty-five percent</span></em> of my guests were from Digitas. I&#8217;ve run <span style="font-size: xx-large;"> twenty-six point one</span> miles in the London marathon, and they helped me raise <em><span style="font-size: xx-large;"> three-hundred</span></em><em> </em><em><span style="font-size: xx-large;"> pounds</span></em><span style="font-size: xx-large;"> </span> for the NSPCC.</p>
<p>I loved working here.<br />
I wish them all a great 2010.</p>
<p><span style="font-size: x-large;">Kenneth</span></p>
<p><span style="font-size: small;">* some estimates may be approximate to the point of complete fiction.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://kenneth.kufluk.com/blog/2009/12/leaving-digitas/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
