API-centric Web Development with Tornado or The Great Refactoring Story

Post on 12-Aug-2015

59 views 0 download

Transcript of API-centric Web Development with Tornado or The Great Refactoring Story

API-centric Web Development with Tornado or The Great Refactoring Story

Roman Zaiev, special for

Tornado? Why not Django / Flask / etc

is

is prototyping tool

djangoproject/ manage.py project/ __init__.py urls.py wsgi.py settings/ __init__.py base.py dev.py prod.py blog/ __init__.py models.py managers.py views.py urls.py templates/ blog/ base.html list.html detail.html static/ … tests/ __init__.py test_models.py test_managers.py test_views.py static/ css/ … js/ … templates/ base.html index.html requirements/ base.txt dev.txt test.txt prod.txt

Django Project Structure

Modern Django Admin

Django ORM Hell

Django NoSQL? No. SQL!

RedirectView

TemplateView

DetailView

UpdateView

CreateView

FormView

DeleteView

DateDetailView

ListView

DayArchiveView

ArchiveIndexView

TodayArchiveView

MonthArchiveView

YearArchiveView

WeekArchiveView

BaseUpdateView

BaseCreateView

BaseFormView

SingleObjectTemplateResponseMixin

BaseListView

BaseArchiveIndexView

MultipleObjectTemplateResponseMixin

BaseTodayArchiveView

ModelFormMixin

ProcessFormMixin

TemplateResponseMixin

BaseDeleteView

BaseDateDetailView

BaseDayArchiveView

BaseMonthArchiveView

BaseYearArchiveView

BaseWeekArchiveView

FormMixin

DeletionMixin

BaseDetailView

SingleObjectMixin

MultipleObjectMixin

ContextMixin

DateMixin

BaseDateListView

WeekMixin| connect the dotsCBV

Kill meWTF?!

Django is bad

Monolith is bad

first steprefactoring

auth

landingsbasketcheckoutproduct

second stepAPI

emails

API to rule them all

ADD. API Driven Development

POST /api/v1/authorize GET /api/v1/user GET /api/v1/product

POST /users/ajax_user_auth GET /products/ajax_current_user GET /products/ajax_prod_info

API Business logic

Templates rendering SEO Urls

Mob

WebUI AJAX

UI API Calls

Front

Mob

API

Templates rendering API Calls SEO Urls

Web UI AJAX

POST /api/v1/authorize GET /api/v1/user GET /api/v1/product

UI API Calls

API

Monolith Services

A A

A A

A A

A A

A A

A

AA A

A A

A A

A

A

A

A

A A A A

auth

product landings

A A

A A

basket

A

P

T

API

Stack Choice

framework? transport? proxy?

PTA

A framework

Flask? Yes. But no.

Easy Light Fast Async Python 2.7

Django

Flask

Pyramid

Tornado

A framework

tornadoweb.org

A framework

import tornado.ioloop import tornado.web

class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")

application = tornado.web.Application([ (r"/", MainHandler), ])

if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.current().start()

A Tornado | Start

A Tornado | Async handler

class GenAsyncHandler(tornado.web.RequestHandler):

@gen.coroutine def get(self): http_client = AsyncHTTPClient() response = yield http_client.fetch("http://async.ua") do_something_with_response(response) self.render("template.html")

PT

Tornado transport? proxy?

transport

ZeroMQ + Protocol Buffers

T

ProtobufT

message BannerItem { required string link = 1; required string image = 2; optional string title = 3; required string alt = 4; required string selector = 5; required string last_edited = 6; required string last_editor = 7; optional string date_start = 8; optional string date_end = 9; optional int32 order = 10; }

‘\n\x01/\x12\x07img.png\x1a\x04Frau "\x06Muller*\x07.snoopy2\tyesterday:\x02me'

new_banner = banner_pb2.BannerItem() new_banner.link = ‘/' new_banner.image = ‘img.png' new_banner.title = ‘Frau' new_banner.alt = ‘Muller' new_banner.selector = ‘.snoopy' new_banner.last_edited = ‘yesterday' new_banner.last_editor = ‘me'

new_banner.SerializeToString()

P

Tornado ZeroMQ proxy?

proxyP

Nginx

Tornado ZeroMQ Nginx

auth

product landings

basket

API

API

Monolith Services

API Front

A Tornado | API HTTP

class ProductAPIHandler(BaseAPIHandler):

def get(self): user_attrs = self.request.arguments pbf_response = get_product(self, **user_attrs) response = protobuf_to_dict( pbf_response, ignore_list=['body'] ) if pbf_response.success: product = product_pb2.ProductSample() product.ParseFromString(pbf_response.body) response['body'] = protobuf_to_dict(product)

self.write(response)

A Tornado | API ZeroMQ

class ProductZMQHandler(BaseZMQHandler):

def get(self): user_attrs = self.request.arguments pbf_response = get_product(self, **user_attrs) return pbf_response

A Tornado | API Call

class ProductHandler(BaseHandler): TEMPLATE = 'pdp.html'

@tornado.web.asynchronous def get(self, slug): zmq_stream = get_zmq_client(self) requests = [{ 'url': '/api/v1/product', 'callback': self.gen_product, 'data': { 'url': slug }, }] self.pbc_client( zmq_stream=zmq_stream, requests=requests, callback=self.on_fetch, ) ...

A Tornado | API Call Processing

class ProductHandler(BaseHandler):

...

def gen_product(self, response): if not response.success: raise HTTPError(404)

product_pbc = product_pb2.ProductSample() product_pbc.ParseFromString(response.body) product_dict = protobuf_to_dict(product_pbc)

self.context['product'] = product_dict

def on_fetch(self): self.render(self.TEMPLATE, **self.context)

Find your balance

Use proper technologies

github.com/semirookua.linkedin.com/in/semirook

Thank you! And good luck!