Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Python, do you even async?

3 979 vues

Publié le

Talk given at DomCode meetup in Utrecht (August 2014) on different frameworks to do asynchronous I/O y Python, with a strong focus on asyncio (PEP-3156).

Publié dans : Technologie
  • Soyez le premier à commenter

Python, do you even async?

  1. 1. Python, do you even async? Saúl Ibarra Corretgé @saghul DomCode, August 2014
  2. 2. repr(self) >>> from Amsterdam import saghul >>> >>> saghul.work() VoIP, SIP, XMPP, chat, Real Time Communications >>> >>> saghul.other() networking, event loops, sockets, MOAR PYTHON >>> >>> saghul.languages() Python, C >>>
  3. 3. github.com/saghul
  4. 4. Please give feedback! https://joind.in/event/view/2471
  5. 5. The road to asynchronous i/o
  6. 6. import socket import socket ! server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(511) print("Server listening on: {}".format(server.getsockname())) ! client, addr = server.accept() print("Client connected: {}".format(addr)) ! while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data) ! server.close()
  7. 7. except Exception: • We can only handle one client at a time! • Solutions for handling multiple clients • Threads • I/O multiplexing • Check the C10K problem if you haven’t already! • http://www.kegel.com/c10k.html
  8. 8. try: sockets + threads import socket import thread ! def handle_client(client, addr): print("Client connected: {}".format(addr)) while True: data = client.recv(4096) if not data: print("Client has disconnected") break client.send(data.upper()) ! server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(511) print("Server listening on: {}".format(server.getsockname())) ! while True: client, addr = server.accept() thread.start_new_thread(handle_client, (client, addr))
  9. 9. except Exception: • Threads have overhead • Stack size • Context switching • Synchronisation • GIL? • Not for I/O!
  10. 10. I/O multiplexing • Examine and block for I/O in multiple file descriptors at the same time • Single thread • A file descriptor is ready if the corresponding I/O operation can be performed without blocking • File descriptor has to be set to be non-blocking
  11. 11. I/O is hard • Different paradigms in Unix vs Windows • “are you ready?” vs “call me later” • Frameworks • Abstract platform differences • Provide tools for easier development of applications
  12. 12. The included batteries • select • asyncore / asyncchat • selectors • asyncio
  13. 13. “batteries don’t fit” http://bit.ly/182HcHT
  14. 14. Frameworks • Platform abstraction • Protocol implementations • Integration with other event loops: Qt, GLib, ... • Different API styles • Async file i/o?
  15. 15. import twisted • Uses select, poll, kqueue, epoll from the select module • IOCP on Windows • Integration with other event loops • Factory / Protocol / Transport abstractions • Deferreds
  16. 16. import tornado • Uses select, poll, kqueue, epoll from the select module • select() on Windows :-( • Mainly oriented to web development • Synchronous looking API with coroutines / Futures
  17. 17. import gevent • Uses libev for platform abstraction • select() on Windows :-( • Syncronous API using greenlet • ME LIKEY! :-)
  18. 18. Solution!
  19. 19. I’m not trying to reinvent the wheel. I’m trying to build a good one. ! Guido van Rossum
  20. 20. asyncio import tulip
  21. 21. import asyncio • Reference implementation for PEP-3156 • Basic components for doing async i/o • Works on Python >= 3.3 • Trollius: backport for Python >= 2.6
  22. 22. Goals • Modern implementation of asynchronous i/o for Python • Use yield from (PEP-380) • But don’t force it • Don’t use anything that requires Python > 3.3 • Interoperability with other frameworks
  23. 23. Goals • Unix and Windows support • IPv4 and IPv6 • TCP, UDP and pipes • Basic SSL (secure by default) • Subprocesses
  24. 24. Non goals • Perfection • Replacing current frameworks • Protocol implementations • Replace httplib, smtplib, ... • Make it work on Python < 3.3
  25. 25. Interoperability twisted tornado gevent ... asyncio selectors iocp
  26. 26. Example: Tornado tornado asyncio / twisted tornado epoll/kqueue
  27. 27. Architecture
  28. 28. Components Event loop, policy Coroutines, Futures, Tasks Transports, Protocols, Streams
  29. 29. Calculate poll time Poll Run callbacks Event loop
  30. 30. Event loop & policy • Chooses the best i/o mechanism for a given platform • APIs for creating server and client connections (TCP, UDP, …) • Establish the context for a loop
  31. 31. Working with threads • loop.call_soon_threadsafe(func, *args) • loop.run_in_executor(exc, func, *args) • loop.set_default_executor(exc) ! • PEP-3148 executor
  32. 32. Coroutines, Futures & Tasks
  33. 33. Coroutines, Futures & Tasks • Coroutines • a generator function, can receive values • decorated with @coroutine • Future • promise of a result or an error • Task • Future which runs a coroutine
  34. 34. Coroutines & yield from import asyncio import socket ! loop = asyncio.get_event_loop() ! @asyncio.coroutine def handle_client(client, addr): print("Client connected: {}".format(addr)) while True: data = yield from loop.sock_recv(client, 4096) if not data: print("Client has disconnected") break client.send(data) ! @asyncio.coroutine def accept_connections(server_socket): while True: client, addr = yield from loop.sock_accept(server_socket) asyncio.async(handle_client(client, addr)) ! server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) server.bind(('127.0.0.1', 1234)) server.listen(511) server.setblocking(False) print("Server listening on: {}".format(server.getsockname())) ! loop.run_until_complete(accept_connections(server))
  35. 35. Coroutines & yield from • Imagine the yield from is not there • Imagine the code is executed sequentially • Not exactly the formal definition of yield from (from PEP-380)
  36. 36. Futures • Similar to Futures from PEP-3148 • API (almost) identical • Adapted for asyncio’s single threaded model
  37. 37. Futures + Coroutines • yield from works with Futures! • f = Future() • Someone will set the result or exception • r = yield from f • Waits until done and returns f.result() • Usually returned by functions
  38. 38. Undoing callbacks (I) @asyncio.coroutine def sync_looking_function(*args): fut = asyncio.Future() def cb(result, error): if error is not None: fut.set_result(result) else: fut.set_exception(Exception(error)) async_function(cb, *args) return (yield from fut)
  39. 39. Undoing callbacks (II) ! def sync_looking_function(*args): fut = asyncio.Future() def cb(result, error): if error is not None: fut.set_result(result) else: fut.set_exception(Exception(error)) async_function(cb, *args) return fut
  40. 40. Tasks • Unicorns covered in fairy dust • It’s a coroutine wrapped in a Future • Inherits from Future • Works with yield from • r = yield from Task(coro(...))
  41. 41. Tasks vs coroutines • A coroutine doesn’t “advance” without a scheduling mechanism • Tasks can advance in their own • The event loop is the scheduler!
  42. 42. Example import asyncio ! loop = asyncio.get_event_loop() clients = {} # task -> (reader, writer) ! def accept_client(client_reader, client_writer): task = asyncio.Task(handle_client(client_reader, client_writer)) clients[task] = (client_reader, client_writer) ! def client_done(task): del clients[task] ! task.add_done_callback(client_done) ! @asyncio.coroutine def handle_client(client_reader, client_writer): while True: data = (yield from client_reader.readline()) client_writer.write(data) !! f = asyncio.start_server(accept_client, '127.0.0.1', 12345) server = loop.run_until_complete(f) loop.run_forever()
  43. 43. Transports & Protocols
  44. 44. Transports & Protocols • Transport: represents a connection (socket, pipe, ...) • Protocol: represents an application (HTTP server, IRC client, ...) • They always go together • API is based on function calls and callbacks
  45. 45. Streams • Abstraction on top of transports / protocols • File-like API using coroutines
  46. 46. Transport -> Protocol • connection_made(transport) • data_received(data) • eof_received() • connection_lost(exc) ! • UDP, pipes and subprocesses are slightly different
  47. 47. Protocol -> Transport • write(data) • writelines(seq) • write_eof() • close()
  48. 48. Enter Trollius! • Backport of asyncio for Python >= 2.6 • By Victor Stinner (@haypo) • Slightly different syntax • yield instead of yield from • raise Return(x) instead of return x on generators • pip install trollius
  49. 49. That was all?! • Go read PEP-3156 • Implement a simple protocol (an IRC client for example) • Checkout the links on the last slide • Use asyncio in your next project
  50. 50. Questions? @saghul bettercallsaghul.com
  51. 51. Links • https://code.google.com/p/tulip/ • http://legacy.python.org/dev/peps/ pep-3156/ • https://code.google.com/p/tulip/wiki/ ThirdParty • http://asyncio.org/ • http://www.slideshare.net/saghul

×