Cloud World

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Wednesday, 23 February 2011

Pushing Updates with the Channel API

Posted on 15:48 by Unknown

If you've been watching Best Buy closely, you already know that Best Buy is constantly trying to come up with new and creative ways to use App Engine to engage with their customers. In this guest blog post, Luke Francl, BBYOpen Developer, was kind enough to share with us Best Buy's latest App Engine project.







As part of Best Buy's Connected Store initiative, we have placed QR codes on our product information Fact Tags, in addition to the standard pricing and product descriptions already printed there. When a customer uses the Best Buy app, or any other QR code scanner, they are shown the product details for the product they have scanned, powered by the BBYOpen API or the m.bestbuy.com platform.



To track what stores and products are most popular, QR codes are also encoded with the store number. My project at Best Buy has been to analyze these scans and make new landing pages for QR codes easier to create.



Since we have the geo-location of the stores and product details from our API, it is a natural fit to display these scans on a map. We implemented an initial version of this idea, which used polling to visualize recent scans. To take our this a step further, we thought it would be exciting to use the recently launched App Engine Channel API to update our map in real-time.








Our biggest challenge was pushing the updates to multiple browsers, since we'd most certainly have more than one user at a time looking at our map. The Channel API does not currently support broadcasting a single update to many connected clients. In order to broadcast updates to multiple users, our solution was to keep a list of client IDs and send an update message to each of them.



To implement this, we decided to store the list of active channels in memcache. This solution is not ideal as there are race conditions when we modify the list of client IDs. However, it works well for our demo.



Here’s how we got it working. The code has been slightly simplified for clarity, including removing the rate limiting that we do. To play with a working demo, check out the channel-map-demo project from GitHub.



As customers in our stores scan QR codes, those scans are recorded by enqueuing a deferred. We defer all writes so we can return a response to the client as quickly as possible.



In the deferred, we call a function to push the message to all the active channels (see full source).




def push_to_channels(scan):
content = '<div class="infowindowcontent">(...) </div>' % {
'product_name': scan.product.name,
'timestamp' : scan.timestamp.strftime('%I:%M %p'),
'store_name': scan.store.name,
'state': scan.store.state,
'image': scan.product.image }

message = {'lat': scan.store.lat,
'lon': scan.store.lon,
'content': content}

channels = simplejson.loads(memcache.get('channels') or '{}')

for channel_id in channels.iterkeys():
encoded_message = simplejson.dumps(message)

channel.send_message(channel_id, encoded_message)


The message is a JSON data structure containing the latitude and longitude of the store where the scan occurred, plus a snippet of HTML to display in an InfoWindow on the map. The product information (such as name and thumbnail image) comes from our BBYOpen Products API.



Then, when a user opens up the site and requests the map page, we create a channel, add it to the serialized channels Python dictionary, stored in memcache, and pass the token back to the client (see full source).




channel_id = uuid.uuid4().hex
token = channel.create_channel(channel_id)

channels = simplejson.loads(memcache.get('channels') or '{}')

channels[channel_id] = str(datetime.now())

memcache.set('channels', simplejson.dumps(channels))


On the map page, JavaScript creates a Google Maps map and uses the token to open a channel. When the onMessage callback is called by the Channel API, a new InfoWindow is displayed on the map using the HTML content and latitude and longitude in the message (see full source).




function onMessage(message) {
var scan = JSON.parse(message.data);

var infoWindow = new google.maps.InfoWindow(
{content: scan.content,
disableAutoPan: true,
position: new google.maps.LatLng(scan.lat, scan.lon)});

infoWindow.open(map);
setTimeout(function() { infoWindow.close(); }, 10000);
};


Finally, since channels can only be open for two hours, we have a cron job that runs once an hour to remove old channels. Before deleting the client ID, a message is sent on the channel which triggers code in the JavaScript onMessage function to reload the page, thus giving it a new channel and client ID (see full source).



You can see the end result on our map, watch a video about the BBYScan project or checkout the sample channel-map-demo project and create your own Channel API based application.



