Agenda

In a comfy chair?

Good! Let's get going.

Google Cloud Platform

  • Google App Engine - Host your applications
  • Google Compute Engine - Host your Virtual Machines
  • Google Cloud Storage - Host your files
  • Google Cloud SQL - Host your databases
  • Google BigQuery - Analyze data
  • Google Cloud Datastore
  • Details at cloud.google.com

Google App Engine

  • A web application platform (PaaS)
    • Create applications in Java, Python, Go or PHP
    • Store data in the cloud
  • A set of APIs, including
    • Data Storage
    • Task Queues
    • Users
  • Easy to
    • Build and Deploy
    • Manage
    • Scale
  • Great for startups: start free and scale as you need it
  • Details at developers.google.com/appengine

Developing with App Engine

Step 1: Get the Google App Engine SDK

  • Provides libraries for APIs and utilities for deploying

Step 2: Write code

  • Handling requests
  • Managing data
  • App Engine APIs reduce development time
  • Git push-to-deploy capability for Python and PHP apps

Step 3: Deploy and test locally

  • Fully functional local development server

Step 4: Deploy to App Engine

Always remember: developers.google.com/appengine

Google Developer Groups

  • Developers who are interested in Google's developer technology
  • 3 in South Africa: Cape Town, Johannesburg, Pretoria
  • Find out more at lnkr.co.za/gdg-za

Who uses Google Cloud Platform?

The life of an App Engine request

That's an intro to App Engine

Onto a real-world application built on App Engine

Meet lnkr

  • It's a Link Shortener: lnkr.co.za
  • Built in Python on Google App Engine
  • There is an API for managing Short Links
  • There is a Web and Android client
  • Social Integration with Google+

You're probably wondering

How does a link shortener work?!

  • 6 characters long
  • 63 characters in alphabet. Only a-z, A-Z, 0-9 and - are allowed
  • So 636 = 62 523 502 209 possible short links

The alogrithm

  • Generate 1 short link of 6 characters
    • For each of the 6 positions, choose at random from the alphabet
  • Check for uniqueness
    • Request 10 short links
    • Check all 10 against the datastore
    • If there are less than 10 collisions, choose one at random from the non-colliding ones
    • If there are 10 collisions, get another 10 random links and repeat
    • If there are still 10 collisions, fail and complain that there is not enough entropy in the world.

But I want MOAR!!1!

  • Increase the length of the short link
    • Length of 7 has 3 938 980 639 167 possible links
    • Length of 8 has 248 155 780 267 521 possible links
  • Increase the alphabet size - adding 2 characters gives you 13 billion more
  • Try harder - don't give up after 2 tries

Application architecture

App Engine features used by lnkr

  • Modules - code is split into modules for scalability
  • Memcache - we use cache for link usage as well as statistics
  • Tasks - we use deferred tasks to update metrics
  • Google Cloud Endpoints - single backend API, web and mobile frontends
  • Datastore - Data are stored in the cloud
  • Custom Google Apps Domain
    • Appspot domain gives me avian-silo-347.appspot.com
    • lnkr.co.za is easier to remember, so I mapped the domains

Modules

  • api
    • the API for managing short links
  • static
    • static files (HTML, CSS, JS)
  • data
    • data gathering code
  • default
    • The link forwarding code
  • Addressed via version.module.applicationID.appspot.com
  • 1 YAML file per module
  • 1 Dispatch YAML file for routing to modules

Why so many modules?

  • Each module has its own versions and instances
  • Split into logical units that can scale individually
  • You can manage the instances available to each
  • Scenarios:
    • More web queries: increase static resources
    • More clients: increase api resources
    • More data consumers: increase data resources
    • More users of short links: increase default resources

Memcache

  • Use in-memory cache for
    • short links that are accessed frequently
    • statistics data

Tasks

  • We want to return to the user as fast as possible
  • 60 second limit for regular requests
  • 10 minute limit for deferred tasks
  • Defer tasks and run them in the background after return to user
deferred.defer(utils.updateMetric, 'create', 
               os.getenv('HTTP_X_APPENGINE_COUNTRY'))
      

Google Cloud Endpoints

  • You write code to handle requests
  • You annotate your code as an API and API Methods
  • You generate client libraries for Java and iOS
  • You profit
  • You win
@endpoints.api(name='shortlink',version='v1', description='Short Link API')
class ShortLinkApi(remote.Service):

  @endpoints.method(CreateShortLinkMessageRequest,
                    CreateShortLinkMessageResponse,
                    name='create',
                    path= '/create',
                    http_method = 'POST')
  def createShortLink(self, request):
    link = request.short_link
     
  • REST API at /_ah/api/shortlink/v1/create

Datastore

  • A non-relational schema-less database
  • Only 2 entities in this application
class ShortLink(ndb.Model):
  """Models a short URL: short link, target, date created."""
  short_link = ndb.StringProperty()
  created_date = ndb.IntegerProperty()
  target_link = ndb.StringProperty()


class MetricCounter(ndb.Model):
  """A metric."""
  name = ndb.StringProperty()
  country = ndb.StringProperty()
  value = ndb.IntegerProperty()
  day = ndb.IntegerProperty()

Datastore code

Write to Datastore

link = ShortLink(parent = pk, short_link = sl,
         target_link = request.target_link,
         created_date = long(time.time()*1000))
link.put()
      

Transactions

@ndb.transactional
def incrementCounter(key):
  obj = key.get()
  obj.value += 1
  obj.put()
      

And now for something completely different.

An interlude

Back to work!

Back(end) to Front(end)

Lnkr website

Features of the web frontend

  • Consumes the Cloud Endpoints API
  • HTML5
  • SASS
  • CSS3
  • Responsive design
  • Integration with Google+
  • Google Chart API

