Top Banner
@saghul Introduction to asyncio Saúl Ibarra Corretgé PyLadies Amsterdam - 20th March 2014
23

Introduction to asyncio

May 06, 2015

Download

Technology

Slides from my "Introduction to asyncio" talk given at PyLadies Amsterdam, the 20th of March of 2014.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Introduction to asyncio

@saghul

Introduction to asyncioSaúl Ibarra Corretgé

PyLadies Amsterdam - 20th March 2014

Page 2: Introduction to asyncio

github.com/saghul

Page 3: Introduction to asyncio
Page 4: Introduction to asyncio

Sockets 101

import socket

server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)server.bind(('127.0.0.1', 1234))server.listen(128)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()

Page 5: Introduction to asyncio

Scaling Up!

import socketimport _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(128)print("Server listening on: {}".format(server.getsockname()))

while True: client, addr = server.accept() _thread.start_new_thread(handle_client, (client, addr))

Page 6: Introduction to asyncio

Scaling Up! (really?)

Threads are too expensive

Also, context switching

Use an event loop instead!

Page 7: Introduction to asyncio

The Async Way (TM)

Single thread

Block waiting for sockets to be ready to read or write

Perform i/o operations and call the callbacks!

Repeat

(Windows is not like this)

Page 8: Introduction to asyncio

Why asyncio?

asyncore and asynchat are not enough

Fresh new implementation of Asynchronous I/O

Python >= 3.3

Trollius: backport for Python >= 2.6

Use new language features: yield from

Designed to interoperate with other frameworks

Page 9: Introduction to asyncio

Components

Event loop, policy

Coroutines, Futures, Tasks

Transports, Protocols

Page 10: Introduction to asyncio

asyncio 101

import asyncio

loop = asyncio.get_event_loop()

@asyncio.coroutinedef infinity(): while True: print("hello!") yield from asyncio.sleep(1)

loop.run_until_complete(infinity())

Page 11: Introduction to asyncio

Coroutines, futures & tasks

Page 12: Introduction to asyncio

Coroutines, futures & tasksCoroutine

generator function, can receive values

decorated with @coroutine

Future

promise of a result or an error

Task

Future which runs a coroutine

Page 13: Introduction to asyncio

FuturesSimilar to Futures from PEP-3148

concurrent.futures.Future

API (almost) identical:

f.set_result(); r = f.result()

f.set_exception(e); e = f.exception()

f.done(); f.cancel(); f.cancelled()

f.add_done_callback(x); f.remove_done_callback(x)

Page 14: Introduction to asyncio

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

Page 15: Introduction to asyncio

Undoing callbacks

@asyncio.coroutinedef 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)

Page 16: Introduction to asyncio

Tasks

Unicorns covered in fairy dust

It’s a coroutine wrapped in a Future

WAT

Inherits from Future

Works with yield from!

r = yield from Task(coro(...))

Page 17: Introduction to asyncio

Tasks vs coroutines

A coroutine doesn’t “advance” without a scheduling mechanism

Tasks can advance on their own

The event loop is the scheduler!

Magic!

Page 18: Introduction to asyncio

Echo Serverimport asyncio

loop = asyncio.get_event_loop()

class EchoProtocol(asyncio.Protocol): def connection_made(self, transport): print('Client connected') self.transport = transport def data_received(self, data): print('Received data:',data) self.transport.write(data) def connection_lost(self, exc): print('Connection closed', exc)

f = loop.create_server(lambda: EchoProtocol(), 'localhost', 1234)server = loop.run_until_complete(f)print('Server started')

loop.run_forever()

Page 19: Introduction to asyncio

Echo Server Reloadedimport 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.coroutinedef 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', 1234)server = loop.run_until_complete(f)loop.run_forever()

Page 20: Introduction to asyncio

HTTP Serverimport asyncioimport aiohttpimport aiohttp.server

class HttpServer(aiohttp.server.ServerHttpProtocol):

@asyncio.coroutine def handle_request(self, message, payload): print('method = {!r}; path = {!r}; version = {!r}'.format( message.method, message.path, message.version)) response = aiohttp.Response(self.transport, 200, close=True) response.add_header('Content-type', 'text/plain') response.send_headers() response.write(b'Hello world!\r\n') response.write_eof()

loop = asyncio.get_event_loop()f = loop.create_server(lambda: HttpServer(), 'localhost', 1234)server = loop.run_until_complete(f)loop.run_forever()

Page 21: Introduction to asyncio

Redis

import asyncioimport asyncio_redis

@asyncio.coroutinedef subscriber(channels): # Create connection connection = yield from asyncio_redis.Connection.create(host='localhost', port=6379) # Create subscriber. subscriber = yield from connection.start_subscribe() # Subscribe to channel. yield from subscriber.subscribe(channels) # Wait for incoming events. while True: reply = yield from subscriber.next_published() print('Received: ', repr(reply.value), 'on channel', reply.channel)

loop = asyncio.get_event_loop()loop.run_until_complete(subscriber(['my-channel']))

Page 22: Introduction to asyncio

More?

We just scratched the surface!

Read PEP-3156 (it’s an easy read, I promise!)

Checkout the documentation

Checkout the third-party libraries

Go implement something cool!

“I hear and I forget. I see and I remember.

I do and I understand.” - Confucius

Page 23: Introduction to asyncio

Questions?

@saghulbettercallsaghul.com