30
Google App Engine Writing a Stack Overflow clone on App Engine in under an hour* http://blog.bitquabit.com/2009/07/01/one-which-i-call-out-hacker-news/

Python Ireland Nov 2009 Talk - Appengine

Tags:

Embed Size (px)

Citation preview

Page 1: Python Ireland Nov 2009 Talk - Appengine

Google App Engine

Writing aStack Overflow clone

on App Enginein under an hour*

http://blog.bitquabit.com/2009/07/01/one-which-i-call-out-hacker-news/

Page 2: Python Ireland Nov 2009 Talk - Appengine

Agenda

1. App Engine Introduction2. Demo: Building an app3. Demo: Deploying an app4. Demo: Writing App Overflow5. App Engine's Services6. Questions

If I say "Cloud" at any point, Boo!

Page 3: Python Ireland Nov 2009 Talk - Appengine

App Engineintroduction

Page 4: Python Ireland Nov 2009 Talk - Appengine

Building web applications is hard

Page 5: Python Ireland Nov 2009 Talk - Appengine

Thinking about scalability

....the tools, platform and design don't matter too much

Just a few users....

Page 6: Python Ireland Nov 2009 Talk - Appengine

Thinking about scalability

....you must design for scalability

Lots and lots of users...

Page 7: Python Ireland Nov 2009 Talk - Appengine

WhiteHouse.gov/openforquestions

Page 8: Python Ireland Nov 2009 Talk - Appengine

Serving developers AND their customersGoogle App Engine

Scalable

High Performance

Standards

Cost effective

Page 9: Python Ireland Nov 2009 Talk - Appengine

The Components

Page 10: Python Ireland Nov 2009 Talk - Appengine

creative commons licensed photograph from cote

1. Scalable Serving Architecture

Page 11: Python Ireland Nov 2009 Talk - Appengine

App Engine Front End

App Engine Front End

App Engine Front End

Incoming Requests

AppServer AppServer AppServer

Load Balancer

1. Scalable Serving Architecture

Page 12: Python Ireland Nov 2009 Talk - Appengine

App Engine Front End

App Engine Front End

App Engine Front End

Incoming Requests

AppServer AppServer AppServer

Load Balancer

1. Scalable Serving Architecture

AppServer

API Layer

Other Google Infrastructure

- Bigtable

- Google Accounts

- Memcache

- Image manipulation

App App App

Page 13: Python Ireland Nov 2009 Talk - Appengine

Wow. That isone big table.

2. Distributed Datastore

The Datastore is...

Distributed

Transactional

Natively Partitioned

Hierarchial

Schemaless

Based on Bigtable

Page 14: Python Ireland Nov 2009 Talk - Appengine

Wow. That isone big table.

2. Distributed Datastore

The Datastore is not...

A relational database

A SQL engine

Just Bigtable

Page 15: Python Ireland Nov 2009 Talk - Appengine

Demo: Writing an app

Page 16: Python Ireland Nov 2009 Talk - Appengine

App configuration

application: appoverflowversion: 1runtime: pythonapi_version: 1

handlers:- url: /.* script: /request.py

Page 17: Python Ireland Nov 2009 Talk - Appengine

Request handling

class IndexHandler(webapp.RequestHandler): def render_template(self, name, values): path = os.path.join(os.path.dirname(__file__), 'templates', name) self.response.out.write(template.render(path, values))

def get(self): self.render_template('index.html', {})

application = webapp.WSGIApplication([ ('/', IndexHandler),], debug=True)

def main(): run_wsgi_app(application)

Page 18: Python Ireland Nov 2009 Talk - Appengine

Demo: Deploying an appIn 30 seconds or less. Time me!

Page 19: Python Ireland Nov 2009 Talk - Appengine

Demo: Writing App Overflow

Page 20: Python Ireland Nov 2009 Talk - Appengine

Adding authentication

class IndexHandler(webapp.RequestHandler): def __init__(self): self.user = users.get_current_user()

def render_template(self, name, values): url = self.request.url template_values.update({ 'user': self.user, 'login_url':users.create_login_url(url), 'logout_url':users.create_logout_url(url), }) path = os.path.join(os.path.dirname(__file__), 'templates', template_name) self.response.out.write(template.render(path, template_values))

Page 21: Python Ireland Nov 2009 Talk - Appengine

Adding authentication

<html><head> <title>{%block title%}App Overflow{%endblock%}</title> {% block head %}{% endblock %}</head><body> <div class="login"> {% if user %} Logged in as {{user.nickname}} | <a href="{{logout_url}}">Log Out</a> {% else %} <a href="{{login_url}}">Log In</a> {% endif %} </div> {% block body %}{% endblock %}</body></html>

Page 22: Python Ireland Nov 2009 Talk - Appengine

Storing data

from google.appengine.ext import db

class Question(db.Model): asker = db.UserProperty(required=True) title = db.StringProperty(required=True, indexed=False) body = db.TextProperty(required=True) asked_at = db.DateTimeProperty(auto_now_add=True)

Page 23: Python Ireland Nov 2009 Talk - Appengine

Storing data

class NewQuestionHandler(BaseHandler): def get(self): self.render_form(forms.QuestionForm())

def post(self): form = forms.QuestionForm(self.request.POST) if not form.is_valid(): self.render_form(form) return entity = models.Question( asker=self.user, **form.clean_data) entity.put() self.redirect('/questions/%d/' % (entity.key().id(),))

Page 24: Python Ireland Nov 2009 Talk - Appengine

Fetching data

class QuestionHandler(BaseHandler): def get_question(self, id): question = models.Question.get_by_id(int(id)) return question def render_page(self, question): template_values = { 'question': question, } self.render_template('question.html', template_values) def get(self, id): question = self.get_question(id) if question: self.render_page(question)

Page 25: Python Ireland Nov 2009 Talk - Appengine

Queries

class IndexHandler(BaseHandler): def get_questions(self): q = models.Question.all() q.order('-asked_at') return q.fetch(20)

def render_page(self): template_values = { 'questions': self.get_questions(), } self.render_template('index.html', template_values)

def get(self): self.render_page()

Page 26: Python Ireland Nov 2009 Talk - Appengine

Precomputation

class Question(db.Model): asker = db.UserProperty(required=True) title = db.StringProperty(required=True, indexed=False) body = db.TextProperty(required=True) asked_at = db.DateTimeProperty(auto_now_add=True) answer_count = db.IntegerProperty(required=True, default=0)

class Answer(db.Model): answerer = db.UserProperty(required=True) body = db.TextProperty(required=True) answered_at = db.DateTimeProperty(auto_now_add=True)

Page 27: Python Ireland Nov 2009 Talk - Appengine

Transactions

answer = models.Answer( parent=question, answerer=self.user, # ...)

def save_answer(answer): def _tx(): question = db.get(answer.parent().key()) question.answer_count += 1 db.put([answer, question]) return answer.key() return db.run_in_transaction(_tx)

Page 28: Python Ireland Nov 2009 Talk - Appengine

XMPP

class XmppHandler(xmpp_handlers.CommandHandler): def ask_command(self, message=None): question = models.Question( title=message.arg, body=message.arg, sender=message.sender) question.put() message.reply('Your question has been received. You will be pinged when an answer is submitted.')

# Elsewhere...

xmpp.send_message([question.sender], 'Answer: ' + answer.body)

Page 29: Python Ireland Nov 2009 Talk - Appengine

Additional services / APIs

URL FetchMemcacheMail - incoming and outgoingImagesGoogle AccountsCron supportTask Queue

Page 30: Python Ireland Nov 2009 Talk - Appengine

Questions?