The Cloud Endpoints API

Step 1: Load the library for the API

<script src="//apis.google.com/js/client.js?onload=init"></script>
<script>
var ROOT = '';
if (window.location.host.indexOf('localhost') == 0) {
  ROOT = 'http://' + window.location.host + '/_ah/api';
} else {
  ROOT = 'https://'+hostname+'/_ah/api';
}

gapi.client.load('shortlink', 'v1', function() {
  console.log('Loaded shortlink library');
}, ROOT);
      

Step 2: Call the API

var inObj = {'target_link': 'http://www.danielacton.com'};
gapi.client.shortlink.create(inObj).execute(function(resp) {
  // Do something with the response
});
</script>
     

HTML5

Using WebStorage

Specific to browser

if (window.localStorage) {
  var currentLinks = storage.getItem('shortlinks');
  if (currentLinks) {
    // Do something with currentLinks
  }
  storage.setItem('shortlinks', currentLinks);
}
      

SASS

  • Syntactically Awesome Style Sheets
  • sass-lang.com
  • Variables and Mixins (includes) in your stylesheets
  • CSS-like and compiles to valid CSS

Use-case: consistent colours

$dark-blue: #4884B2;
color: $dark-blue;
     

Use-case: vendor prefixes

@mixin box-shadow() {
  -moz-box-shadow:    2px 2px 2px 2px $mid-grey;
  -webkit-box-shadow: 2px 2px 2px 2px $mid-grey;
  box-shadow:         2px 2px 2px 2px $mid-grey;
}

@include box-shadow();
     

CSS3

Vendor prefixes omitted for brevity

  • Transitions
#someElement {
  transition: opacity 1s ease-in-out;
}
     
  • Gradient backgrounds
background: linear-gradient(top, $white, $mid-grey);
     
  • Bo Selektas
.grid-row:nth-child(even) {
  background: $white;
}
     

Responsive Design

  • Sizes in em as much as possible
  • Media Queries
@media all and (max-width: 420px) {
  .grid-caption {
    width: 360px;
  }
}
     

Integration with Google+

  • You're probably shortening in order to share
  • Use the Google+ share button
<a href="https://plus.google.com/share?url=http://lnkr.co.za/aa13"
            onclick="javascript:windowOpenText;">
            <img src="https://www.gstatic.com/images/icons/gplus-16.png"
            alt="Share on Google+"/>Share on</a>
     

Integration with Google+

Google Chart API

function drawVisualization() {
  var data = google.visualization.arrayToDataTable([
    ["country", "count"],
    ["ZA", 22],
    ["NG", 1],
    ["NL", 1],
    ["GB", 5],
    ["KE", 1],
    ["FR", 3]
  ]);

  var geochart = 
    new google.visualization.GeoChart(document.querySelector('#viz-use'));
  geochart.draw(data, {width: 556, height: 347});
};
     

Google Chart API

Web to mobile

Using the Shortlink API on Android

  • Step 1: Generate the endpoint library for Android
  • Step 2: Add the libraries to your project
  • In your app code:
    • Step 3: Initialise the service
    • Step 4: Use the service

Step 1: Generate the endpoint library

$ appengine-sdk-directory/endpointscfg.py \
      get_client_lib java [--hostname localhost] \
      your_module.YourServiceClass
      

If hostname not specified

  • Take from API specification
@endpoints.api(
...
hostname='your_app_id.appspot.com')
      
  • Or take from app.yaml
application: your_app_id
version: 1
runtime: python27
      

This will give you a zip file with all required libraries

Step 2: Add the libraries to your project

  • The zip file will have a lib directory. From there, take these files:
    • google-api-client-*.jar
    • google-api-client-android-*.jar
    • google-http-client-*.jar
    • google-http-client-android-*.jar
    • google-http-client-gson-*.jar
    • google-oauth-client-*.jar
    • gson-*.jar
    • guava-jdk5-*jar
    • jsr305-*.jar
  • It also contains source for your Endpoint helper classes
    • your_app_id_appspot_com-shortlink-v1-*-java-*-rc-source.jar

Step 3: Initialise the service

Shortlink service = new Shortlink.Builder(
    AndroidHttp.newCompatibleTransport(),
    new GsonFactory(),
    null);

service.setRootUrl("https://your_app_id.appspot.com/_ah/api/");
service.setServicePath("shortlink/v1/");
service.build();
      

Step 4: Use the service

List Short links

service.list().execute().getShortLinks()
      

Create a short link

ApiMessagesCreateShortLinkMessageRequest request =
    new ApiMessagesCreateShortLinkMessageRequest();

request.setTargetLink(targetUrl);
request.setShortLink(shortLink);

ApiMessagesCreateShortLinkMessageResponse response =
    service.create(request).execute();
      

A note on concurrency

A conundrum!

  • You shouldn't execute long-running tasks on the main UI thread
  • Only the main UI thread can update the UI
  • AsyncTask to the rescue
private class ListShortLinks extends AsyncTask {
  protected String doInBackground(String... params) {
    // Invoke the Shortlink API
    return aStringResult;
  }

  protected void onPostExecute(String result) {
    // Do something with the string result
  }

  protected void onPreExecute() {}
  protected void onProgressUpdate(Void... values) {}
}

// In your Activity
new ListShortLinks().execute("");
      

App screenshot - create

App screenshot - share

App screenshot - list

Back to front and front to back

In Summary

  • That's a sample App Engine application that
    • I'd actually use
    • Uses some neat features of App Engine
    • Has a web and mobile front-end
    • (Hopefully) scales automagically

Useful links

<Thank You!>