Python Ireland Nov 2009 Talk - Appengine

Post on 17-May-2015

549 views 0 download

Tags:

Transcript of 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/

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!

App Engineintroduction

Building web applications is hard

Thinking about scalability

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

Just a few users....

Thinking about scalability

....you must design for scalability

Lots and lots of users...

WhiteHouse.gov/openforquestions

Serving developers AND their customersGoogle App Engine

Scalable

High Performance

Standards

Cost effective

The Components

creative commons licensed photograph from cote

1. Scalable Serving Architecture

App Engine Front End

App Engine Front End

App Engine Front End

Incoming Requests

AppServer AppServer AppServer

Load Balancer

1. Scalable Serving Architecture

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

Wow. That isone big table.

2. Distributed Datastore

The Datastore is...

Distributed

Transactional

Natively Partitioned

Hierarchial

Schemaless

Based on Bigtable

Wow. That isone big table.

2. Distributed Datastore

The Datastore is not...

A relational database

A SQL engine

Just Bigtable

Demo: Writing an app

App configuration

application: appoverflowversion: 1runtime: pythonapi_version: 1

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

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)

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

Demo: Writing App Overflow

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))

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>

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)

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(),))

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)

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()

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)

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)

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)

Additional services / APIs

URL FetchMemcacheMail - incoming and outgoingImagesGoogle AccountsCron supportTask Queue

Questions?