Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Asynchronous Python and You

313 views

Published on

Why and when you should use aynchronous Python plus how AddShoppers uses it.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Asynchronous Python and You

  1. 1. Asynchronous Python and You A guide by Dayne Jones
  2. 2. dir(Dayne) I’m Dayne. I try to make martech better.
  3. 3. Itinerary I. A bit about AddShoppers II. What is asynchronous programming? A. In general B. In python III. Why does it matter? A. Are there benefits? B. Is it worth using? IV. How do I use it? A. Which libraries should I use? B. What kind of support can I expect? V. Demo
  4. 4. How are we using asynchronous Python at AddShoppers?
  5. 5. AddShoppers ● ### million requests per day, #### rps ● Hanes, American Giant, Jockey, more ● ## app servers (all tornado), ## db, ## cache, ## queue ● ## data pipeline servicing ~ ### rps (asyncio) ● Hiring? Yes, please.
  6. 6. What is asynchronous programming anyways?
  7. 7. Sync vs. Async ● Synchronous code waits to return ● Asynchronous code returns immediately # synchronous response = requests.get(url) print(response) # asynchronous def print_resp(response): print(response) async_requests.get(url, callback=print_resp)
  8. 8. How does it work?
  9. 9. Event Loopdef start(self): [...] try: while True: [...] while self._events: fd, events = self._events.popitem() try: fd_obj, handler_func = self._handlers[fd] handler_func(fd_obj, events) except (OSError, IOError) as e: [...] except Exception: self.handle_callback_exception(self._handlers.get(fd)) fd_obj = handler_func = None Event Loop
  10. 10. def sync_handler(url): response = requests.get(url) # takes 1 second return response.status_code def async_handler(url): def print_resp(response): return response.status_code async_requests.get(url, callback=print_resp) # takes 1 second
  11. 11. Synchronous Asynchronous 1 Thread Available 1s 2s 3s 4s 5s 6s 1 Request 1s 2s 3s 4s 5s 6s
  12. 12. Synchronous Asynchronous 5 Threads Available 1s 2s 3s 4s 5s 6s 1 Request 1s 2s 3s 4s 5s 6s
  13. 13. Event-loop (continued) ● The CPU very often context switches at non deterministic intervals. ● Allows the programmer to mark context switches ● Single threaded (don’t use synchronous code in your thread)
  14. 14. Common Patterns
  15. 15. class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("http://my-api.com/v2/users", callback=self.on_response) def on_response(self, response): if response.error: raise tornado.web.HTTPError(500) json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + "users from the API") self.finish() Callbacks
  16. 16. Futures/Coroutines class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http = tornado.httpclient.AsyncHTTPClient() response = yield http.fetch("http://my-api.com/v2/users") json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " users from the API")
  17. 17. # Python 3.5 async def fetch_coroutine(url): response = await some_async_function() return response.body # Tornado 4.3, Python 3.5 async def fetch_coroutine(url): http_client = AsyncHTTPClient() response = await http_client.fetch(url) return response.body Async/Await
  18. 18. Why do I want to use asynchronous python?
  19. 19. # synchronous response = requests.get(url) # takes 1 second print(response) # asynchronous def print_resp(response): print(response) async_requests.get(url, callback=print_resp) # still takes 1 second Efficiency
  20. 20. Real-time web ● Long lived connections ● Mostly idle
  21. 21. Common Use Cases ● Real-time web. Anything that requires long lived, mostly idle connections from users. ● Network I/O. Waiting for HTTP requests to return. ● Database calls. Waiting for a database to return a query. ● Filesystem. Waiting for a disk. ● Anything else non CPU bound.
  22. 22. How do I write asynchronous programs in Python?
  23. 23. Libraries ● Tornado Web ● asyncio ● Twisted (Event driven networking engine) ● Gevent (Networking library) ● Cyclone (Tornado API on top of Twisted)
  24. 24. Python 2.x, <= 3.3 ● Tornado ○ Callbacks ○ Coroutines (generators) ● Gevent ● Twisted ● Cyclone # Python 2 class MyHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http_client = AsyncHTTPClient() response = yield http_client.fetch(“http://url.com") return response
  25. 25. Python >= 3.3, < 3.5 ● Tornado ○ Asyncio bridge library ○ Callbacks ○ Coroutines ● Asyncio ○ Coroutines (generators) # Tornado class MyHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http_client = AsyncHTTPClient() response = yield http_client.fetch(“http://url.com" return response # asyncio import asyncio import aiohttp @asyncio.coroutine def fetch_page(url): response = yield from aiohttp.request('GET', url) assert response.status == 200 content = yield from response.read() print('URL: {0}: Content: {1}'.format(url, content))
  26. 26. Python >= 3.5 ● Tornado ○ async/await ● Asyncio ○ async/await # Tornado class MyHandler(tornado.web.RequestHandler): async def get(self): http_client = AsyncHTTPClient() url = 'http://myurl.com' response = await http_client.fetch(url) return response # asyncio import asyncio import aiohttp @asyncio.coroutine def fetch_page(url): response = yield from aiohttp.request('GET', url) assert response.status == 200 content = yield from response.read() print('URL: {0}: Content: {1}'.format(url, content))
  27. 27. Miscellaneous ● Database driver (motor, Momoko) ● Redis ● Reading files
  28. 28. DEMO
  29. 29. Questions?

×