api-centric web development with tornado or the great refactoring story
TRANSCRIPT
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!