Posted by Fred Sauer, App Engine Team
Email ThisBlogThis!Share to XShare to Facebook
Posted in | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Bridging Mobile Backend as a Service to Enterprise Systems with Google App Engine and Kinvey
    The following post was contributed by Ivan Stoyanov , VP of Engineering for Kinvey, a mobile Backend as a Service provider and Google Cloud ...
  • Tutorial: Adding a cloud backend to your application with Android Studio
    Android Studio lets you easily add a cloud backend to your application, right from your IDE. A backend allows you to implement functionality...
  • 2013 Year in review: topping 100,000 requests-per-second
    2013 was a busy year for Google Cloud Platform. Watch this space: each day, a different Googler who works on Cloud Platform will be sharing ...
  • Easy Performance Profiling with Appstats
    Since App Engine debuted 2 years ago, we’ve written extensively about best practices for writing scalable apps on App Engine. We make writ...
  • TweetDeck and Google App Engine: A Match Made in the Cloud
    I'm Reza and work in London, UK for a startup called TweetDeck . Our vision is to develop the best tools to manage and filter real time ...
  • Scaling with the Kindle Fire
    Today’s blog post comes to us from Greg Bayer of Pulse , a popular news reading application for iPhone, iPad and Android devices. Pulse has ...
  • Who's at Google I/O: Mojo Helpdesk
    This post is part of Who's at Google I/O , a series of guest blog posts written by developers who are appearing in the Developer Sandbox...
  • A Day in the Cloud, new articles on scaling, and fresh open source projects for App Engine
    The latest release of Python SDK 1.2.3, which introduced the Task Queue API and integrated support for Django 1.0, may have received a lot ...
  • SendGrid gives App Engine developers a simple way of sending transactional email
    Today’s guest post is from Adam DuVander, Developer Communications Director at SendGrid. SendGrid is a cloud-based email service that deliv...
  • Qubole helps you run Hadoop on Google Compute Engine
    This guest post comes form Praveen Seluka, Software Engineer at Qubole, a leading provider of Hadoop-as-a-service.  Qubole is a leading pr...

Categories

  • 1.1.2
  • agile
  • android
  • Announcements
  • api
  • app engine
  • appengine
  • batch
  • bicycle
  • bigquery
  • canoe
  • casestudy
  • cloud
  • Cloud Datastore
  • cloud endpoints
  • cloud sql
  • cloud storage
  • cloud-storage
  • community
  • Compute Engine
  • conferences
  • customer
  • datastore
  • delete
  • developer days
  • developer-insights
  • devfests
  • django
  • email
  • entity group
  • events
  • getting started
  • google
  • googlenew
  • gps
  • green
  • Guest Blog
  • hadoop
  • html5
  • index
  • io2010
  • IO2013
  • java
  • kaazing
  • location
  • mapreduce
  • norex
  • open source
  • partner
  • payment
  • paypal
  • pipeline
  • put
  • python
  • rental
  • research project
  • solutions
  • support
  • sustainability
  • taskqueue
  • technical
  • toolkit
  • twilio
  • video
  • websockets
  • workflows

Blog Archive

  • ►  2013 (143)
    • ►  December (33)
    • ►  November (15)
    • ►  October (17)
    • ►  September (13)
    • ►  August (4)
    • ►  July (15)
    • ►  June (12)
    • ►  May (15)
    • ►  April (4)
    • ►  March (4)
    • ►  February (9)
    • ►  January (2)
  • ►  2012 (43)
    • ►  December (2)
    • ►  November (2)
    • ►  October (8)
    • ►  September (2)
    • ►  August (3)
    • ►  July (4)
    • ►  June (2)
    • ►  May (3)
    • ►  April (4)
    • ►  March (5)
    • ►  February (3)
    • ►  January (5)
  • ▼  2011 (46)
    • ►  December (3)
    • ►  November (4)
    • ►  October (4)
    • ►  September (5)
    • ►  August (3)
    • ►  July (4)
    • ►  June (3)
    • ►  May (8)
    • ►  April (2)
    • ►  March (5)
    • ▼  February (3)
      • Pushing Updates with the Channel API
      • App Engine 1.4.2 SDK - API Updates and Additions E...
      • Beauty On the Outside, High Replication on the Inside
    • ►  January (2)
  • ►  2010 (38)
    • ►  December (2)
    • ►  October (2)
    • ►  September (1)
    • ►  August (5)
    • ►  July (5)
    • ►  June (6)
    • ►  May (3)
    • ►  April (5)
    • ►  March (5)
    • ►  February (2)
    • ►  January (2)
  • ►  2009 (47)
    • ►  December (4)
    • ►  November (3)
    • ►  October (6)
    • ►  September (5)
    • ►  August (3)
    • ►  July (3)
    • ►  June (4)
    • ►  May (3)
    • ►  April (5)
    • ►  March (3)
    • ►  February (7)
    • ►  January (1)
  • ►  2008 (46)
    • ►  December (4)
    • ►  November (3)
    • ►  October (10)
    • ►  September (5)
    • ►  August (6)
    • ►  July (4)
    • ►  June (2)
    • ►  May (5)
    • ►  April (7)
Powered by Blogger.

About Me

Unknown
View my complete profile