tornado mais do que um framework bonitinho
DESCRIPTION
Palestra feita no Mutirão PyCursos 2013, ela tem como objetivo introduzir os aspectos do non-blocking I/O aplicados ao desenvolvimento web e mostrar como o Tornado pode lhe ajudar a construir soluções mais completas. O Tornado é um framework web non-blocking escrito para lidar com milhares de conexões simultâneas. Iremos conhecer as camadas mais baixas do framework (ioloop, iostream, stack_context, gen, timers, ...) entender como elas funcionam e como podemos utilizá-las.TRANSCRIPT
Mais do que um framework web bonitinho
marcelnicolay.com
Tornado é um framework web nonblocking, escrito para lidar com milhares de conexõessimultâneamente.E que mais?
URL SpecTemplateLocaleRequest HandlerHTTP Server
marcelnicolay.com
import tornado.ioloopimport tornado.web
class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world")
if __name__ == "__main__": application = tornado.web.Application([ (r"/", MainHandler), ]) application.listen(8888) tornado.ioloop.IOLoop.instance().start()
marcelnicolay.com
mas é só isso?não...
marcelnicolay.com
backgroundO que você sabe sobre:
Nonblocking I/O?Callback pass style?
marcelnicolay.com
nonblocking I/OÉ um forma de processamento de entrada/saída (input/output) que não bloqueia oprocesso, permitindo executar outras coisas em paralelo.
Operações de entrada e saída, como leitura e escrita, podem ser extremamente lentas emcomparação com o processamento.
Por exemplo, durante uma operação de disco que leve 10 milisegundos para ser realizada,um processador de 1 gigahertz pode processar até 10milhões de instruções.
marcelnicolay.com
nonblocking I/OTypical flow of the synchronous blocking I/O model
marcelnicolay.com
asynchronous I/OTypical flow of the asynchronous nonblocking I/O model
marcelnicolay.com
nonblocking I/OEverything in unix is a file descriptor
incluindo:
socketsconnections
marcelnicolay.com
client server
sockets operations
marcelnicolay.com
synchronous accept
marcelnicolay.com
asynchronous accept
marcelnicolay.com
callback passing styledef start(foo): # do stuff with foo and when donw call the next function stuff(callback=next_step, data=foo);
def next_step(bar): # call more_stuff to parse bar more_stuff(callback=last_step, data=bar)
def last_step(baz): # senf the response send_response(baz)
#let our handler know we are done finish()
marcelnicolay.com
IOLoopAn I/O event loop for nonblocking sockets.
core da camada de rede do tornadorápido e fácil de usaruma instância por processocrossplataformacódigo abertoclient libraries & server applications
marcelnicolay.com
IOLoopimport errnoimport functoolsimport socketfrom tornado import ioloop
def handle_connection(connection, address): print "new connection..."
def connection_ready(sock, fd, events): while True: try: connection, address = sock.accept() except socket.error, e: if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN): raise return connection.setblocking(0) handle_connection(connection, address)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.setblocking(0)sock.bind(("", 8888))sock.listen(128)
io_loop = ioloop.IOLoop.instance()callback = functools.partial(connection_ready, sock)io_loop.add_handler(sock.fileno(), callback, io_loop.READ)io_loop.start()
marcelnicolay.com
IOStreamUtility classes to write to and read from nonblocking files and sockets.
faz todo o "trabalho sujo" para vocêtambém usado em client & server
marcelnicolay.com
IOStream: a very simple HTTP clientfrom tornado import ioloopfrom tornado import iostreamimport socket
def send_request(): stream.write("GET / HTTP/1.0\r\nHost: friendfeed.com\r\n\r\n") stream.read_until("\r\n\r\n", on_headers)
def on_headers(data): headers = {} for line in data.split("\r\n"): parts = line.split(":") if len(parts) == 2: headers[parts[0].strip()] = parts[1].strip() stream.read_bytes(int(headers["Content-Length"]), on_body)
def on_body(data): print data stream.close() ioloop.IOLoop.instance().stop()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)stream = iostream.IOStream(s)stream.connect(("friendfeed.com", 80), send_request)ioloop.IOLoop.instance().start()
tornado.stack_contextpermite preservar o estado atual para ser usado em um outro contexto de execução
@contextlib.contextmanagerdef die_on_error(): try: yield except Exception: logging.error("exception in asynchronous operation",exc_info=True) sys.exit(1)
with StackContext(die_on_error): # Any exception thrown here *or in callback and its desendents* # will cause the process to exit instead of spinning endlessly # in the ioloop. http_client.fetch(url, callback)ioloop.start()
marcelnicolay.com
tornado.genNão curtiu o 'callback passing style'?
class AsyncHandler(RequestHandler): @asynchronous def get(self): http_client = AsyncHTTPClient() http_client.fetch("http://example.com", callback=self.on_fetch)
def on_fetch(self, response): do_something_with_response(response) self.render("template.html")
marcelnicolay.com
tornado.genExiste uma forma mais 'pythonica' de fazer isso...
class GenAsyncHandler(RequestHandler): @asynchronous @gen.engine def get(self): http_client = AsyncHTTPClient() response = yield gen.Task(http_client.fetch, "http://example.com") do_something_with_response(response) self.render("template.html")
marcelnicolay.com
timersexecutar um callback na próxima iteração, ou após algum intervalo
ioloop.add_timeout(deadline, callback)ioloop.remove_timeout(timeout)ioloop.add_callback(callback)
marcelnicolay.com
tornado.httpclient.AsyncHTTPClientAn nonblocking HTTP client.
from tornado import ioloop, httpclient
def handle_request(response): if response.error: print "Error:", response.error else: print response.body ioloop.IOLoop.instance().stop()
http_client = httpclient.AsyncHTTPClient()http_client.fetch("http://www.google.com/", handle_request)ioloop.IOLoop.instance().start()
tornado.platform.twistedThis module contains a Twisted reactor build on the Tornado IOLoop
import tornado.platform.twistedtornado.platform.twisted.install()from twisted.internet import reactor
marcelnicolay.com
tornado.websocketWebSockets allow for bidirectional communication between the browser and server.
class EchoWebSocket(websocket.WebSocketHandler): def open(self): print "WebSocket opened"
def on_message(self, message): self.write_message(u"You said: " + message)
def on_close(self): print "WebSocket closed"
marcelnicolay.com
tornado.netutil.TCPServerA nonblocking, singlethreaded TCP server.
from tornado import ioloopfrom tornado import netutil
class EchoServer(netutil.TCPServer):
def handle_stream(self, stream, address): self._stream = stream self._read_line()
def _read_line(self): self._stream.read_until('\n', self._handle_read)
def _handle_read(self, data_in): self._stream.write('You sent: %s' % data_in) self._read_line()
if __name__ == '__main__': server = EchoServer() server.listen(2007) ioloop.IOLoop.instance().start())
marcelnicolay.com
Mais do que um framework bonitinho
marcelnicolay.com
referênciashttp://en.wikipedia.org/wiki/Asynchronous_I/Ohttp://www.kegel.com/dkftpbench/nonblocking.htmlhttp://www.kernel.org/doc/manpages/online/pages/man4/epoll.4.htmlhttp://scotdoyle.com/pythonepollhowto.htmlhttp://docs.python.org/2/library/select.htmlhttp://www.tornadoweb.org/documentation/index.htmlhttp://dabeaz.com/coroutines/Coroutines.pdf
marcelnicolay.com