Upload
python-ireland
View
548
Download
0
Tags:
Embed Size (px)
Citation preview
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?